button pressed more than 2 seconds do somethingelse

i would like that if i hold the button more than 2 seconds arduino it does something (i.g. turn on a led for 2 seconds), while when is pressed normal (less than 2 seconds) does something else (i.g. turn on a led for 1 second)

what code should i use? i didn't find anything working since now :frowning:

1 Like

if you have one switch then the best thing you could do is to register how long the button is press.
depending on how you set up your hardware the simplest implementation would be to detect the change/transition of state of the switch. recond the time between press and release then you have something to work with
say maybe something in line with this will help
if PreviousSwitch low and CurrentSwitch high record time means the beginning of press
if previousswitch high and currentSwitch low record time means switch release time
then use if release-press>=2000UL then led blink 2second else led blink 1 second.
HTH...

It is fairly involved, :).

The basic logic is to count the time the key has been pressed. If it exceeds a preset limit, test for long presses.

Here is the basic flow:

#define KEY_PRESSED LOW //state of key being pressed
#define KEY_NO_PRESS 0 //key not pressed
#define KEY_SHORT_PRESS 1 //key pressed short
#define KEY_LONG_PRESS 2 //key pressed long
#define KEY_DURATION 100 //cycle count, minimum threshold to test for long presses

unsigned char key_read(unsigned char pin)  {
  unsigned char count=0;
  if (digitalRead(pin) != KEY_PRESSED) return KEY_NO_PRESS; //key not pressed
  //key is pressed
  while (digitalRead(pin) == KEY_PRESSED) count+=1; //increment count if key is continuously pressed
  if (count > KEY_DURATION) return KEY_LONG_PRESS;
  else return KEY_SHORT_PRESS;
}

The test for long_press / short_press could be further enhanced, based on your design.

1 Like

Hmm... that's blocking code.

Here's an alternative version:

unsigned long keyPrevMillis = 0;
const unsigned long keySampleIntervalMs = 25;
byte longKeyPressCountMax = 80;    // 80 * 25 = 2000 ms
byte longKeyPressCount = 0;

byte prevKeyState = HIGH;         // button is active low
const byte keyPin = 2;            // button is connected to pin 2 and GND


// called when button is kept pressed for less than 2 seconds
void shortKeyPress() {
    Serial.println("short");
}


// called when button is kept pressed for more than 2 seconds
void longKeyPress() {
    Serial.println("long");
}


// called when key goes from not pressed to pressed
void keyPress() {
    Serial.println("key press");
    longKeyPressCount = 0;
}


// called when key goes from pressed to not pressed
void keyRelease() {
    Serial.println("key release");
    
    if (longKeyPressCount >= longKeyPressCountMax) {
        longKeyPress();
    }
    else {
        shortKeyPress();
    }
}


void setup() {
    Serial.begin(115200);
    pinMode(keyPin, INPUT_PULLUP);
}


void loop() {
    // key management section
    if (millis() - keyPrevMillis >= keySampleIntervalMs) {
        keyPrevMillis = millis();
        
        byte currKeyState = digitalRead(keyPin);
        
        if ((prevKeyState == HIGH) && (currKeyState == LOW)) {
            keyPress();
        }
        else if ((prevKeyState == LOW) && (currKeyState == HIGH)) {
            keyRelease();
        }
        else if (currKeyState == LOW) {
            longKeyPressCount++;
        }
        
        prevKeyState = currKeyState;
    }


    // other code goes here
}

When the button is released, if it was held down for less than 2 second the string "short" is printed, if it was held down for more than 2 seconds the string "long" is printed.

(edit: led blink should be added with blink without delay style code, and instead of printing "short" and "long" a "blink duration" variable should be set, as per OP request).

wow thanks i will test that code to my project

Post the code when you have a working test sketch :slight_smile:

ummm i am unable to merge that code with my code

this is one of the action should be performed when the button is pushed less than 2 seconds; i would like to add another action when buttonPin is pressed more than 2 seconds

in the first action it emitts a sound via mp3 board and count the number of pushed times (that is needed for other purpose)
i added a debounce time to avoid false start

                                                                             // SELECTOR BEGIN
