Need help with millis timer

So I found a code example that does something I want and after stripping out the blinking light stuff that always seems to be in every code example (stop that!, It makes it more confusing, really), I was able to set up this

#define PRESSED LOW
#define NOT_PRESSED HIGH
 
int led = 13;

 
const unsigned long shortPress = 100;
const unsigned long  longPress = 500;
 
 
typedef struct Buttons {
    int pin = 10;
    const int debounce = 10;
 
    unsigned long counter=0;
    bool prevState = NOT_PRESSED;
    bool currentState;
} Button;
 
// create a Button variable type
Button button;

void setup() {
  
  pinMode(led, OUTPUT);
  pinMode(button.pin, INPUT_PULLUP);
}
 
void loop() {
      
    // check the button
    button.currentState = digitalRead(button.pin);
 
    // has it changed?
    if (button.currentState != button.prevState) {
        delay(button.debounce);
        // update status in case of bounce
        button.currentState = digitalRead(button.pin);
        if (button.currentState == PRESSED) {
            // a new press event occured
            // record when button went down
            button.counter = millis();
        }
 
        if (button.currentState == NOT_PRESSED) {
            // but no longer pressed, how long was it down?
            unsigned long currentMillis = millis();
            //if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
            if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
                // short press detected. 
                handleShortPress();
            }
            if ((currentMillis - button.counter >= longPress)) {
                // the long press was detected
                handleLongPress();
            }
        }
        // used to detect when state changes
        button.prevState = button.currentState;
    } 
 
   
}
 
void handleShortPress() {
 digitalWrite(led, LOW);  
}
 
void handleLongPress() {
 digitalWrite(led, HIGH);
 delay(5000);
 digitalWrite(led, LOW);

I added the delay part but thats what I need to change.

I want to do the handleLongpress just like that, but I want to be able to make the handleShortPress pull the led LOW if the button is pressed before the 5 seconds is up.

I tried multiple variations of hacking up the blink without delay code so that it didn't blink, just stayed HIGH, but would be able to turn off early and I failed.

I dont know how to do that and I need help.

I cobbled together this code from another example that works as a timer when power is applied and I cant figure out how to apply it to the handleLongButtonPress function either

unsigned long time = millis();

int ledpin = 13;

void setup()
{
  pinMode(ledpin, OUTPUT);        //Onboard LED

 
  digitalWrite(ledpin, HIGH);     //Initial LED state when powered up
}

void loop()
{
  if(millis()-time > 5000)     //Has 5 seconds passed?
    digitalWrite(ledpin, LOW);  //turn off LED
    }

Im lost, frustrated and I think I just need a clear explanation of what I need to do properly so that I can get the idea of how the work flow should be like.

(deleted)

Never use delay in a handler type routine.

#define PRESSED LOW
#define NOT_PRESSED HIGH

const unsigned long shortPress = 100;
const unsigned long  longPress = 500;

unsigned long startedByLongPress;
unsigned long stayOnFor;

struct Buttons { // create a Button variable type
  byte pin = 10;
  int debounce = 10;
  unsigned long counter = 0;
  bool prevState = NOT_PRESSED;
  bool currentState;
} button;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(button.pin, INPUT_PULLUP);
}

