A new(?) method for switch debounce - comments please

I searched to see if this had been done before but I couldn’t find anything. So I thought I’d post here and ask for feedback.

My context is turning on a relay (controlling a fridge lightbulb) when a bouncy door switch is closed, in case you’re wondering about the naming!


The Old Way

Previously I have always done debouncing like this, but it feels kinda clumsy, and it forces me to break my program structure by putting external hardware logic in the main loop():

volatile bool doorChanged = false;

void doorISR () {
    doorChanged = true;
}

void loop() {
    if (doorChanged) {
         Serial.println("Saw door state change!");
         delay(60);
         doorChanged = false;
         setLight();
    }
}

// Debounce first!
void setLight() {
    digitalWrite(LIGHT_PIN, digitalRead(DOOR_PIN));
}

The New Way

I’ve recently started using Software Timers and been really impressed by their performance. Then I realised I could do this instead for my interrupt handling, which seems a lot neater to me - it trades the ISR ‘flag’ for a timer, and removes all the handling from the main loop and lets me put it in the class related to this hardware interface, where it belongs.

Timer debounceTimer(60, setLight, true);

void doorISR () {
    debounceTimer.resetFromISR();
}

void setLight() {
    digitalWrite(LIGHT_PIN, digitalRead(DOOR_PIN));
}

I’d be interested to hear your feedback - are you already doing this? Is there a better way? What drawbacks might there be to using this method?

:smiley:

While testing this with much longer debounce intervals (200ms!) I did notice one difference in behaviour - the Timer version resets the debounce timeout on every state change, so the overall period can be much longer before an action is taken, if the interrupt is fired continuously. This could be beneficial - it prevents you from getting caught by that one rogue bounce that goes on a little longer than expected.

Unfortunately, there is no difference between reset() and start(), otherwise start() could perhaps have been used to only kick off the timer once without resetting it, and get the ‘original’ behaviour. I suppose you could always use a flag to see whether the timer is running to avoid resetting it I guess, but that feels like a step backwards! :smiley:

I’m not aware of any method to check the state of a Timer.

I had a discussion about this with the ingenious @peekay123, who has never-ending insight on these hidden features, but I can’t find the thread anymore, but maybe he could chime in :wink:
I just remember is some FreeRTOS call.

@pwallington, @ScruffR, the FreeRTOS xTimerIsTimerActive() function is not exposed in the HAL. It is possible to add it via a PR. I’ll raise an issue for now since I think it is important to have. In the meantime, you could use a flag and it is “kludgy” but it will work.

I look at buttons being either event “driving” or polled. In almost all my apps, I poll buttons in loop() using the clickButton library which gives me the most bang for the least button bucks. Only when I absolutely need a “driving” event do I use an interrupt driving button. In one product, I have a button on the WKP pin to wake the product from deep sleep and then switch to using clickButton() to then use the button as part of the user interface.

All this to say that the approach needs to fit the requirement. Using an interrupt to fire a timer to call a function may be more than what is required! But then again, it may be exactly what’s required. I’ll leave that up to you :wink:

2 Likes

Thanks @ScruffR and @peekay123 - my project is pretty small so consuming an interrupt and a timer (both of which I agree are limited resources!) is not a big problem for me but it definitely might not fit with some larger projects!

1 Like