One button, two functions

I've been messing around with the Uno for quite some time trying get a momentary button to do a latching function when pressed and then do an actual momentary function when held for a certain time and when I let go it should turn off. I've just been testing with an LED for now.

Basically trying to take these two pieces and put them together:

void loop() {

//momentary:
if (buttonState == LOW) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }


//latching:
buttonState = digitalRead(buttonPin);

if (buttonState == LOW){

  digitalWrite(ledPin, !digitalRead(ledPin));

  }
}

Just the standard examples. I'm running the button pin high and the functions should actuate when grounded.

I tried to do it in a while loop like this:

float pressLength_milliSeconds = 0;

int MOMENT = 800;
int LATCH = 300;

void setup() {
  
  //SET PINS:

    pinMode(buttonPin, INPUT);
    pinMode(ledPin, OUTPUT);
    digitalWrite(buttonPin, HIGH);
    
}

void loop() {
  
  while (digitalRead(SW) == LOW){

    delay(100);
    pressLength_milliSeconds = pressLength_milliSeconds + 100;
  }

  if (pressLength_milliSeconds >= MOMENT){

      digitalWrite(LED, HIGH);
 
  }

  pressLength_milliSeconds = 0;

    }

...but when I hold the switch, the LED doesn't turn on until I let go of the switch.
How do I separate these functions with one button? Am I on the right track?

Work through this for the basics:
https://forum.arduino.cc/index.php?topic=719995.0

Then, when you understand that, look at this, which has an example close to what you are looking for:
https://forum.arduino.cc/index.php?topic=720016.0

Store the time when the button is pressed.

When the button is released, check how long it was pressed for and use that to determine if it was a long or short click.

Jiggy-Ninja:
Store the time when the button is pressed.

When the button is released, check how long it was pressed for and use that to determine if it was a long or short click.

Or: If the button is released before the 'long press' interval is reached, do a short press. If the 'long press' interval is reached, do a long press. That way you don't have to wait until the button is released to do the long press. It will happen when the time is reached.

const byte ButtonPin = 2;
const unsigned long DebounceTime = 30;
const unsigned long LongPressInterval = 1000;


boolean ButtonWasPressed = false;
boolean ButtonBeingTimed = false;
unsigned long ButtonStateChangeTime = 0; // Debounce timer


void setup()
{
  pinMode (ButtonPin, INPUT_PULLUP);  // Button between Pin and Ground
}


void loop()
{
  checkButton();
}


void checkButton()
{
  unsigned long currentTime = millis();


  boolean buttonIsPressed = digitalRead(ButtonPin) == LOW;  // Active LOW


  // Check for button state change and do debounce
  if (buttonIsPressed != ButtonWasPressed &&
      currentTime  -  ButtonStateChangeTime > DebounceTime)
  {
    // Button state has changed
    ButtonWasPressed = buttonIsPressed;


    if (ButtonWasPressed)
    {
      ButtonBeingTimed = true;
    }
    else
    {
      // Button was just released
      ButtonBeingTimed = false;
      if (currentTime - ButtonStateChangeTime < LongPressInterval)
      {
        // Short Press
      }
    }


    ButtonStateChangeTime = currentTime;
  }


  if (ButtonBeingTimed && ButtonWasPressed)
  {
    // Button is still pressed
    if (currentTime - ButtonStateChangeTime >= LongPressInterval)
    {
      ButtonBeingTimed = false;
      // Long Press=
    }
  }
}
1 Like

johnwasser:
Or: If the button is released before the 'long press' interval is reached, do a short press. If the 'long press' interval is reached, do a long press. That way you don't have to wait until the button is released to do the long press. It will happen when the time is reached.

const byte ButtonPin = 2;

const unsigned long DebounceTime = 30;
const unsigned long LongPressInterval = 1000;

boolean ButtonWasPressed = false;
boolean ButtonBeingTimed = false;
unsigned long ButtonStateChangeTime = 0; // Debounce timer

void setup()
{
 pinMode (ButtonPin, INPUT_PULLUP);  // Button between Pin and Ground
}

void loop()
{
 checkButton();
}