void loop() {
  // check the button
  button.currentState = digitalRead(button.pin);

  // has it changed?
  if (button.currentState != button.prevState) {
    delay(button.debounce);
    // update status in case of bounce
    button.currentState = digitalRead(button.pin);
    if (button.currentState == PRESSED) {
      // a new press event occured
      // record when button went down
      button.counter = millis();
    }
    if (button.currentState == NOT_PRESSED) {
      // but no longer pressed, how long was it down?
      unsigned long currentMillis = millis();
      //if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
      if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
        // short press detected.
        handleShortPress();
      }
      if ((currentMillis - button.counter >= longPress)) {
        // the long press was detected
        handleLongPress();
      }
    }
    // used to detect when state changes
    button.prevState = button.currentState;
  }
  if (stayOnFor != 0 && millis() - startedByLongPress >= stayOnFor) {
    stayOnFor = 0;
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void handleShortPress() {
  digitalWrite(LED_BUILTIN, LOW);
}

void handleLongPress() {
  digitalWrite(LED_BUILTIN, HIGH);
  startedByLongPress = millis();
  stayOnFor = 5000;
}

You can find a button library with longPress support here: GitHub - JChristensen/JC_Button: Arduino library to debounce button switches, detect presses, releases, and long presses

Whandall:
Never use delay in a handler type routine.

#define PRESSED LOW

#define NOT_PRESSED HIGH

const unsigned long shortPress = 100;
const unsigned long  longPress = 500;

unsigned long startedByLongPress;
unsigned long stayOnFor;

struct Buttons { // create a Button variable type
  byte pin = 10;
  int debounce = 10;
  unsigned long counter = 0;
  bool prevState = NOT_PRESSED;
  bool currentState;
} button;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(button.pin, INPUT_PULLUP);
}

void loop() {
  // check the button
  button.currentState = digitalRead(button.pin);

// has it changed?
  if (button.currentState != button.prevState) {
    delay(button.debounce);
    // update status in case of bounce
    button.currentState = digitalRead(button.pin);
    if (button.currentState == PRESSED) {
      // a new press event occured
      // record when button went down
      button.counter = millis();
    }
    if (button.currentState == NOT_PRESSED) {
      // but no longer pressed, how long was it down?
      unsigned long currentMillis = millis();
      //if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
      if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
        // short press detected.
        handleShortPress();
      }
      if ((currentMillis - button.counter >= longPress)) {
        // the long press was detected
        handleLongPress();
      }
    }
    // used to detect when state changes
    button.prevState = button.currentState;
  }
  if (stayOnFor != 0 && millis() - startedByLongPress >= stayOnFor) {
    stayOnFor = 0;
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void handleShortPress() {
  digitalWrite(LED_BUILTIN, LOW);
}

void handleLongPress() {
  digitalWrite(LED_BUILTIN, HIGH);
  startedByLongPress = millis();
  stayOnFor = 5000;
}




You can find a button library with longPress support here: https://github.com/JChristensen/Button

The presses work fine. IF I take the delay part off of the longpress so it just digitalWrite(led, HIGH), I can turn the led on with a long press and turn it off with the short press. I want the light to only stay on for a certain time ONCE no blinking and turn itself off if the shortpress isn;t done first. That is precisely what I asked about and for whatever reason, I cannot get my millis timer code to work with a button press, so I came here for some guidance.

What further help than a corrected working version do you need?

The demo Several Things at a Time illustrates the use of millis() to manage timing. It may help with understanding the technique.

...R

If I add the second code to the long press subroutine,

#define PRESSED LOW
#define NOT_PRESSED HIGH
 
int led = 13;

 
const unsigned long shortPress = 100;
const unsigned long  longPress = 500;
unsigned long time = millis(); 
 
typedef struct Buttons {
    int pin = 10;
    const int debounce = 10;
 
    unsigned long counter=0;
    bool prevState = NOT_PRESSED;
    bool currentState;
} Button;
 
// create a Button variable type
Button button;

void setup() {
  
  pinMode(led, OUTPUT);
  pinMode(button.pin, INPUT_PULLUP);
}
 
void loop() {
      
    // check the button
    button.currentState = digitalRead(button.pin);
 
    // has it changed?
    if (button.currentState != button.prevState) {
        delay(button.debounce);
        // update status in case of bounce
        button.currentState = digitalRead(button.pin);
        if (button.currentState == PRESSED) {
            // a new press event occured
            // record when button went down
            button.counter = millis();
        }
 
        if (button.currentState == NOT_PRESSED) {
            // but no longer pressed, how long was it down?
            unsigned long currentMillis = millis();
            //if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
            if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
                // short press detected. 
                handleShortPress();
            }
            if ((currentMillis - button.counter >= longPress)) {
                // the long press was detected
                handleLongPress();
            }
        }
        // used to detect when state changes
        button.prevState = button.currentState;
    } 
 
   
}
 