// read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  if ((millis() - lastDebounceTime) > debounceDelay){
    
      // control that input is HIGH (button pressed)  
      // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
      // if the state has changed, increment the counter
      if (buttonState == LOW) {
    // play file 004.mp3 in folder advert02 -- switching sound
    Serial.write(0x7E);
    Serial.write(0x07); 
    Serial.write(0xA0);
    Serial.write(0x30);
    Serial.write(0x32);
    Serial.write(0x30);
    Serial.write(0x30);
    Serial.write(0x34);
    Serial.write(0x7E);
            // if the current state is HIGH then the button
      // wend from off to on:
     buttonPushCounter++;
     TrackCounter =  0;
     lastDebounceTime = millis(); //set the current time
   }
    }
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;
                                                                          // SELECTOR END

buttonPin it's already declared as

const int buttonPin = 2;    // WEAPON SELECTOR

pinMode(buttonPin, INPUT);

in your code i should change as "INPUT_PULLUP" and i dont' know if it's ok..

INPUT_PULLUP requires IDE 1.0.3 IIRC.
It can be substituted by
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);

Don't know what this code does

// play file 004.mp3 in folder advert02 -- switching sound
    Serial.write(0x7E);
    Serial.write(0x07); 
    Serial.write(0xA0);
    Serial.write(0x30);
    Serial.write(0x32);
    Serial.write(0x30);
    Serial.write(0x30);
    Serial.write(0x34);
    Serial.write(0x7E);

but I feel it should be put in its own function, lets say playMp3File();

This function should be called from shortKeyPress(). Have you read the comments in my code ?

I have a better solution - the previous solutions don't take into consideration that a "long" button press may need to be the same duration on different processors, or in coordination with different blocking calls that may alter how long it takes to run the loop.

I have a solution that uses a timer to count how long the button press has been in place, and debounces the button of course.

// long button press variables
unsigned long longButtonTimeout_ms = 2000; // two second button timeout
bool previousButtonState;
bool listeningForLongButtonPress;

// debounce variables
int buttonState;
int lastButtonState = LOW;
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

byte buttonPin = 9;

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  currentTime_ms = milils();
  bool buttonPressed = false;

  int reading = digitalRead(buttonPin);
  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        buttonPressed = true;
      } else {
        buttonPressed = false;
    }
  }


  // check for a long button press
  if (buttonPressed) {
    // if we are already within a button press
    if (listeningForLongButtonPress) {
      // and the button has been pressed for a long enough time
      if (currentTime_ms > (lastUpdate_ms + longButtonTimeout_ms)) {
        // we have a long button press
        longButtonPress();
        // stop listening for a button press
        listeningForLongButtonPress = false;
        Serial.println("Button Pressed for a long time!");
      } 
    } else {
      // otherwise, this is a new button press... begin listening for a long buttonpress
      listeningForLongButtonPress = true;
      Serial.println("Button has been pressed");
    }
  } else {
    // button released?  Stop listening for a long button press
    listeningForLongButtonPress = false;
    Serial.println("Button Released");
  }
}

Sorry for resurrecting a dead thread, but I used tuxduino's code for a project I am working on, but I changed a couple of things that might be relevant for someone else:

  • Added a mediumKeyPress (.5sec - 1.99999sec)
  • LongKeyPress is determined while the button is down, and not after release

The second one I find the most useful as usually I find that you want your project to react once the button has been held for a certain time, not leave it up to the user to decide if they have held the button long enough.

// Thanks to tuxduino and Grumpy_Mike on the Arduino.cc boards

unsigned long keyPrevMillis = 0;
const unsigned long keySampleIntervalMs = 25;
byte longKeyPressCountMax = 80;    // 80 * 25 = 2000 ms
byte mediumKeyPressCountMin = 20;    // 20 * 25 = 500 ms
byte KeyPressCount = 0;

byte prevKeyState = HIGH;         // button is active low
const byte keyPin = 2;            // button is connected to pin 2 and GND


// called when button is kept pressed for less than .5 seconds
void shortKeyPress() {
    Serial.println("short");
}


// called when button is kept pressed for more than 2 seconds
void mediumKeyPress() {
    Serial.println("medium");
}


// called when button is kept pressed for 2 seconds or more
void longKeyPress() {
    Serial.println("long");
}


// called when key goes from not pressed to pressed
void keyPress() {
    Serial.println("key press");
    KeyPressCount = 0;
}