void checkButton()
{
 unsigned long currentTime = millis();

boolean buttonIsPressed = digitalRead(ButtonPin) == LOW;  // Active LOW

// Check for button state change and do debounce
 if (buttonIsPressed != ButtonWasPressed &&
     currentTime  -  ButtonStateChangeTime > DebounceTime)
 {
   // Button state has changed
   ButtonWasPressed = buttonIsPressed;

if (ButtonWasPressed)
   {
     ButtonBeingTimed = true;
   }
   else
   {
     // Button was just released
     ButtonBeingTimed = false;
     if (currentTime - ButtonStateChangeTime < LongPressInterval)
     {
       // Short Press
     }
   }

ButtonStateChangeTime = currentTime;
 }

if (ButtonBeingTimed && ButtonWasPressed)
 {
   // Button is still pressed
   if (currentTime - ButtonStateChangeTime >= LongPressInterval)
   {
     ButtonBeingTimed = false;
     // Long Press=
   }
 }
}

This layout has been very helpful! Thank you so much! I tried to add my LED in the mix and can only get the latching portion to work. Trying to figure out how to get out of the momentary part of the code.

const int ButtonPin = 2;
const int LedPin = 13;
const unsigned long DebounceTime = 100;
const unsigned long LongPressInterval = 150;


boolean ButtonWasPressed = false;
boolean ButtonBeingTimed = false;
unsigned long ButtonStateChangeTime = 0; // Debounce timer


void setup()
{
Serial.begin(9600);                 // Start the serial monitor to see the results.
char fileName[] = {__FILE__};
Serial.println(fileName);  
pinMode (ButtonPin, INPUT);  // Button between Pin and Ground
digitalWrite(ButtonPin, HIGH);
pinMode(LedPin, OUTPUT); 
}


void loop()
{
checkButton();
}


void checkButton()
{
unsigned long currentTime = millis();


boolean buttonIsPressed = digitalRead(ButtonPin) == LOW;  // Active LOW


// Check for button state change and do debounce
if (buttonIsPressed != ButtonWasPressed &&
currentTime  -  ButtonStateChangeTime > DebounceTime)
{
// Button state has changed
ButtonWasPressed = buttonIsPressed;


if (ButtonWasPressed)
{
ButtonBeingTimed = true;
}
else
{
// Button was just released
ButtonBeingTimed = false;
if (currentTime - ButtonStateChangeTime < LongPressInterval)
{
Serial.println("Short press"); // Short Press
digitalWrite(LedPin, !digitalRead(LedPin));
}
}
ButtonStateChangeTime = currentTime;

}


if (ButtonBeingTimed && ButtonWasPressed)
{
// Button is still pressed
if (currentTime - ButtonStateChangeTime >= LongPressInterval)
{
ButtonBeingTimed = false;
Serial.println("Long press");
digitalWrite(LedPin, HIGH);
//??? How to get out of this 

}else{
Serial.println("Off");
digitalWrite(LedPin, LOW);
}
}
}

as of right now, when the LED has been engaged with a short press, the long press function will just make it blink once and when the LED is completely off, it won't even turn on with the hold function. I tried adding a quick else statement but no luck.

This what you're looking for? One LED latches to the button presses on/off the other blinks if the button is held down > a second.

#include <mechButton.h> 
#include <blinker.h>

#define BTN_MS       1000  // Time out for button press.
#define BTN_PIN      2
#define BLINK_PIN    13
#define LED_PIN      9

                
mechButton  ourButton(BTN_PIN);     // A button on pin 2
timeObj     buttonTimer(BTN_MS);    // A one second timer
blinker     ourBlinker(BLINK_PIN);  // An LED blinker on pin 13
bool        BlinkerOn;              // Saved state of blinker.
bool        LEDon;                  // Saved state of LED.

// Standard setup()..
void setup() {

   ourBlinker.setOnOff(false);   // Turn it off.
   BlinkerOn = false;                     // We start up blinker off.
   pinMode(LED_PIN,OUTPUT);               // Set up for another LED.
   LEDon = false;                         // And LED off.
   ourButton.setCallback(buttonClicked);  // When the button is clicked, we'll call this function.
}