void handleShortPress() {
 digitalWrite(led, LOW);  
}
 
void handleLongPress() {
 digitalWrite(led, HIGH);
  if(millis()-time > 5000)     //Has 5 seconds passed?
    digitalWrite(led, LOW);  //turn off LED
    

  }

the LED turns on when I do a long press, but it does not turn off on its own and if I do turn it off, I can't turn it back on again without resetting the Arduino.

I not asking for it to be done for me, I need a clear explanation of where I am messing up.

I already have posted the code that does what you want, did you ever bother to read it?

mattyb0:
If I add the second code to the long press subroutine,

#define PRESSED LOW

#define NOT_PRESSED HIGH

int led = 13;

const unsigned long shortPress = 100;
const unsigned long  longPress = 500;
unsigned long time = millis();

typedef struct Buttons {
    int pin = 10;
    const int debounce = 10;

unsigned long counter=0;
    bool prevState = NOT_PRESSED;
    bool currentState;
} Button;

// create a Button variable type
Button button;

void setup() {
 
  pinMode(led, OUTPUT);
  pinMode(button.pin, INPUT_PULLUP);
}

void loop() {
     
    // check the button
    button.currentState = digitalRead(button.pin);

// has it changed?
    if (button.currentState != button.prevState) {
        delay(button.debounce);
        // update status in case of bounce
        button.currentState = digitalRead(button.pin);
        if (button.currentState == PRESSED) {
            // a new press event occured
            // record when button went down
            button.counter = millis();
        }

if (button.currentState == NOT_PRESSED) {
            // but no longer pressed, how long was it down?
            unsigned long currentMillis = millis();
            //if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
            if ((currentMillis - button.counter >= shortPress) && !(currentMillis - button.counter >= longPress)) {
                // short press detected.
                handleShortPress();
            }
            if ((currentMillis - button.counter >= longPress)) {
                // the long press was detected
                handleLongPress();
            }
        }
        // used to detect when state changes
        button.prevState = button.currentState;
    }

}

void handleShortPress() {
digitalWrite(led, LOW); 
}

void handleLongPress() {
digitalWrite(led, HIGH);
  if(millis()-time > 5000)    //Has 5 seconds passed?
    digitalWrite(led, LOW);  //turn off LED

}




the LED turns on when I do a long press, but it does not turn off on its own and if I do turn it off, I can't turn it back on again without resetting the Arduino. 

I not asking for it to be done for me, I need a clear explanation of where I am messing up.

Whandall:
I already have posted the code that does what you want, did you ever bother to read it?

I did. Detecting the long press is not the issue, that part works just fine. I can toggle the light on and off depending the lenth of press as shown in the first code example I posted in my original post. My issue is timing how long the light stays on after its gets there and being able to turn it off early.

Maybe Im missing something crucial and my newbie eyes are missing it?

What do you think these lines do?

 if (stayOnFor != 0 && millis() - startedByLongPress >= stayOnFor) {
    stayOnFor = 0;
    digitalWrite(LED_BUILTIN, LOW);
  }

mattyb0:
Detecting the long press is not the issue, that part works just fine.

I don't think so.

For me at least a long press begins after pressing the button for long, not after releasing it.

Whandall:
What dio you think these lines do?

  if (stayOnFor != 0 && millis() - startedByLongPress >= stayOnFor) {

stayOnFor = 0;
   digitalWrite(LED_BUILTIN, LOW);
 }

Which sketch example has that from the github library zip you linked to?

