Button function: click, double click, more...

I nailed down code to process a button in different ways and thought it might be handy for other folks too.

This "4-way button" of "multi click" example shows four different ways to use one button-- click, double click, hold, and "long hold." LEDs light up according to the event triggered.

The motivation is getting the most input from the least physical input devices-- one button and a rotary up/down controller. Using the button for multiple functions, branching menu and value setting UIs can be built in software.

Video demo showing four events: 4-Way Button | Using a single button's timing for four funct… | Flickr

The "long hold" was an afterthought-- keep holding the button down so it does something else after a while-- and limited since it has to trigger the "normal" hold event first. It would be easy to have longer holds trigger more events, but they would all include the shorter hold events, since holds trigger when the button is held down. Wouldn't be tough to add a triple-click either, by extending the double-click logic and timekeeping, but that's not a common way to interact with a button.

The bulk of the code just sets up the button and test LEDs and events, the real content being the checkButton() function and its variables, after "MULTI-CLICK." I tried to make the sketch easy to read and the function as general as possible by only returning an event code, but welcome any suggestions for improvement.

Here's the first tab:

/* 4-Way Button: Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch

By Jeff Saltzman
Oct. 13, 2009

To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.

  1. Click: rapid press and release
  2. Double-Click: two clicks in quick succession
  3. Press and Hold: holding the button down
  4. Long Press and Hold: holding the button for a long time
    */

#define buttonPin 19 // analog input pin to use as a digital input
#define ledPin1 17 // digital output pin for LED 1
#define ledPin2 16 // digital output pin for LED 2
#define ledPin3 15 // digital output pin for LED 3
#define ledPin4 14 // digital output pin for LED 4

// LED variables
boolean ledVal1 = false; // state of LED 1
boolean ledVal2 = false; // state of LED 2
boolean ledVal3 = false; // state of LED 3
boolean ledVal4 = false; // state of LED 4

//=================================================

void setup() {
// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );
// Set LED output pins
pinMode(ledPin1, OUTPUT);
digitalWrite(ledPin1, ledVal1);
pinMode(ledPin2, OUTPUT);
digitalWrite(ledPin2, ledVal2);
pinMode(ledPin3, OUTPUT);
digitalWrite(ledPin3, ledVal3);
pinMode(ledPin4, OUTPUT);
digitalWrite(ledPin4, ledVal4);
}

void loop() {
// Get button event and act accordingly
int b = checkButton();
if (b == 1) clickEvent();
if (b == 2) doubleClickEvent();
if (b == 3) holdEvent();
if (b == 4) longHoldEvent();
}

//=================================================
// Events to trigger

void clickEvent() {
ledVal1 = !ledVal1;
digitalWrite(ledPin1, ledVal1);
}
void doubleClickEvent() {
ledVal2 = !ledVal2;
digitalWrite(ledPin2, ledVal2);
}
void holdEvent() {
ledVal3 = !ledVal3;
digitalWrite(ledPin3, ledVal3);
}
void longHoldEvent() {
ledVal4 = !ledVal4;
digitalWrite(ledPin4, ledVal4);
}

1 Like

Here's the second (of two) tabs with the checkButton() function:

//=================================================
// MULTI-CLICK: One Button, Multiple Events

// Button timing variables
int debounce = 20; // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 250; // max ms between clicks for a double click event
int holdTime = 1000; // ms hold period: how long to wait for press+hold event
int longHoldTime = 3000; // ms long hold period: how long to wait for press+hold event

// Button variables
boolean buttonVal = HIGH; // value read from button
boolean buttonLast = HIGH; // buffered value of the button's previous state
boolean DCwaiting = false; // whether we're waiting for a double click (down)
boolean DConUp = false; // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true; // whether it's OK to do a single click
long downTime = -1; // time the button was pressed down
long upTime = -1; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false; // when held, whether to wait for the up event
boolean holdEventPast = false; // whether or not the hold event happened already
boolean longHoldEventPast = false;// whether or not the long hold event happened already

int checkButton() {
int event = 0;
buttonVal = digitalRead(buttonPin);
// Button pressed down
if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
{
downTime = millis();
ignoreUp = false;
waitForUp = false;
singleOK = true;
holdEventPast = false;
longHoldEventPast = false;
if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true) DConUp = true;
else DConUp = false;
DCwaiting = false;
}
// Button released
else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
{
if (not ignoreUp)
{
upTime = millis();
if (DConUp == false) DCwaiting = true;
else
{
event = 2;
DConUp = false;
DCwaiting = false;
singleOK = false;
}
}
}
// Test for normal click event: DCgap expired
if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
{
event = 1;
DCwaiting = false;
}
// Test for hold
if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
// Trigger "normal" hold
if (not holdEventPast)
{
event = 3;
waitForUp = true;
ignoreUp = true;
DConUp = false;
DCwaiting = false;
//downTime = millis();
holdEventPast = true;
}
// Trigger "long" hold
if ((millis() - downTime) >= longHoldTime)
{
if (not longHoldEventPast)
{
event = 4;
longHoldEventPast = true;
}
}
}
buttonLast = buttonVal;
return event;
}

I have to admit that it is good idea but it looks like the code is rather too long for me to use it... If I get some time then I will have a more detailed look at it...

On my project I just did

while(digitalRead(button) == LOW){}
delay(1000)
if(digitalRead(button) == HIGH){
"Do something"
}
else{
"do something else"
}

Then if it pressed for less than one second it does one thing, if it is held then it does another... It is used on my projects with menus... I focused on the idea of like a bike computer with single button...

Mowcius

double click and other functions (and not using delay!) makes things a lot more complex :slight_smile:

nice work salsaman - I wrote some code last weekend to do a similar thing (single, double and hold) - I figured those three events were enough for me.