Debounce Interrupt on Spark Core

Hey All,

So I am developing an application for the Spark Core where I will send an HTTP Request when a button is pressed. Since I want to make sure this button press is never missed in the main loop I want to use an interrupt. However the problem is when the button is pressed the method fires several times(Bouncing). Since delay does not work how do you debounce an interrupt on the spark core?

Thanks

I guess one of the easiest ways would look like this

...
#define TIMEOUT 50 // milliseconds
volatile unsigned long timestamp = 0;
volatile int  triggered = 0;
...
void loop()
{
  ...
  if (triggered)
  {
    // we got a hit
    triggered = 0;  // reset 
  }
  ...
}

void ISR()  // interupt service routine
{
  if ((millis() - timestamp) < TIMEOUT) 
    return;                           

  timestamp = millis();
  triggered = -1;
}

I hope millis() is callable inside the ISR, otherwise some other time source should be used.

Other ways would be masking/unmasking or detaching/re-attaching the interrupt.
Or a load of other more academic procedures :wink:

EDIT: After @harrisonhjones hint I changed if (timestamp > millis() - TIMEOUT) for if ((millis() - timestamp) < TIMEOUT)

2 Likes

I use similar logic, but moved it out of ISR to keep its code as short as possible. Calling millis() in ISR should return value, but the value doesn’t increase during handling interrupt (at least Arduino folks say that).

#define TIMEOUT 50 // milliseconds
unsigned long timestamp = 0;
volatile int  triggered = 0;
...
void loop()
{
  ...
  if (triggered)
  {
    triggered = 0;  // reset in all cases
    if (millis() - timestamp > TIMEOUT) {
      timestamp = millis();
      // we got a hit
      // CODE HERE
    }
  }
  ...
}

void ISR()  // interupt service routine
{
  triggered = -1;
}

Edit: fixed wrong code as @ScruffR pointed out - I had brain blackout probably, never write “just one line of code before going to sleep” :smile:
This is closest reimplementation of code I use to be in line with @ScruffR’s example. It should overflow correctly if using same data types (as 1 - 0xFFFFFFFF = 2). It blocks for first TIMEOUT ms, but I think it doesn’t matter anyway (you can initialize timestamp = 0; timestamp -= TIMEOUT; )

2 Likes

@lami, while it might well be that millis() doesn’t change during the ISR, it wouldn’t matter anyhow, since you only need one reading - even if called twice.
It would matter if you tried to compare two readings in one visit to the ISR, then you would use the same value twice if millis() does not change - although I’m not sure this is actually true for the Core, too.

But I guess there must be something wrong with your code here, because as I see it, the inner if (timestamp > millis() - TIMEOUT) will never be satisfied with a starting timestamp = 0.
And even if you start with a higher timestamp, you will catch the first trigger, but all following bounces too, since millis() - TIMEOUT will normally always be less then millis() of the previous visit to loop().

On the other hand if it should be if (timestamp < millis() - TIMEOUT) this would work only if the bouncing lasts longer than TIMEOUT, otherwise the last bounce before TIMEOUT will reset triggered = 0 and not enter and since there is no bounce following it won’t enter if (triggered) anymore.
But this version would work if triggered = 0 happened inside the inner if. The only drawback of this is, that it ignores all bounces first and does the work after TIMEOUT, where my version triggeres immediately and ignores the following bounces, at the cost of a bit more code inside ISR.

I’ve been told, and agree with, that the following line is best when comparing current time vs last time vs a delay threshold:

if((millis() - _time) > _ledDelay)

I guess you have the docs ( http://docs.spark.io/firmware/#time-millis ) in mind

Note: The parameter for millis is an unsigned long, errors may be generated if a programmer tries to do math with other datatypes such as ints.

And you are right, the risk exists :wink:
But I haven’t experianced them with a simple subtraction ulong - uint - especially since on a 32bit µP ulong and uint are both 32bit numbers.
On other platforms or more complicated expressions may be more susceptible.

But I’ll bear it in mind :thought_balloon: and have already edited my first post :wink:

The only problem with this solution is, if a trigger occures exactly when _time is within 2^32 - _ledDelay and millis() has just overrun 32bit, then any bounces would push through, since the subtraction will produce huge results.

Yes, I meant millis() in ISR should not be a problem [unless wanting it to change, which is bad thing to do in ISR anyway].

Actually I modified your example without too much worrying about ovetflows and comparisons, my mistake - you are absolutely right. My point was to move any logic out of ISR.

Edit: fixed my example, thanks @ScruffR!

1 Like

Whoa guys. I do not want to read the input in the run loop. I plan on doing a lot of functions there. I want to register the input to a interrupt so it catches triggers. Putting this in the main loop is not what I want to do. I think I have figured it out without using delay or the main loop at all. Here is my code so far:

/**
 ******************************************************************************
 * @file    application.cpp
 * @authors  Satish Nair, Zachary Crockett and Mohit Bhoite
 * @version V1.0.0
 * @date    05-November-2013
 * @brief   Tinker application
 ******************************************************************************
  Copyright (c) 2013 Spark Labs, Inc.  All rights reserved.

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation, either
  version 3 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this program; if not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/  
#include "application.h"

int LED = D7;
int button = D0;

void sendNotification(void);

SYSTEM_MODE(SEMI_AUTOMATIC);

/* This function is called once at start up ----------------------------------*/
void setup()
{
	Serial.begin(9600);
	pinMode(LED, OUTPUT);
	digitalWrite(LED, HIGH);
	delay(1000);
	Serial.println("Start");

	WiFi.on();
	WiFi.clearCredentials();
	WiFi.setCredentials("Photo-Video-Station","1234567890");
	WiFi.connect();
	while(!WiFi.ready()){
		Serial.println("WiFi Not Ready");
		delay(100);
	}
	digitalWrite(LED, LOW);
	pinMode(button, INPUT_PULLUP);
	attachInterrupt(button, sendNotification, FALLING);//Trigger function when input is pulled to ground by button
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	//This will run in a loop
	//Much more code to run here in the future

}

void sendNotification(){
	bool success = true;
	//Read input 10 times to make sure it stays tripped(Eliminate Bounce).
	//Times read can be lowered to highten sensitivity or raised to lower sensitivity.
	for(int i = 0; i < 10; i++){
		if(digitalRead(button)==HIGH){
			success = false;
			break;
		}
	}
	if(success == false){
		//Do Nothing
		return;
	}else{
		Serial.println("Sending Notification!");
	}
}

I don't think this is a good solution - sorry.

Don't do multiple digitalRead() 's and Serial.println() in your ISR!
There is no need to digitalRead() at all, since the fact of the triggered ISR already tells you the state of the input.

If you look, our solutions don't do this

If you don't want to clutter the loop() put the logic into a function - this would only add one line to loop(), but this is a lot better than cluttering an ISR.

@IOTrav: In case it’s wasn’t clear, the reason @ScruffR doesn’t want you to put those two functions in your ISR is that your ISR is supposed to be as lightweight as possible. Ideally the ISR would simply inform the core that something has happened and it’s so important (that event) that you are willing to stop whatever you are doing to think about it. However, if you put huge code in your ISR you could, potentially, bring your micro to a halt if it’s spending all its time handling the ISR

1 Like