title: How to recognize and differentiate between single, double, and triple clicks in Arduino?
body: <p>I'm trying to make a sketch for Arduino that differentiates between single, double, triple, maybe quadruple clicks, and hold clicks with a push button.</p>
  <p>I've tried making my own program that uses a variable that increments with each click but haven't had much success. I then found this Arduino sketch that mostly does what I need, but lacks triple and quadruple clicks.</p>
  <p><a href="https://forum.arduino.cc/index.php?topic=14479.0" rel="nofollow noreferrer">https://forum.arduino.cc/index.php?topic=14479.0</a></p>
  <p>I just know I need to copy the same logic the double click uses but haven't figured it out.</p>
  <pre><code>/* 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);
  }
  
  //=================================================
  //  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 &amp;&amp; buttonLast == HIGH &amp;&amp; (millis() - upTime) &gt; debounce)
     {
         downTime = millis();
         ignoreUp = false;
         waitForUp = false;
         singleOK = true;
         holdEventPast = false;
         longHoldEventPast = false;
         if ((millis()-upTime) &lt; DCgap &amp;&amp; DConUp == false &amp;&amp; DCwaiting == true)  DConUp = true;
         else  DConUp = false;
         DCwaiting = false;
     }
     // Button released
     else if (buttonVal == HIGH &amp;&amp; buttonLast == LOW &amp;&amp; (millis() - downTime) &gt; 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 &amp;&amp; (millis()-upTime) &gt;= DCgap &amp;&amp; DCwaiting == true &amp;&amp; DConUp == false &amp;&amp; singleOK == true &amp;&amp; event != 2)
     {
         event = 1;
         DCwaiting = false;
     }
     // Test for hold
     if (buttonVal == LOW &amp;&amp; (millis() - downTime) &gt;= holdTime) {
         // Trigger &quot;normal&quot; hold
         if (not holdEventPast)
         {
             event = 3;
             waitForUp = true;
             ignoreUp = true;
             DConUp = false;
             DCwaiting = false;
             //downTime = millis();
             holdEventPast = true;
         }
         // Trigger &quot;long&quot; hold
         if ((millis() - downTime) &gt;= longHoldTime)
         {
             if (not longHoldEventPast)
             {
                 event = 4;
                 longHoldEventPast = true;
             }
         }
     }
     buttonLast = buttonVal;
     return event;
  }
  </code></pre>
  <p>If you want background, this is for a device for someone to navigate Windows who can only use one button and a joystick, and this is what my current idea requires, but I'm not a programmer so having a bit of a rough time.</p>