Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

arduino

atmega

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

like image 442
Rafael Beckel Avatar asked Dec 01 '22 14:12

Rafael Beckel


2 Answers

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");  
      }
  }
}
like image 95
Rafael Beckel Avatar answered Dec 09 '22 21:12

Rafael Beckel


for your interest here's an example of code using 2 arrays to store button states of arduino pin given to the corresponding input button. During the loop you can do a simple check with the wanted repeat:

  if(button_down(But1_pin, BTN_LOOP_DELAY_MS))
  {
    // code here repeated if the button is either clicked or maintained
  }

button_down() also defers the first repeat by DELAY_WAIT_BEFORE_REPEAT ms.

Here the full tested example:

#define BTN_LOOP_DELAY_MS   100
#define DELAY_WAIT_BEFORE_REPEAT  500
#define NB_MAX_PIN_INPUT    13

#define But1_pin    7
#define But2_pin    6

// array to check status change
bool prev_button[NB_MAX_PIN_INPUT];
unsigned long button_last_down[NB_MAX_PIN_INPUT];

// macro : our read init with prev_button storage
#define READ_INIT_BUTTON(pin)  \
  do{ \
    pinMode(pin, INPUT); \
    prev_button[pin] = digitalRead(pin); \
   } while(false)

// function at the end of the code
bool button_down(byte pin_num, unsigned int delay_repeated);

void setup() {
  READ_INIT_BUTTON(But1_pin);
  READ_INIT_BUTTON(But2_pin);
  Serial.begin(115200);
}

void loop() {
  if(button_down(But1_pin, BTN_LOOP_DELAY_MS))
  {
    Serial.print("new inpulse");
    Serial.print(millis());
    Serial.println();
  }

  if(button_down(But2_pin, BTN_LOOP_DELAY_MS))
  {
    Serial.println("button2");
  }

}

bool button_down(byte pin_num, unsigned int delay_repeated)
{
  bool b = digitalRead(pin_num);
  bool r = false;

  unsigned long currentMillis = millis();

  if(prev_button[pin_num] != HIGH && b == HIGH)
  {
    r = true;
    button_last_down[pin_num] = currentMillis + DELAY_WAIT_BEFORE_REPEAT;
  }
  else if(b == HIGH 
      && prev_button[pin_num] == HIGH
      && currentMillis > button_last_down[pin_num] 
      && currentMillis - button_last_down[pin_num] > delay_repeated
    )
  {
    // save the last time we give a button impusle at true
    button_last_down[pin_num] = currentMillis;
    r = true;
  }

  // store button state, if changed
  if(prev_button[pin_num] != b)
  {
    prev_button[pin_num] = b;
  }
    
  return r;
}
like image 43
Sylvain Avatar answered Dec 09 '22 23:12

Sylvain