As far as I can tell by that code you posted, it says that if stayOnFor is not equal to 0 and millis() minus startedBYLongpress is more than or equal to stayOnFor, set stayOnFor to 0 and digitalWrite the built in led off

I seem to be missing something from that, how long is stayOnFor?

Another part of the code I posted above

void handleLongPress() {
  digitalWrite(LED_BUILTIN, HIGH);
  startedByLongPress = millis();
  stayOnFor = 5000;
}

Whandall:
I don't think so.

For me at least a long press begins after pressing the button for long, not after releasing it.

I got it from the creator of that library you posted

That does not make it better.

FYI

/*SwitchManager skeleton
 This sketch is to introduce new people to the SwitchManager library written by Nick Gammon
 
 The library handles switch de-bouncing and provides timing and state change information in your sketch.
 The SwitchManager.h file should be placed in your libraries folder, i.e.
 C:\Users\YourName\Documents\Arduino\libraries\SwitchManager\SwitchManager.h
 You can download the library at:
 http://gammon.com.au/Arduino/SwitchManager.zip    Thank you Nick!
 
 In this example we have 2 normally open (N.O.) switches connected to the Arduino - increment and decrement.
 The increment switch will also be used as a "Reset" switch if pressed for more than two seconds.
 The two switches are connected between GND (0 volts) and an Arduino input pin.
 The library enables pull-up resistors for your switch inputs.
 Pushing a switch makes its pin LOW. Releasing a switch makes its pin HIGH.
 
 The SwitchManager library provides 10ms de-bounce for switches. 
 i.e. enum { debounceTime = 10, noSwitch = -1 };
 If you need more time, edit the SwitchManager.h file
 i.e. enum { debounceTime = 50, noSwitch = -1 }; //here it is changed to 50ms
 */

#include <SwitchManager.h>             
//object instantiations
SwitchManager myIncSwitch;
SwitchManager myDecSwitch;

unsigned long currentMillis;
unsigned long heartBeatMillis;
unsigned long heartFlashRate  = 500UL; // time the led will change state       
unsigned long incShortPress   = 500UL; // 1/2 second
unsigned long incLongPress    = 2000UL;// 2 seconds 
unsigned long decShortPress   = 500UL; // 1/2 second

const byte heartBeatLED       = 13;
const byte incSwitch          = 4; //increment switch is on Arduino pin 4
const byte decSwitch          = 5; //decrement switch is on Arduino pin 5

int myCounter;

//======================================================================

void setup()
{
  Serial.begin(9600);

  //gives a visual indication if the sketch is blocking
  pinMode(heartBeatLED, OUTPUT);  

  myIncSwitch.begin (incSwitch, handleSwitchPresses); 
  myDecSwitch.begin (decSwitch, handleSwitchPresses);
  //the handleSwitchPresses() function is called when a switch changes state

} //                   E N D  O F  s e t u p ( )

//======================================================================

void loop()
{
  //leave this line of code at the top of loop()
  currentMillis = millis();

  //***************************
  //some code to see if the sketch is blocking
  if (CheckTime(heartBeatMillis, heartFlashRate, true))
  {
    //toggle the heartBeatLED
    digitalWrite(heartBeatLED,!digitalRead(heartBeatLED));
  }

  //***************************
  //check to see what's happening with the switches
  //"Do not use delay()s" in your sketch as it will make switch changes unresponsive 
  //Use BlinkWithoutDelay (BWD) techniques instead.
  myIncSwitch.check ();  
  myDecSwitch.check (); 

  //***************************
  //put other non-blocking stuff here


} //                      E N D  O F  l o o p ( )


//======================================================================
//                          F U N C T I O N S
//======================================================================


//                        C h e c k T i m e ( ) 
//**********************************************************************
//Delay time expired function
//parameters:
//lastMillis = time we started
//wait = delay in ms
//restart = do we start again  

