3

I wrote code that tracks the duration of a button click. It has bounce protection and frequent clicks. (Freeze)

My code:

bool freeze_time, btn_read, debounce;
unsigned int freeze_timer, btn_timer;

#define BTN 6

void setup() {
  Serial.begin(9600);
  pinMode(BTN, INPUT);
}

void loop() {
  /* Freeze Timer Protection */
  if(freeze_time) {
    if((millis() - freeze_timer) >= 2500) {
      freeze_time = false;
      btn_read = false;
    }
  }
  /* Read Button State */
  if(digitalRead(BTN) == HIGH) {
    if(!btn_read) {
      btn_timer = millis();
      debounce = true;
      btn_read = true;
    }
  }
  /* BTN Read Process */  
  if(btn_read) {
    /* Debounce Protection */
    if(debounce) {
      if((millis() - btn_timer) >= 1000) {
        if(digitalRead(BTN) == HIGH) {
          Serial.println("Debounce ok.");
          btn_timer = millis();
          debounce = false;
        } else {
          Serial.println("Debounce fail.");
          freeze_time = true;
          debounce = false;
        }
      }
    }
    /* BTN Read Process */ 
    else if(!freeze_time) {
      if((millis() - btn_timer) >= 1500) {
        if(digitalRead(BTN) == HIGH) {
          Serial.println("Ok. Button Pressed > 1500ms");
          freeze_timer = millis();
          freeze_time = true;
        } else {
          Serial.println("Ok. Button Pressed < 1500ms");
          freeze_timer = millis();
          freeze_time = true;
        }
      }
    }
  }  
}

I checked it on real Arduino UNO and Tinkercad simulator. Initially, everything works well, but if I press the button indefinitely, the system will fail (every time you press the serial port, messages appear as if all flags were lost and the timers did not work. I filmed the behavior:

1) Initially: http://recordit.co/lVlR1z1CUq 2) System is broken: http://recordit.co/lhvzM100DI

What could be the problem and how to make it fault tolerant?

4
  • (millis() - btn_timer) >= 1000 Switch bounce doesn't last for a full second. You could however press the button, and release it within one second. Try changing it to a more sensible value like 10. The rest of the code doesn't really make sense to me. Could you explain what it is you want to achieve, as it looks like the code it way to complicated to just measure if a button was press for more or for less that 1500ms.
    – Gerben
    Mar 30 '19 at 12:28
  • upvote for posting correctly formatted code
    – jsotola
    Mar 30 '19 at 18:55
  • you can reduce the number of lines of code by putting repeated command lines outside of if-else blocks ..... for example, in the last if-else block, keep the serial.println() lines inside the block, but move the freeze_timer = millis(); and freeze_time = true; to outside of the block, either before or after the block (it makes no difference logically in this instance) ........... same with the debounce = false; in the previous if-else block
    – jsotola
    Mar 30 '19 at 19:00
  • Use a more concise and reliable model for debounce (arduino.stackexchange.com/a/33577/42061) Apr 1 '19 at 14:13
4

Why did the timers stop working?

The main issue seems to be that freeze_timer and btn_timer are of type unsigned int, while millis, as stated in its documentation, returns an unsigned long.

As you may know, unsigned int is normally be able to hold a much smaller range of values than unsigned long. In the case of whatever compiler Tinkercad uses for the Arduino Uno, I checked and unsigned int can hold values from 0 to 65535, while unsigned long can hold values from 0 to 4294967295. Whenever you save an unsigned long into an unsigned int, it will be reduced modulo 65536.

This means that once you pass 65.536 seconds checks such as the (millis() - freeze_timer) >= 2500 will always be true (at least until millis wraps back around after 4294967.296 seconds - around 50 days).

If you would like to learn a bit more about integer conversions, I think this Stack Overflow question covers it.

The simplest solution is, of course, to declare freeze_timer and btn_timer to be of the appropriate type, unsigned long.

Use simpler code

All of the above being said, like Gerben said, the code seems overly complex.

There also seem to be other issues. For instance, I would expect the debounce behavior should not ignore presses that last less than 1 second. Like Gerben said, 10 ms should be enough to discard any bounces.

Furthermore, I think it would be more appropriate to not even discard presses that last less than 10 ms. I would say the debouncer should trigger "press" regardless of whether the press lasted less than 10 ms. It's role should just be to ignore multiple presses within that 10 ms window (although to be fair, ignoring short presses is not a terribly big issue).

Although one could make some guesses, to really help you with this, we would need a description of your intended behavior. What is the purpose of freezing? Is it intended to allow multiple presses to be triggered when you never release the button (or maybe you should wait until release before triggering one again)?

1
  • Works! Thank you!!!
    – Delta
    Apr 1 '19 at 14:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.