Introduction: Arduino - Dual Function Button - Long Press/Short Press (Without Delay) + Bonus
Using software we can take a single button and have it toggle a light on or off, or complete a more complicated function if we so desire. But what if we have two functions but still only one button? Many of us use a button like this, on smartphones for example, everyday; this is called a short press and a long press (press and hold).
The following guide will take you through all the steps needed to create a simple push button that can control the state of two separate LED's.
Step 1: The Theory
First some theory. Using a button to turn on a light is simple, if the button is reading HIGH (when pressed), then we can turn the LED on by writing the output pin it's attached to HIGH as well. Once the button is reading LOW, we can then set the output pin LOW again to turn off the LED. This is what we call momentary.
For this set-up though, we are looking to toggle the LED on or off. This in it'self is a problem for the simple example above, as the loop function in Arduino repeats hundreds of times per second. Even the quickest press could toggle on and off many many times. This first problem we will overcome using a simple boolean or two, explained on the next few pages.
The second problem is the long press function. How do we trigger this function without triggering the short press function first? The answer is simple. The long press function is triggered whilst the button is being pressed, the short press function is triggered once the button is released. This can again be observed on a smart phone by releasing an object on screen just before the long press function activates.
In the next step we will be creating our hardware set-up, feel free to skip this if you are already at this stage...
Step 2: The Hardware
The hardware is very straight forward, so I won't bore you too much.
First are the two LED's. We wire the anodes of each to pins 12 and 13 on the Arduino, the cathodes then go to ground.
The button is connected to 5V output and pin 3 on the Arduino, then grounded with a 10kOhm resistor.
Step 3: The Software in Detail Part 1
Now to detail each step of the code. If you want to go straight to the full code, you'll find that in the step 5
Let's first look at the variables needed for this set-up, starting with the straight forward pin references.
int LED1 = 12; int LED2 = 13; int button = 3;
Next we need a few booleans. These will be used to flip the state of the LEDs on or off
boolean LED1State = false; boolean LED2State = false;
Another two booleans now. The first will be changed dependant on the state of the button; this will allow the code to detect the first loop after the button has been pressed or released. The second will serve two functions, it will allow the code to stop the long press from activating more than once, and will stop the short press function being activated when we release the button after a long press.
boolean buttonActive = false; boolean longPressActive = false;
Finally the two last variables. The first variable is used to record the time the button is first pressed. The second variable, is the length of time (in milliseconds) you wish to have the button held down for, to activate the long press function. This can be changed to a figure of your choosing.
long buttonTimer = 0; long longPressTime = 250;
Now for the set-up, this is again very straight forward, we are simply setting the pin modes.
void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(button, INPUT); }
Step 4: The Software in Detail Part 2
Now for the loop. I'm going to break this down bit by bit, to explain what's going on. The first thing the program will do is read the state of the button. This will give us the code below and and allow us to create functions for both un-pressed and pressed states, remember that the short press function happens upon button release.
if (digitalRead(button) == HIGH) { //Button pressed } else { //Button not pressed }
In the button pressed section, the first thing we will do is test the buttonActive variable, upon set-up this is false. If this is currently false, this means the program is detecting the button being pressed for the first time. This is because after reading false we immediately set it to true, and only releasing the button can set it back to false. We also record the time the button was first pressed.
if (buttonActive == false) { buttonActive = true; buttonTimer = millis(); }
The next step within the button pressed section, is to test how long we have held the button for. We do this by testing the current time, subtracting the time first pressed in milliseconds and compare that to the length of time we wish until the long press is activated.
We also check that the longPressActive boolean is false, because once we have activated the long press function we will set this boolean to true to stop repeat activation. Once we have held the button for the desired time, we will flip the state of the LED1State boolean which will in turn flip the pin the LED is attached to HIGH or LOW accordingly.
if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) { longPressActive = true; LED1State = !LED1State; digitalWrite(LED1, LED1State); }
Moving on to the button not pressed section of the code. The first thing we will check is if the buttonActive boolean is true. If it is set to true, the code is looping for the first time since the button was released. It will then set the boolean back to false to allow the code above to detect the button being pressed again.
if (buttonActive == true) { buttonActive = false; }
Within the above if statement, as we are detecting the first loop after button release, we are next going to check if long press function was activated, which will give up two possible steps. If long press was activated we will simply set the longPressActive back to false to allow the program to long press once again. If it was not activated, we will flip the state of the second LED like we did with the first.
if (longPressActive == true) { longPressActive = false; } else { LED2State = !LED2State; digitalWrite(LED2, LED2State); }
Step 5: The Full Code
Here is the full code ready for upload to your Arduino.
int LED1 = 12; int LED2 = 13; int button = 3; boolean LED1State = false; boolean LED2State = false; long buttonTimer = 0; long longPressTime = 250; boolean buttonActive = false; boolean longPressActive = false; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(button, INPUT); } void loop() { if (digitalRead(button) == HIGH) { if (buttonActive == false) { buttonActive = true; buttonTimer = millis(); } if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) { longPressActive = true; LED1State = !LED1State; digitalWrite(LED1, LED1State); } } else { if (buttonActive == true) { if (longPressActive == true) { longPressActive = false; } else { LED2State = !LED2State; digitalWrite(LED2, LED2State); } buttonActive = false; } } }
Step 6: Bonus - Two Buttons 6 Functions
What if we have two buttons? Then we have the possibility of 6 functions as follows.
Short Press Button 1
Short Press Button 2
Short Press Button 1 & 2
Long Press Button 1
Long Press Button 2
Long Press Button 1 & 2
The program works in the same way the first code does, with some notable changes.
First there are more LED's, meaning there are more variables, and of course the extra button. But the biggest change comes to the point in which the long press or short press are activated. Once either of these two points are reached, rather than turn an LED on or off, we have three possible actions for each function. Resulting one of six possible LED's turning on or off. To determine which action to take, we check the state of each button and those points, and respond accordingly.
Here is the code
int LED1 = 8; int LED2 = 9; int LED3 = 10; int LED4 = 11; int LED5 = 12; int LED6 = 13; int button1 = 3; int button2 = 4; long buttonTimer = 0; long buttonTime = 250; boolean buttonActive = false; boolean longPressActive = false; boolean button1Active = false; boolean button2Active = false; boolean LED1Active = false; boolean LED2Active = false; boolean LED3Active = false; boolean LED4Active = false; boolean LED5Active = false; boolean LED6Active = false; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); pinMode(button1, INPUT); pinMode(button2, INPUT); } void loop() { if (digitalRead(button1) == HIGH) { if (buttonActive == false) { buttonActive = true; buttonTimer = millis(); } button1Active = true; } if (digitalRead(button2) == HIGH) { if (buttonActive == false) { buttonActive = true; buttonTimer = millis(); } button2Active = true; } if ((buttonActive == true) && (millis() - buttonTimer > buttonTime) && (longPressActive == false)) { longPressActive = true; if ((button1Active == true) && (button2Active == true)) { LED4Active = !LED4Active; digitalWrite(LED4, LED4Active); } else if((button1Active == true) && (button2Active == false)) { LED5Active = !LED5Active; digitalWrite(LED5, LED5Active); } else { LED6Active = !LED6Active; digitalWrite(LED6, LED6Active); } } if ((buttonActive == true) && (digitalRead(button1) == LOW) && (digitalRead(button2) == LOW)) { if (longPressActive == true) { longPressActive = false; } else { if ((button1Active == true) && (button2Active == true)) { LED1Active = !LED1Active; digitalWrite(LED1, LED1Active); } else if ((button1Active == true) && (button2Active == false)) { LED2Active = !LED2Active; digitalWrite(LED2, LED2Active); } else { LED3Active = !LED3Active; digitalWrite(LED3, LED3Active); } } buttonActive = false; button1Active = false; button2Active = false; } }
From here we could even add a third button for a possible 14 LEDs, or four button for 30 LEDs. Maybe another time though :)