// called when key goes from pressed to not pressed
void keyRelease() {
    Serial.println("key release");
    
    if (KeyPressCount < longKeyPressCountMax && KeyPressCount >= mediumKeyPressCountMin) {
        mediumKeyPress();
    }
    else {
      if (KeyPressCount < mediumKeyPressCountMin) {
        shortKeyPress();
      }
    }
}


void setup() {
    Serial.begin(9600);
    pinMode(keyPin, INPUT_PULLUP);
}


void loop() {
    // key management section
    if (millis() - keyPrevMillis >= keySampleIntervalMs) {
        keyPrevMillis = millis();
        
        byte currKeyState = digitalRead(keyPin);
        
        if ((prevKeyState == HIGH) && (currKeyState == LOW)) {
            keyPress();
        }
        else if ((prevKeyState == LOW) && (currKeyState == HIGH)) {
            keyRelease();
        }
        else if (currKeyState == LOW) {
            KeyPressCount++;
    if (KeyPressCount >= longKeyPressCountMax) {
        longKeyPress();
    }
        }
        
        prevKeyState = currKeyState;
    }


    // other code goes here
}

That's good functionality indeed.

In the serial port, one can see that only ever one short or medium key press is triggered, but the long key press fires repeatedly, until the user eventually releases it.

How would one time-bracket the long key press similarly to the short and medium key press?

this code doesn't work properly.
if the button is keep pressed more than 8 seconds, longKeyPressCount reaches 255 and then resets to 0. if you release right after that you will get a "short press" reading

marcello.romani:
Hmm... that's blocking code.

Here's an alternative version:

unsigned long keyPrevMillis = 0;

const unsigned long keySampleIntervalMs = 25;
byte longKeyPressCountMax = 80;    // 80 * 25 = 2000 ms
byte longKeyPressCount = 0;

byte prevKeyState = HIGH;        // button is active low
const byte keyPin = 2;            // button is connected to pin 2 and GND

// called when button is kept pressed for less than 2 seconds
void shortKeyPress() {
    Serial.println("short");
}

// called when button is kept pressed for more than 2 seconds
void longKeyPress() {
    Serial.println("long");
}

// called when key goes from not pressed to pressed
void keyPress() {
    Serial.println("key press");
    longKeyPressCount = 0;
}

// called when key goes from pressed to not pressed
void keyRelease() {
    Serial.println("key release");
   
    if (longKeyPressCount >= longKeyPressCountMax) {
        longKeyPress();
    }
    else {
        shortKeyPress();
    }
}

void setup() {
    Serial.begin(115200);
    pinMode(keyPin, INPUT_PULLUP);
}

void loop() {
    // key management section
    if (millis() - keyPrevMillis >= keySampleIntervalMs) {
        keyPrevMillis = millis();
       
        byte currKeyState = digitalRead(keyPin);
       
        if ((prevKeyState == HIGH) && (currKeyState == LOW)) {
            keyPress();
        }
        else if ((prevKeyState == LOW) && (currKeyState == HIGH)) {
            keyRelease();
        }
        else if (currKeyState == LOW) {
            longKeyPressCount++;
        }
       
        prevKeyState = currKeyState;
    }

// other code goes here
}




When the button is released, if it was held down for less than 2 second the string "short" is printed, if it was held down for more than 2 seconds the string "long" is printed.

(edit: led blink should be added with blink without delay style code, and instead of printing "short" and "long" a "blink duration" variable should be set, as per OP request).

you can use the below code. it works for me. I put two time cycle. press less than 10 second then system will print short press, more than 10 second then system will print Long press and not press that system will print release.
const byte keyPin = 2; // The switch is connected to pin number 2
int pressTime = 0; // the counter that acounnt the pressing time
void setup() {
Serial.begin(115200);
pinMode(keyPin, INPUT);
}
void loop() {
byte key = digitalRead(keyPin);
if (key == HIGH) { // that means the key is pressed
pressTime++;
delay(1000); // works like a timer with one second steps
if (pressTime <= 10){
Serial.println("Short click");
}else {
Serial.println("Long click");
}
}
else if (key == LOW) {// key is not pressed
Serial.println("Released");
pressTime=0;
}
Serial.println(pressTime);
}