NOOB - Odd behavior from Banzi book Ex 03C

I have been working my way through tons of examples and when I was working through the Banzi book I had something very odd happen.

I entered the code exactly as in the text (after making that damnable mistake with the = instead of ==, grrr...)

but I kept having problems with the debounce, only about 75% accuracy when using any amount of delay: tried 10, 20, 40, 80, 100, 200 etc; Still I was getting VERY inconsistent results. By accuracy I mean that a button push would flash the LED but not always change the state of the LED.

I put in a serial print to see if I could identify anything in the output that was odd. I placed this after the else statement at the very end of the program.
Serial.println(state);

and suddenly the thing worked beautifully. probably about 99% accuracy.

A little bit more fooling around and I moved the delay statement to the end of the loop instead of where it is in the book example and, without any other alteration, it is now working perfectly.

Can anyone explain why simply moving the delay function would cause such a radical change in behavior? Also, I switched all this code around about 15 times, going back and forth between my fix and the original code: in every case, the best accuracy is achieved by moving the delay function to the end of the loop.

Any advice on understanding this would help me understand coding much better.

I am using an UNO R3, arduino board.

Here is my code

//Example 03B: Turn on the LED when the button is pressed
//and keep it on after release
//with new and improved formula
// and now with fabulous debouncing included!!

const int LED = 13;          //LED pin
const int BUTTON = 7;        //button pin
int val = 0;                 //stores state of pin
int old_val = 0;             //place to hold last state written
int state = 0;               //holds value after a change

void setup(){
  pinMode(LED, OUTPUT);      //sets LED pin as output
  pinMode(BUTTON, INPUT);    //sets button pin as input
}

void loop(){
  val = digitalRead(BUTTON); //Reads state of button
  if (val == HIGH && old_val==LOW){
    state = 1 - state;       //changes state if value is high
  }
    
    
  old_val = val;             //changes old_val to last val
  if (state == 1){
    digitalWrite(LED, HIGH); //writes current state to LED
  }
  else{
      digitalWrite(LED, LOW);
  }  
  delay(10);  //the code only works reliably with the delay function here
}

With the delay where it is you are effectively ignoring the button for 10 milliseconds, then grabbing the state.

I don't know where the delay was originally, but this is a perfectly valid way of debouncing a button.

Again, amazment from a noob on the quick response time.

The delay statement was orginally just after the statement:
state = 1 - state;

It works fine as is, i was more or less curious if there is a reason for the behavior change.

Thanks

Can you paste the exact non-working code? Not just describe it, put the change back, make sure it is flaky when you test it, and then copy and paste what you had?

My code is posted above, here is the code as it appears in the book by Banzi.

Again, the only change is the location of the delay. I have tested the Banzi code many times with different delays and it just doesn't work consitently as consistently as when I place the delay function at the end of the loop as in my code posted earlier.

Just any insights as to why this would be happending would help my coding as I begin to work on some more complex sensor projects with my students.
Thanks in advance Nick,
Steve

//Example 03B: Turn on the LED when the button is pressed
//and keep it on after release
//with new and improved formula
// and now with fabulous debouncing included!!

const int LED = 13;          //LED pin
const int BUTTON = 7;        //button pin
int val = 0;                 //stores state of pin
int old_val = 0;             //place to hold last state written
int state = 0;               //holds value after a change

void setup(){
  pinMode(LED, OUTPUT);      //sets LED pin as output
  pinMode(BUTTON, INPUT);    //sets button pin as input
}

void loop(){
  val = digitalRead(BUTTON); //Reads state of button
  if (val == HIGH && old_val==LOW){
    state = 1 - state;       //changes state if value is high
    delay(10);  //this is where the Banzi book shows the delay function
  }
    
    
  old_val = val;             //changes old_val to last val
  if (state == 1){
    digitalWrite(LED, HIGH); //writes current state to LED
  }
  else{
      digitalWrite(LED, LOW);
  }  

}

The good news is, I can reproduce the problem with your posted code. However looking with the logic analyzer:

We can clearly see there some bounces at the 0 mS mark (ie the first press) but some more at the 65 mS mark. So 10 mS in this case resulted in the LED turning on, and then off again 65 mS later.

I changed the delay to 100 and it worked reliably.

I agree moving the delay (10) to the end of the loop works too, but here is why ...

The delay happens every time, even if the button is not pressed. So the test (for the switch press) is only done every 10 mS. Effectively the test will be done at the arrows (10 mS, 20 mS, 30 mS and so on). So for a long press, like you would do as a human, it will still be down when the arrow "arrives". But the little bounce at the end is missed because it is between the arrows.

BTW I reversed the logic, instead of wiring the switch to +5V I wired it to Gnd instead, and put a pull-up on the pin. ie.

void setup(){
  pinMode(LED, OUTPUT);      //sets LED pin as output
  pinMode(BUTTON, INPUT);    //sets button pin as input
  digitalWrite (BUTTON, HIGH);  // internal pull-up
}

Just to amplify on my previous reply, I measured the width of the second big bounce (when the switch was released) and it was 0.3 mS long. So, since you test every 10 mS, you have a 0.3/10 (3 %) chance of testing during that bounce. Not only that, but some of the time during the second bounce, the switch would have appeared to be off anyway (like, half the time), so the chance of your code detecting it would probably be around 1%.

Still, the change of the debounce to 100 would be more reliable, as your change would sometimes fail (not very often, admittedly). Plus, your switch is probably different to mine.


You know what would be even more reliable? And responsive? Debounce all state changes, not just "off" to "on". Like this:

void loop()
  {
  val = digitalRead(BUTTON); //Reads state of button
  if (val == LOW && old_val==HIGH)
    {
    state = 1 - state;       //changes state if value is high
    }
 
  if (val != old_val)   
    delay(10);  // debounce switch state change
    
  old_val = val;             //changes old_val to last val
  if (state == 1)
    {
    digitalWrite(LED, HIGH); //writes current state to LED
    }
  else
    {
      digitalWrite(LED, LOW);
    }  
}

The code above goes back to a 10 mS bounce on all state changes. That works reliably. So if the switch bounces when released, the delay "absorbs" the bounce.

Thanks for the incredibly thorough reply.

I think I will take an evening and run through the pull up example you provided. I am sure it will come in handy sometime.

Thanks again!