boolean CheckTime(unsigned long  & lastMillis, unsigned long wait, boolean restart) 
{
  //has time expired for this task?
  if (currentMillis - lastMillis >= wait) 
  {
    //should this start again? 
    if(restart)
    {
      //yes, get ready for the next iteration
      lastMillis = millis();  
    }
    return true;
  }
  return false;

} //                 E N D   o f   C h e c k T i m e ( )


//                h a n d l e S w i t c h P r e s s e s( )
//**********************************************************************

void handleSwitchPresses(const byte newState, const unsigned long interval, const byte whichPin)
{
  //  You get here "ONLY" if there has been a change in a switches state.

  //When a switch has changed state, SwitchManager passes this function 3 arguments:
  //"newState" this will be HIGH or LOW. This is the state the switch is in now.
  //"interval" the number of milliseconds the switch stayed in the previous state
  //"whichPin" is the switch pin that we are examining  

  switch (whichPin)
  {
    //***************************
    //are we dealing with this switch?
  case incSwitch: 

    //has this switch gone from LOW to HIGH (gone from pressed to not pressed)
    //this happens with normally open switches wired as mentioned at the top of this sketch
    if (newState == HIGH)
    {
      //The incSwitch was just released
      //was this a short press followed by a switch release
      if(interval <= incShortPress) 
      {
        Serial.print("My counter value is = ");
        myCounter++;
        if(myCounter > 1000)
        {
          //limit the counter to a maximum of 1000
          myCounter = 1000; 
        }
        Serial.println(myCounter);
      }

      //was this a long press followed by a switch release
      else if(interval >= incLongPress) 
        //we could also have an upper limit
        //if incLongMillis was 2000UL; we could then have a window between 2-3 seconds
        //else if(interval >= incLongMillis && interval <= incLongMillis + 1000UL) 
      {
        //this could be used to change states in a StateMachine
        //in this example however, we will just reset myCounter
        myCounter = 0;
        Serial.print("My counter value is = ");
        Serial.println(myCounter);
      }

    }

    //if the switch is a normally closed (N.C.) and opens on a press this section would be used
    //the switch must have gone from HIGH to LOW 
    else 
    {
      Serial.println("The incSwitch was just pushed");
    } 

    break; //End of case incSwitch

    //*************************** 
    //are we dealing with this switch?
  case decSwitch: 

    //has this switch gone from LOW to HIGH (gone from pressed to not pressed)
    //this happens with normally open switches wired as mentioned at the top of this sketch
    if (newState == HIGH)
    {
      //The decSwitch was just released
      //was this a short press followed by a switch release
      if(interval <= decShortPress) 
      {
        Serial.print("My counter value is = ");
        myCounter--;
        if(myCounter < 0) 
        {
          //don't go below zero
          myCounter = 0;
        }
        Serial.println(myCounter);
      }

    }

    //if the switch is a normally closed (N.C.) and opens on a press this section would be used
    //the switch must have gone from HIGH to LOW
    else 
    {
      Serial.println("The decSwitch switch was just pushed");
    } 

    break; //End of case decSwitch

    //*************************** 
    //Put default stuff here
    //default:
    //break; //END of default

  } //End switch (whichPin)

} //      E n d   o f   h a n d l e S w i t c h P r e s s e s ( )


//======================================================================
//                      E N D  O F  C O D E
//======================================================================

Whandall:
What do you think these lines do?

 if (stayOnFor != 0 && millis() - startedByLongPress >= stayOnFor) {

stayOnFor = 0;
    digitalWrite(LED_BUILTIN, LOW);
  }

Whandall:
Another part of the code I posted above

void handleLongPress() {

digitalWrite(LED_BUILTIN, HIGH);
  startedByLongPress = millis();
  stayOnFor = 5000;
}

This looks like a state machine code and timer for it, but Im having a hard time understanding the skeleton code of it. Im looking for the why and how the code works in the situation Im trying to implement and not the actual code, if that makes any sense.