// Our button will call this when it sees a change on its pin.
void buttonClicked(void) {

   if (!ourButton.trueFalse()) {       // If the button has been grounded (pressed)..
      buttonTimer.start();             // Start the timer.
      LEDon = !LEDon;                  // Toggle our LED state
      digitalWrite(LED_PIN,LEDon);     // Set the LED to that state.
   }
}


// And the old loop() function..
void loop() {
   
   idle();                             // Give the background idlers time to do their thing.
   if (!ourButton.trueFalse()) {       // If the button is grounded..
      if (buttonTimer.ding()) {        // And, if the timer has expired..
         ourBlinker.setOnOff(true);    // Fire up the blinker.
         BlinkerOn = true;             // Note it.
      }
   } else {                            // Else, if the button has been released..
      if (BlinkerOn) {                 // If the blinker was on..
         ourBlinker.setOnOff(false);   // Turn it off.
         BlinkerOn = false;            // And note it.
      }
   }
}

If you would like to try this instal LC_baseTools from your IDE library manager.

Have fun!

-jim lee

You are turning the LED off if the button is pressed but has not reached the Long Press time yet. You wanted to turn the LED off when the button is released after a long press. To do that, make a note that you turned on the LED due to a long press. Then, when the button is released, act on that information.

// Short press: Toggle the LED
// Long press: Turn the LED on until button is released
const int ButtonPin = 2;
const int LedPin = 13;
const unsigned long DebounceTime = 100;
const unsigned long LongPressInterval = 150;


boolean ButtonWasPressed = false;
boolean ButtonBeingTimed = false;
boolean InLongPress = false;


unsigned long ButtonStateChangeTime = 0; // Debounce timer


void setup()
{
  Serial.begin(9600);                 // Start the serial monitor to see the results.
  char fileName[] = {__FILE__};
  Serial.println(fileName);
  pinMode (ButtonPin, INPUT);  // Button between Pin and Ground
  digitalWrite(ButtonPin, HIGH);
  pinMode(LedPin, OUTPUT);
}

void loop()
{
  checkButton();
}




void checkButton()
{
  unsigned long currentTime = millis();


  boolean buttonIsPressed = digitalRead(ButtonPin) == LOW;  // Active LOW


  // Check for button state change and do debounce
  if (buttonIsPressed != ButtonWasPressed &&
      currentTime  -  ButtonStateChangeTime > DebounceTime)
  {
    // Button state has changed
    ButtonWasPressed = buttonIsPressed;




    if (ButtonWasPressed)
    {
      // Button was just pressed
      ButtonBeingTimed = true;
    }
    else
    {
      // Button was just released
      if (InLongPress)
        digitalWrite(LedPin, LOW);  // Long press being released
      InLongPress = false;


      ButtonBeingTimed = false;


      if (currentTime - ButtonStateChangeTime < LongPressInterval)
      {
        Serial.println("Short press"); // Short Press
        digitalWrite(LedPin, !digitalRead(LedPin));
      }
    }


    ButtonStateChangeTime = currentTime;
  }


  if (ButtonBeingTimed && ButtonWasPressed)
  {
    // Button is still pressed
    if (currentTime - ButtonStateChangeTime >= LongPressInterval)
    {
      ButtonBeingTimed = false;
      Serial.println("Long press");
      digitalWrite(LedPin, HIGH);
      InLongPress = true;
    }
  }
}

Just came back to this when I finally had the time. I've been trying to rearrange this to where it will turn on the LED, time the switch that way and depending on how many milliseconds, will go into latching or turn off (momentary). As of right now, this will turn on the LED after a certain time has elapsed. What is the order to go about this? I'm having a hard time with the logic.

If the code I provided is not doing what you want, please say what the sketch is doing different from what you wanted.

So instead of timing the button and then turning the LED on, time it while the LED is on. From there if 500ms have passed, after release, it'll go low (momentary). If less than 500ms have passed, the latching code will go into effect and then press and then back to low. Right now there is a delay of 500ms of low LED state. I've tried to put the High states while the button is being pressed and it messes with the latching state. Sorry if my previous response wasn't more specific.

You said you wanted the LED to toggle on a short press:

If you turn on the LED at the start of a press, it will always toggle to OFF when the short press is over.

If you want the LED to be ON during the press, you will need a separate variable to keep track of the previous state so that the toggle can be done correctly.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.