Hi, I am trying to detect short/long press on a single button, the thing is, my code works, for some time, but when I don't touch it for a while, and then press the button for a short time, it detects a long press instead.
Can someone tell me what am I doing wrong? Thanks
int IsSwitchEnabled(int nPin){
if (digitalRead(nPin)==LOW){
delayMicroseconds(50); //debounce
int time = millis(); //start time
while(digitalRead(nPin)==LOW){ //button presses
if((millis() - time) >= 3000){
return 2; //long press
}
delayMicroseconds(50);
}
return 1; //short press
}
return 0;
}
Despite the comments in the code, you are not actually debouncing the button. Think step-by-step through your code again. Keep in mind that button contacts can be a few microseconds, with bounces of milliseconds.
There are a couple of button libraries that distinguish between long and short presses.
Weirdly, this seems to work ...
but I can't increase the time the button is supposed to be pressed
int IsSwitchEnabled(int nPin){
if (digitalRead(nPin) == LOW){
int time = millis();
delay(200); //debounce
// check if the switch is pressed for longer than 1 second.
if(digitalRead(nPin) == LOW && time - millis() >1000)
{
return 2;
} else
return 1;
}
return 0;
}
this handles click, double-click, long press, very long press:
// for types of button presses for the Arduino: click, double-click, long press (and release), very long press
// might work with other controllers. modified from: http://pastebin.com/gQLTrHVF
/*
i had to make a few changes (update it?) to make the code work. main changes:
1. REMOVED: #include "WProgram.h" (no longer required).
2. CHANGED: bpUP TO bp.
3. ADDED: event listeners for ALL four types of presses in the sample code.
4. CHANGED: time intervals for single, double, long clicks and for long press. these feel more intuitive to me.
5. CHANGED: event OnLongPress is raised ONLY after the button is released. this, again, feels more intuitive. code is tested and at http://pastebin.com/87cCn6h9
*/
//
// jb_exe
// Class from the code of JEFF'S ARDUINO BLOG
// http://jmsarduino.blogspot.com/2009/10/4-way-button-click-double-click-hold.html
//
// the modified version at http://pastebin.com/gQLTrHVF
// further modified by mahesh [at] tinymogul.com
// How to use me :
// "FWB_Project.pde"
// #include "FWB.h"
// #define BP 0 //the pin where your button is connected
//
// FWB bp;
//
// void OnClick(int pin) {
// //Your code here
// }
//
// void OnDblClick(int pin) {
// //Your code here
// }
//
// void OnLongPress(int pin) {
// //Your code here
// }
//
// void OnVLongPress(int pin) {
// //Your code here
// }
//
// void setup()
// {
// // errors in code fixed here. empty event handlers added.
// bp.Configure(BP);
// bp.OnClick = OnClick;
// bp.OnDblClick = OnDblClick;
// bp.OnLongPress = OnLongPress;
// bp.OnVLongPress = OnVLongPress;
// }
//
// void loop()
// {
// // Test button state
// bp.CheckBP();
// }
#define PULL_UP 1
#define PULL_DOWN 0
class FWB
{
private:
int _pin;
boolean _pullMode;
// Properties //
////////////////
// Debounce period to prevent flickering when pressing or releasing the button (in ms)
int Debounce;
// Max period between clicks for a double click event (in ms)
int DblClickDelay;
// Hold period for a long press event (in ms)
int LongPressDelay;
// Hold period for a very long press event (in ms)
int VLongPressDelay;
// Variables //
///////////////
// Value read from button
boolean _state;
// Last value of button state
boolean _lastState;
// whether we're waiting for a double click (down)
boolean _dblClickWaiting;
// whether to register a double click on next release, or whether to wait and click
boolean _dblClickOnNextUp;
// whether it's OK to do a single click
boolean _singleClickOK;
// time the button was pressed down
long _downTime;
// time the button was released
long _upTime;
// whether to ignore the button release because the click+hold was triggered
boolean _ignoreUP;
// when held, whether to wait for the up event
boolean _waitForUP;
// whether or not the hold event happened already
boolean _longPressHappened;
// whether or not the long hold event happened already
boolean _vLongPressHappened;
public:
void (*OnClick)(int pin);
void (*OnDblClick)(int pin);
void (*OnLongPress)(int pin);
void (*OnVLongPress)(int pin);
FWB()
{
// Initialization of properties
Debounce = 20;
DblClickDelay = 250;
LongPressDelay = 750;
// LongPressDelay = 1000;
VLongPressDelay = 3500;
// VLongPressDelay = 3000;
// Initialization of variables
_state = true;
_lastState = true;
_dblClickWaiting = false;
_dblClickOnNextUp = false;
_singleClickOK = false; //Default = true
_downTime = -1;
_upTime = -1;
_ignoreUP = false;
_waitForUP = false;
_longPressHappened = false;
_vLongPressHappened = false;
}
void Configure(int pin, int pullMode = PULL_DOWN)
{
_pin = pin;
_pullMode = pullMode;
pinMode(_pin, INPUT);
}
void CheckBP(void)
{
int resultEvent = 0;
long millisRes = millis();
_state = digitalRead(_pin) == HIGH;
// Button pressed down
if (_state != _pullMode && _lastState == _pullMode && (millisRes - _upTime) > Debounce)
{
//Serial.println("button down");
_downTime = millisRes;
_ignoreUP = false;
_waitForUP = false;
_singleClickOK = true;
_longPressHappened = false;
_vLongPressHappened = false;
if ((millisRes - _upTime) < DblClickDelay && _dblClickOnNextUp == false && _dblClickWaiting == true)
_dblClickOnNextUp = true;
else
_dblClickOnNextUp = false;
_dblClickWaiting = false;
}
// Button released
else if (_state == _pullMode && _lastState != _pullMode && (millisRes - _downTime) > Debounce)
{
//Serial.println("button up");
if (_ignoreUP == false) //Replace "(!_ignoreUP)" by "(not _ignoreUP)"
{
_upTime = millisRes;
if (_dblClickOnNextUp == false) _dblClickWaiting = true;
else
{
resultEvent = 2;
_dblClickOnNextUp = false;
_dblClickWaiting = false;
_singleClickOK = false;
}
}
}
// Test for normal click event: DblClickDelay expired
if (_state == _pullMode && (millisRes - _upTime) >= DblClickDelay && _dblClickWaiting == true && _dblClickOnNextUp == false && _singleClickOK == true && resultEvent != 2)
{
resultEvent = 1;
_dblClickWaiting = false;
}
// added code: raise OnLongPress event when only when the button is released
if (_state == _pullMode && _longPressHappened && !_vLongPressHappened) {
resultEvent = 3;
_longPressHappened = false;
}
// Test for hold
if (_state != _pullMode && (millisRes - _downTime) >= LongPressDelay)
{
// Trigger "normal" hold
if (_longPressHappened == false)
{
// resultEvent = 3;
_waitForUP = true;
_ignoreUP = true;
_dblClickOnNextUp = false;
_dblClickWaiting = false;
//_downTime = millis();
_longPressHappened = true;
}
// Trigger "long" hold
if ((millisRes - _downTime) >= VLongPressDelay)
{
if (_vLongPressHappened == false)
{
resultEvent = 4;
_vLongPressHappened = true;
//_longPressHappened = false;
}
}
}
_lastState = _state;
//if (resultEvent!=0)
// Serial.println((String)"resultEvent: " + (String) resultEvent);
if (resultEvent == 1 && OnClick) OnClick(_pin);
if (resultEvent == 2 && OnDblClick) OnDblClick(_pin);
if (resultEvent == 3 && OnLongPress) OnLongPress(_pin);
if (resultEvent == 4 && OnVLongPress) OnVLongPress(_pin);
// if (resultEvent != 0)
// Usb.println(resultEvent);
}
};
Have you considered using Switch Manager by Nick Gammon.
/*SwitchManager skeleton
This sketch is to introduce new people to the SwitchManager library written by Nick Gammon
The library handles switch de-bouncing and provides timing and state change information in your sketch.
The SwitchManager.h file should be placed in your libraries folder, i.e.
C:\Users\YourName\Documents\Arduino\libraries\SwitchManager\SwitchManager.h
You can download the library at:
http://gammon.com.au/Arduino/SwitchManager.zip Thank you Nick!
In this example we have 2 normally open (N.O.) switches connected to the Arduino - increment and decrement.
The increment switch will also be used as a "Reset" switch if pressed for more than two seconds.
The two switches are connected between GND (0 volts) and an Arduino input pin.
The library enables pull-up resistors for your switch inputs.
Pushing a switch makes its pin LOW. Releasing a switch makes its pin HIGH.
The SwitchManager library provides 10ms de-bounce for switches.
i.e. enum { debounceTime = 10, noSwitch = -1 };
If you need more time, edit the SwitchManager.h file
i.e. enum { debounceTime = 50, noSwitch = -1 }; //here it is changed to 50ms
*/
#include <SwitchManager.h>
//object instantiations
SwitchManager myIncSwitch;
SwitchManager myDecSwitch;
unsigned long currentMillis;
unsigned long heartBeatMillis;
unsigned long heartFlashRate = 500UL; // time the led will change state
unsigned long incShortPress = 500UL; // 1/2 second
unsigned long incLongPress = 2000UL;// 2 seconds
unsigned long decShortPress = 500UL; // 1/2 second
const byte heartBeatLED = 13;
const byte incSwitch = 4; //increment switch is on Arduino pin 4
const byte decSwitch = 5; //decrement switch is on Arduino pin 5
int myCounter;
//======================================================================
void setup()
{
Serial.begin(9600);
//gives a visual indication if the sketch is blocking
pinMode(heartBeatLED, OUTPUT);
myIncSwitch.begin (incSwitch, handleSwitchPresses);
myDecSwitch.begin (decSwitch, handleSwitchPresses);
//the handleSwitchPresses() function is called when a switch changes state
} // E N D O F s e t u p ( )
//======================================================================
void loop()
{
//leave this line of code at the top of loop()
currentMillis = millis();
//***************************
//some code to see if the sketch is blocking
if (CheckTime(heartBeatMillis, heartFlashRate, true))
{
//toggle the heartBeatLED
digitalWrite(heartBeatLED,!digitalRead(heartBeatLED));
}
//***************************
//check to see what's happening with the switches
//"Do not use delay()s" in your sketch as it will make switch changes unresponsive
//Use BlinkWithoutDelay (BWD) techniques instead.
myIncSwitch.check ();
myDecSwitch.check ();
//***************************
//put other non-blocking stuff here
} // E N D O F l o o p ( )
//======================================================================
// F U N C T I O N S
//======================================================================
// C h e c k T i m e ( )
//**********************************************************************
//Delay time expired function
//parameters:
//lastMillis = time we started
//wait = delay in ms
//restart = do we start again
boolean CheckTime(unsigned long & lastMillis, unsigned long wait, boolean restart)
{
//has time expired for this task?
if (currentMillis - lastMillis >= wait)
{
//should this start again?
if(restart)
{
//yes, get ready for the next iteration
lastMillis += wait;
}
return true;
}
return false;
} // E N D o f C h e c k T i m e ( )
// h a n d l e S w i t c h P r e s s e s( )
//**********************************************************************
void handleSwitchPresses(const byte newState, const unsigned long interval, const byte whichPin)
{
// You get here "ONLY" if there has been a change in a switches state.
//When a switch has changed state, SwitchManager passes this function 3 arguments:
//"newState" this will be HIGH or LOW. This is the state the switch is in now.
//"interval" the number of milliseconds the switch stayed in the previous state
//"whichPin" is the switch pin that we are examining
switch (whichPin)
{
//***************************
//are we dealing with this switch?
case incSwitch:
//has this switch gone from LOW to HIGH (gone from pressed to not pressed)
//this happens with normally open switches wired as mentioned at the top of this sketch
if (newState == HIGH)
{
//The incSwitch was just released
//was this a short press followed by a switch release
if(interval <= incShortPress)
{
Serial.print("My counter value is = ");
myCounter++;
if(myCounter > 1000)
{
//limit the counter to a maximum of 1000
myCounter = 1000;
}
Serial.println(myCounter);
}
//was this a long press followed by a switch release
else if(interval >= incLongPress)
//we could also have an upper limit
//if incLongMillis was 2000UL; we could then have a window between 2-3 seconds
//else if(interval >= incLongMillis && interval <= incLongMillis + 1000UL)
{
//this could be used to change states in a StateMachine
//in this example however, we will just reset myCounter
myCounter = 0;
Serial.print("My counter value is = ");
Serial.println(myCounter);
}
}
//if the switch is a normally closed (N.C.) and opens on a press this section would be used
//the switch must have gone from HIGH to LOW
else
{
Serial.println("The incSwitch was just pushed");
}
break; //End of case incSwitch
//***************************
//are we dealing with this switch?
case decSwitch:
//has this switch gone from LOW to HIGH (gone from pressed to not pressed)
//this happens with normally open switches wired as mentioned at the top of this sketch
if (newState == HIGH)
{
//The decSwitch was just released
//was this a short press followed by a switch release
if(interval <= decShortPress)
{
Serial.print("My counter value is = ");
myCounter--;
if(myCounter < 0)
{
//don't go below zero
myCounter = 0;
}
Serial.println(myCounter);
}
}
//if the switch is a normally closed (N.C.) and opens on a press this section would be used
//the switch must have gone from HIGH to LOW
else
{
Serial.println("The decSwitch switch was just pushed");
}
break; //End of case decSwitch
//***************************
//Put default stuff here
//default:
//break; //END of default
} //End switch (whichPin)
} // E n d o f h a n d l e S w i t c h P r e s s e s ( )
//======================================================================
// E N D O F C O D E
//======================================================================