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?
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”
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; )
@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 afterTIMEOUT, where my version triggeres immediately and ignores the following bounces, at the cost of a bit more code inside ISR.
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
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 and have already edited my first post
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.
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!");
}
}
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