One of the common questions related to using the millis() function in Arduino, is around timed events. After an event occurs, you want the code to wait for some time before doing the next step. But you don’t want to stop the program with delay().
In this example, we will use millis() to wait a few seconds after a pushbutton press to turn on an LED. Then a few seconds later, we will turn it off. All without using delay().
Understanding millis()
The Arduino millis() function will let you accomplish this delayed action relatively easily. First, read through my multitasking with millis() tutorial and then look at some of my millis() cookbook examples I’ve already posted. Getting used to seeing this kind of code will make this line by line tutorial on timed events using millis() easier to follow.
The Full Code
(FYI, you can grab the complete code from pastebin if copy/paste doesn’t work here.)
//Global Variables const byte BUTTON=2; // our button pin const byte LED=13; // LED (built-in on Uno) unsigned long buttonPushedMillis; // when button was released unsigned long ledTurnedOnAt; // when led was turned on unsigned long turnOnDelay = 2500; // wait to turn on LED unsigned long turnOffDelay = 5000; // turn off LED after this time bool ledReady = false; // flag for when button is let go bool ledState = false; // for LED is on or not. void setup() { pinMode(BUTTON, INPUT_PULLUP); pinMode(LED, OUTPUT); digitalWrite(LED, LOW); } void loop() { // get the time at the start of this loop() unsigned long currentMillis = millis(); // check the button if (digitalRead(BUTTON) == LOW) { // update the time when button was pushed buttonPushedMillis = currentMillis; ledReady = true; } // make sure this code isn't checked until after button has been let go if (ledReady) { //this is typical millis code here: if ((unsigned long)(currentMillis - buttonPushedMillis) >= turnOnDelay) { // okay, enough time has passed since the button was let go. digitalWrite(LED, HIGH); // setup our next "state" ledState = true; // save when the LED turned on ledTurnedOnAt = currentMillis; // wait for next button press ledReady = false; } } // see if we are watching for the time to turn off LED if (ledState) { // okay, led on, check for now long if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay) { ledState = false; digitalWrite(LED, LOW); } } }
Global Variables
const byte BUTTON=2; // our button pin const byte LED=13; // LED (built-in on Uno)
Setup a constant for BUTTON and LED. (Read this post on why I am using const instead of #define.)
unsigned long buttonPushedMillis; // when button was released unsigned long ledTurnedOnAt; // when led was turned on
These two variables will store the “current” value of millis() when their “event” occurs. Instead of trying to reset millis(), we will compare against itself later on.
unsigned long turnOnDelay = 2500; // wait to turn on LED unsigned long turnOffDelay = 5000; // turn off LED after this time
These two values are arbitrary for this example. It’s how long we wait until we turn on the LED and then how long we wait to turn off the LED.
bool ledReady = false; // flag for when button is let go bool ledState = false; // for LED is on or not.
Two flag variables to create the states for the LED. For this simple example, I didn’t create an enum to track the states, but that could have been an option too.
ledReady tells the Arduino to wait the amount of time in “turnOnDelay” before turning on the LED. Then it enables the next state, which is waiting to turn off the LED.
void setup()
void setup() { pinMode(BUTTON, INPUT_PULLUP); pinMode(LED, OUTPUT); digitalWrite(LED, LOW); }
Nothing unique here. In my circuit, I am using the built-in pull-up resistor on my button pin. Just so I know what state the LED pin is in, I set it to LOW.
void loop()
void loop() { // get the time at the start of this loop() unsigned long currentMillis = millis();
Remember that loop() runs around and around. So each time loop() starts over, I store the current “time.” Back on the “blink without delay line by line” post, I used the analogy of looking at a watch. That’s what we are doing here, getting the current time.
Note: This is where beginners get tripped up. They get focused on the XY Problem of Resetting millis(), it isn’t necessary. Just check to see what time it is!
Check the button
if (digitalRead(BUTTON) == LOW) {
Since the button uses a pull-up resistor, a LOW means the button is being pressed.
Keep in mind, the Arduino checks this single if-statement a THOUSAND times while a human being pushes the button.
Store the time
buttonPushedMillis = currentMillis;
As long as the button is down we are updating buttonPushedMillis with the value in currentMillis. Another option would have been to determine when the button was released, but that would have made the code more complicated.
Even if the button bounces a little bit, this code works fine.
ledReady = true;
This flag enables the next “state” which is to wait long enough to turn on the LED.
Turn on the led?
if (ledReady) {
It might seem silly to set ledReady to TRUE and then immediately check to see if it is TRUE. None of this code “stops and waits.” We use this “flag” to let the Arduino know when we are ready to turn on the LED.
if ((unsigned long)(currentMillis - buttonPushedMillis) >= turnOnDelay) {
Standard millis() check. We compare the current value of millis(), stored in currentMillis, to the value in buttonPushedMillis.
Keep in mind, buttonPushedMillis will keep getting updated as long as something is pressing the button down. So until you let go of the button, (currentMillis – buttonPushedMillis) is going to be zero (0).
The gap between currentMillis and buttonPushedMillis will grow after the button is released. Once it gets to 2500 or more, the if-statement becomes TRUE.
digitalWrite(LED, HIGH);
Turn on our LED. Hopefully this one is obvious. 🙂
ledState = true;
Get into the next state, which is waiting long enough to turn off the LED.
ledTurnedOnAt = currentMillis;
Just like the button push, we store the time when the LED turned on. Again, we just look at the clock so we can compare how long the LED has been on.
ledReady = false;
If we don’t set this state to FALSE, then ledTurnedOnAt will keep getting updated, and the LED would never turn off.
When to turn Off LED
if (ledState) {
Here’s our final state. We’re waiting to turn off the LED
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay) {
The only similarity between this line and line 32 is that we are still comparing against currentMillis. LedTurnedOnAt and turnOffDelay are specific to this state.
ledState = false; digitalWrite(LED, LOW);
Clear the flag that enabled this state. And turn off the LED.
Conclusion
That’s it. This code delays turning on the LED after you release the button, and then the LED stays on for a given time. No delay()s, so you could use the code to do other tasks.
Exercise for the reader
Look what happens if you press the button again while the LED is on. How long does it stay on? Why? Is there a way to “reset” the on-time each time the button is pressed?
Thank-you, Thank-you, Thank-you!! I am trying to learn Arduino while working on a project. I have the project working using old school hardware mainly based on mono-stable 555 timers. But then I decided to add a color display to serve as an object counter and Arduino was the best way I could think of to do it. Since I was going to add a pro-mini for the display I figured it would only make sense to do the whole thing with the Arduino. Once I had the counter/display working I tried to add the other functions that were being handled by the 555s. That’s when I quickly learned that using Delay wasn’t going to work. I’ve been trying to figure out how to implement Millis() for a week or more and just wasn’t getting anywhere. I wasn’t even sure Millis() was the way to go about it. I finally found this tutorial and at 1st thought it might work with a few changes. Then I realized that it was verbatim exactly what I was looking for. I ended up changing pin assignments and renaming the variables to reflect my project but otherwise I just copied your sketch. Now that i see how it is done I realize my attempts weren’t even close!! You have gotten my project past a major hurdle. It now runs simultaneous with the counter and i can move on to other aspects. In the meantime I will be studying this sketch (which I now call the 555 timer sketch lol) until I understand the reason for each line. You put a big smile on my face today. Thank-you again.
James Phillips
Magnificent! You are brilliant explaining and very generous of share your knowled
Nice job! Can we use this type of led off latency after a few push of button. Delay after state change (button push counter)
Sure, why not.
So in my case I am turning on a relay from a web interface button (to open a garage door) but I want it to stay on after I “unclick” the button for 500ms to make sure the contactor on the motor has time to pull in.
Can I compare the time from when the button was pressed (openTime) to millis() + 500. Is there a reason this won’t work? I ran it and it does not seem to be working but I’m wondering if its a syntax error.
if (request.indexOf(“/Open=HIGH”) != -1) {
digitalWrite(relayOpen, HIGH);
openTime = millis();
}
if (millis() >= (openTime + 500));{
digitalWrite(relayOpen, LOW);
}
Thank you for this write up
Code snippets rarely contain the actual problem.
#1 What variable type is openTime. It needs to be an unsigned long.
#2 The if-statement needs to be called inside of a loop. You can’t check if 500ms has passed, unless you keep looking at it.
#3 This code isn’t rollover-safe. To make the numbers easier to follow, let’s say rollover occurs at 10000. So let’s say openTime is 9900. Adding 500 to that will cause it to rollover to 400. Problem is when the if-statement before millis() rolls over your check could be 9904 >= 400. That would evaluate as true. This reason is why you should always use “the current time” subtracted from a “previous time” compared to an inverval. (I doubt this is why the code is not working though, since you probably haven’t hit rollover yet. unless maybe you’re using an int for openTime. However this will give you problems eventaully)
Very nice, just what I was looking for.
Needed a push button to initiate a timed output to reset a real time clock at midnight
Thank you
does the “check the button” image misleading? seems like it is missing a supply and resistor.
No. Both are provided by using the internal pull-up resistor (enabled with line 13.)
What if you wanted to change this code so that it starts the timer as soon as the button is pressed instead of released? In my case, a switch will turn on and stay on for the entire loop. I want the LED to turn on as soon as the switched is set HIGH.
You would just need to follow the button state change example on arduino.cc.
In the if-statement, check to see if the button is ON (HIGH or LOW depending on your situation) and then set the ledReady to true.
This doesn’t seem to work correctly. Need it to turn the LED on for 3 seconds as soon as the button is pressed, and then off – although the button will remain pressed until reset.
I don’t have time to look in depth, but add an AND statement to your button check to ignore it if co2ValveReady is true. There’s no reason to check the button at that point (and you keep updating the buttonPushedMillis) in that case.
did you get this sorted out? if so can you share? In similar situation at the moment. Thanks
I amend code to blink when you press button and at the end of setup time, but when ever you press button within this time led is blinking again and I want to switch it off this behavior.
something like this:
press the button –> LED (on and off)
time is ticking
press the button again –> LED stay off only millis() time is reset to begin
time is off –> LED (on and off)
I would create a flag called “isBlinking”. When the timer runs out, set it to false. If the button is pressed and isBlinking is true, set it to false.
How you can disable led blinking every time when you press button with in turnOffDelay?
I don’t understand your question. I guess you need to create a flag variable for LED-blinking.
Can be the possibility of millis() roll over if the arduino is left ON for enougth time ????
Yes, of course. It rolls over about every 49 days. This code is specifically written to handle the rollover.
This article discusses how this rollover handling works.