Search code examples
carduinoatmega

How to detect how long a button was pressed in Arduino?


How can I detect how long a button was pressed / released in Arduino and then print some custom output after that?


Solution

  • Arduino can only detect the state of your button (pressed OR unpressed).

    You could use a timer variable (based on this example from their docs) to save the exact time when you pressed or released the button, so you can check the difference between both variables to calculate how long it is on hold or idle.

    The code could look like this:

    const int buttonPin = 2;  
    
    int buttonState = 0;     // current state of the button
    int lastButtonState = 0; // previous state of the button
    int startPressed = 0;    // the moment the button was pressed
    int endPressed = 0;      // the moment the button was released
    int holdTime = 0;        // how long the button was hold
    int idleTime = 0;        // how long the button was idle
    
    void setup() {
      pinMode(buttonPin, INPUT); // initialize the button pin as a input
      Serial.begin(9600);        // initialize serial communication
    }
    
    void loop() {
      buttonState = digitalRead(buttonPin); // read the button input
    
      if (buttonState != lastButtonState) { // button state changed
         updateState();
      }
    
      lastButtonState = buttonState;        // save state for next loop
    }
    
    void updateState() {
      // the button has been just pressed
      if (buttonState == HIGH) {
          startPressed = millis();
          idleTime = startPressed - endPressed;
    
          if (idleTime >= 500 && idleTime < 1000) {
              Serial.println("Button was idle for half a second");
          }
    
          if (idleTime >= 1000) {
              Serial.println("Button was idle for one second or more"); 
          }
    
      // the button has been just released
      } else {
          endPressed = millis();
          holdTime = endPressed - startPressed;
    
          if (holdTime >= 500 && holdTime < 1000) {
              Serial.println("Button was held for half a second"); 
          }
    
          if (holdTime >= 1000) {
              Serial.println("Button was held for one second or more"); 
          }
    
      }
    }
    

    However, if you want to trigger an event while the button is still pressed (or maybe you want to increment a counter in some display), you can still do the same math.

    Change your condition in the loop function to be like this:

      if (buttonState != lastButtonState) { 
         updateState(); // button state changed. It runs only once.
      } else {
         updateCounter(); // button state not changed. It runs in a loop.
      }
    

    And then implement your new function like this:

    void updateCounter() {
      // the button is still pressed
      if (buttonState == HIGH) {
          holdTime = millis() - startPressed;
    
          if (holdTime >= 1000) {
              Serial.println("Button is held for more than a second"); 
          }
    
      // the button is still released
      } else {
          idleTime = millis() - endPressed;
    
          if (idleTime >= 1000) {
              Serial.println("Button is released for more than a second");  
          }
      }
    }