Participated in the
Coded Creations
21 Comments
Question 2 years ago on Step 2
The input wires in the schematics are connected to the wrong side of the button. If a button is not pressed, Arduino's input must be pulled down to ground to avoid floating. Check this reference... https://www.arduino.cc/en/tutorial/button
6 years ago
This is good tutorial. I have only one question: what kind of resistor I have to connect with LED? I tried this circle in https://circuits.io but does not work :( I will try live :)
Reply 6 years ago
The resistor you use depends on the voltage drop off, source voltage and led current. For example
(SourceVoltage-LEDVoltage)/Current = Ohm
(5V-3.4V)/0.005A = 320 Ohm resistor
I hope that makes sense
Reply 3 years ago
Thank you, Brian29! It's frustrating when you are trying to learn something and the instructions are not correct.
Reply 6 years ago
his diagrams are wrong. button(s) is/are always stuck on HIGH with this setup.
You need to swap the resistor and the 5v wire on the bottom of the button(s)
Question 3 years ago
Hi, thanks for the tutorial, how can i add a timer to LED2 so after triggering it goes off after X seconds?
Question 4 years ago
hi. all of you.when observe in some controling system controling system gaid as i.e like press * for next and press # to come back or the like so how i can set/write /thus like code on arduino in order to shift lcd text left and wright when i press some key on keypad.
so please help me.
4 years ago on Step 5
thank you for your support
5 years ago
Thank you for this good tutorial.
My class example with short and long press button functions.
https://github.com/bercho/dual-function-button
You can download it and add to Arduino IDE.
On example program is showed "How to use this library"
Reply 5 years ago
Thanks — this was exactly what I was looking for in order to avoid spaghetti code.
5 years ago
Great Work
I need help,
If i push button (short press or long press) no action taken care by program withing 1 minutes after LED on
same case after LED off.
6 years ago
How would dimming be added to this sketch?
Say i wanted to dim what ever LED was high from a different button?
7 years ago
my class (library) based on others') detects click, double-click, long press & release and very long press. see: http://pastebin.com/87cCn6h9
the same code reproduced below for your convenience:
// 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);
}
};
7 years ago
THANKYOU!
Reply 7 years ago
You're welcome. I presume you were able to use this for a project
Reply 7 years ago
Hi, here is the application. one button at front and one at back uses this long / short press concept.
https://www.instructables.com/id/Sparrow-My-Assistant/
Reply 7 years ago
Yes. i was able to grasp half of concept. but was in hurry, saved to understand it properly. :)
Reply 7 years ago
modified it and used it for nested menu
7 years ago
Great, thanks, i will give a try over the weekend.
7 years ago
This is very cool! Thanks for the ible