A lot of the answers are still going over my head and seem cryptic to a person like me that doesn't have a firm grasp on the basics. If only you could see my search history for the last few weeks trying to understand things, you'd see Im trying and its just not clicking.

If anything, could we back up, not worry about button presses or the like and just break down what you posted? The biggest issue for me is trying to figure out where to put what and why. The stickied post regarding Doing multiple things at the same time that Robin2 posted has been read at least 15 times over by me, but I seem to be getting dragged down by the servo code and blinking ect instead of catching on to the backbone of it.

That said we have a state machine that checks the press, in your second code example, Im hung up on how " startedByLongPress = millis();" works in the grand scheme of things. Is statedbyLongPress and sub routine, an unsignied long, an int?

could you comment the lines of code so I can follow along? You know this stuff, I dont and Im just trying to figure out the hows and whys. In my regular line of work, there is a similar steep learning curve, but it was much easier for me to learn the whys of things to flatten it out somewhat and thats all Im looking for. Why is my code bad, what should I improve on that kind of stuff. Its hard to follow when the answer is a chink of code with no explanation of how it works and that is what has been so frustrating about searching a lot of the question asked on the forum.

mattyb0:
That said we have a state machine that checks the press, in your second code example,
Im hung up on how " startedByLongPress = millis();" works in the grand scheme of things.
Is statedbyLongPress and sub routine, an unsignied long, an int?

Another part of the code I posted:

unsigned long startedByLongPress;
unsigned long stayOnFor;

I have already read my additions to your code to you three times, and no, I will not spoonfeed each line.

startedByLongPress = millis(); just remembers the time the LED went on,
to shut it down again when the wished duration (stayOnFor) has expired.

A simple boolean (stayOnFor being unequal to zero) could be seen as a most primitive state machine,
but I feel that is only a big name for an innocent condition.

If you use a library for the Button debouncing, the code shrinks down to

#include <Bounce2.h>
// https://github.com/thomasfredericks/Bounce2

#define PRESSED LOW
#define NOT_PRESSED HIGH

const byte buttonPin = 10;

const unsigned long longPress = 500;
const unsigned long liteLedFor = 5000;

unsigned long keyWentDown;
unsigned long startedByLongPress;
unsigned long stayOnFor;

Bounce key;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  key.attach(buttonPin, INPUT_PULLUP);
}

void loop() {
  // check the button
  if (key.update()) {         // true on a change
    if (key.fell()) {         // went down, remember when
      keyWentDown = millis();
    } else if (key.rose()) {  // key released
      if (millis() - keyWentDown < longPress) {
        handleShortPress();
      }
    }
  } else {        // no change
    if (key.read() == PRESSED) { // key still pressed?
      if (millis() - keyWentDown >= longPress) {  // long enough down?
        handleLongPress();
      }
    }
  }
  // if the LED is on
  if (stayOnFor != 0) {
    // check whether on long enough
    if (millis() - startedByLongPress >= stayOnFor) {
      stayOnFor = 0;
      digitalWrite(LED_BUILTIN, LOW);
    }
  }
}

void handleShortPress() {
  digitalWrite(LED_BUILTIN, LOW);
  stayOnFor = 0;
}

void handleLongPress() {
  digitalWrite(LED_BUILTIN, HIGH);
  startedByLongPress = millis();
  stayOnFor = 5000;
}

This code detects the long press as soon as a key is held down long enough.

Crap, nevermind, I thought you had just replied and had my code in your reply and not changed it and just posted a link to the library.

I just noticed it.

I dont see any of the code you are posting from the link you posted???
~~WTF am I missing that you posted? I also read the readme for it and ther was nothing that I could find that matched up with what you are posting. I attached the 3 examples that are included with the library you said I should use and they aren't there. ~~

SimpleOnOff.ino (2.16 KB)

LongPress.ino (4.83 KB)

UpDown.pde (3.96 KB)