How to track debouncing in an interrupt function?


Continuing the discussion from Interrupt not working on calling other function [Solved]:

As suggested by @andyW, I have done debounce tracking inside loop() but how to do it inside the interrupt function. What I’ve done would be something like this:

int state;
int lastState = HIGH;
long lastDebounceTime = 0;
long debounceDelay = 50; // 50 mS
volatile bool light = false;

void setup() {
  pinMode(D0, INPUT_PULLUP);
  attachInterrupt(D3, lightOn, FALLING);
void loop() {
  int reading = digitalRead(D0);

  // if reading isn't LOW
  if (reading != lastState) {
    lastDebounceTime = millis(); // set the clock
  // Check to see if the press is there long enough to exceed delay of 50 mS (not just noise)
  if ( (millis() - lastDebounceTime) > debounceDelay) ) {
    if (reading != state) {
      state = reading;

      if (state == LOW) {
        // here is where I'm supposed to call an interrupt function to change a variable
        // or just go ahead and change the variable 
        light = !light;

void lightOn() {
  light = !light;

Ifttt recipe; each click results in four actions?!
Is there a library for switches?

@metaculus, for non-ISR button sensing you may want to use the clickButton library. For debouncing a button that fires an ISR, you can do something like this:

void btnEvent()   //triggers when the switch is pressed. each time you press the switch,
  if ((millis() - lastDebounceTime) > debounceDelay) //if current time minus the last trigger time is greater than
  {                                                  //the delay (debounce) time, button is completley closed.
    lastDebounceTime = millis();
   //switch was pressed, do whatever you need to here


@peekay123 can this code be inside an interrupt function without blocking too much? I was told that within one it should be very brief code. That’s why I just change a volatile variable and place conditionals outside:

void btnEvent() {
   switchPressed = !switchPressed


It is always worth remembering that debounce may not happen, so the code needs to work if there is only a single transition and no subsequents. One approach is to trigger the action from the first transition, and ignore subsequent transitions for a short period of time, there are a great many other algorithms.

Contact bounce isn’t brain surgery, but it can be surprisingly subtle for something that’s often taken for granted.


@peekay123 as far as I know, I can’t use millis() accurately inside an interrupt event can I?


Just to throw in a somewhat heretic view of this topic :wink:

There might be no call for debouncing when the interrupt is attached to only FALLING or RAISING edge of the interrupt at all.

Sure for the given example

debouncing would be required, since the outcome is unpredictable (odd or even count of bounces) - in this case the ISR should rather be called lightToggle().

But if you do what the function name lightOn()suggests, there would not be any need for debouncing, since you would only switch the light on, which doesn’t matter, if it was on already. But in case this is unwanted you could just do this

void lightOn() {
  if (!light)
    light = true;

void lightOff() {
  if (light)
    light = false;