Why You Shouldn’t Always Use the Arduino Delay Function

The very first time you used an Arduino board, you probably did something like this:

  • Connected an LED to your Arduino
  • Uploaded the default blink sketch that would turn on and off your LED every second

This is called the “Hello World” program of Arduino and shows that with just a few lines of code you can create something that has a real world application.

blink sketch

In the preceding example, you use the delay() function to define the intervals between the LED turning on and off.

Here’s the deal: while delay() is handy and works for basic examples, you really shouldn’t be using it in the real world… Keep reading to learn why.

How delay() Function Works

The way the Arduino delay() function works is pretty straight forward.

It accepts a single integer as an argument. This number represents the time in milliseconds the program has to wait until moving on to the next line of code.

When you do delay(1000) your Arduino stops on that line for 1 second.

delay() is a blocking function. Blocking functions prevent a program from doing anything else until that particular task has completed. If you need multiple tasks to occur at the same time, you simply cannot use delay().

If your application requires that you constantly read/save data from inputs, you should avoid using the delay() function. Luckily there is a solution.

millis() Function to the Rescue

The millis() function when called, returns the number of milliseconds that have passed since the program was first started.

Why is that useful?

Because by using some math, you can easily verify how much time has passed without blocking your code.

The sketch below shows how you can use the millis() function to create a blink project. It turns the LED light on for 1000 milliseconds, and then turns it off. But, it does it in a way that’s non-blocking.

Let’s take a closer look at a blink sketch that works without a delay function:

    Blink without Delay, example here: arduino.cc/en/Tutorial/BlinkWithoutDelay

// constants won't change. Used here to set a pin number :
const int ledPin =  13;      // the number of the LED pin

// Variables will change :
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);

View raw code

This sketch above can be found here and it works by subtracting the previous recorded time (previousMillis) from the current time (currentMillis). If the remainder is greater than the interval (in this case, 1000 milliseconds), the program updates the previousMillis variable to the current time, and either turns the LED on or off.

And because it’s a non-blocking, any code that’s located outside of that first if statement should work normally.

You can now understand that you could add other tasks to your loop() function and your code would still be blinking the LED every one second.

Which function should you use?

We’ve learned two different ways of dealing with time with the Arduino. Using the millis() functions takes a little of extra work when compared to using delay(). But your programs can’t do multitasking on the Arduino without it.

Share this post with a friend that also likes electronics!

You can contact me by leaving a comment. If you like this post probably you might like my next ones, so please support me by subscribing my blog and my Facebook Page.

Thanks for reading,

-Rui Santos

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

25 thoughts on “Why You Shouldn’t Always Use the Arduino Delay Function”

  1. I would change the
    previousMillis = currentMillis;
    previousMillis += interval;
    Because when there is another task that takes more time so that the led task doesn’t get called many times in a second, the timing wouldn’t get much off.
    If for example the other task takes 450ms of time, the led would switch at 1350ms each time.
    With the += interval the first switch would be at 1350ms, but the second switch at 2250ms, that’s better that 2700ms.

  2. The Metro library is also extreme useful for implementing timed events, and avoiding the use of delay().
    The use of millis() and/or Metro, in conjunction with Finite State Machine (FSM) implementation means you can you can do many tasks at once .
    The example that Rue has shown is a simple FSM which only has two finite states and the state variable is ledState. (IMHO that variable should be a BOOL though).

    • I’ve used the sketch that comes in the Arduino examples, so people might be more familiar with that sketch.
      I agree with you, there is definitely room for improvement in that sketch above

  3. Thank you for this article, Rui!
    In order to build Arduino applications using multiple timers while keeping the loop clean and tidy, I created the Timer library: github.com/dniklaus/arduino-utils-timer.
    This library’s Timer encapsulates the time evaluation and triggers a timeExpired() event.

  4. OK, I tried to change lines in the code. So, what I did:
    1. You’re assigning previous as VAR1 and current as VAR2 >> to my understanding 🙂
    2. I tried to cancel both the current or previous variables or one of them, and incorporate millis() directly in the calculations, so as a result the LED got continuous OFF or ON after several or more blinks. I think it’s because the WTD is triggered.
    3. I changed >= to ==, it’s still working. I don’t know if the timings are the same.
    4. I Canceled interval and applied direct value of 1000.

    I understood that:
    1. current would get the same continuous change in time, so it’s changing all the time.
    2. previous is connected to current after the 1st if statement, so it gets the +-1000 value.

    My questions:
    1. Could you explain why you assigned 2 variables? current an previous.
    2. Why I have to apply 2 arguments with the bitwise >= or ==, in:

    if (current-previous >= 1000)

    instead of 1 direct argument?
    if (current>= 1000) or if ( millis() >= 1000).


  5. Hi I try to use one of Your scetches to send RF data from a remote controle with 16 channels to use for my irrrigation system. I decoded all ready the transmitting side and wane send the code with an Arduin Nano, Uno and an ESP 32.
    Though I have to send one time the data to switch on the relay from channel 1, as an example , for 50 miliseconds, than the relay stays on to switch on the valve for 20 min, than the Arduino must send again the same signal for 50 msec to switch of ch 1 (relay 1) wait (100 msec) and switch on ch 2 (50 msec) with an other data code. This are al ready 5 delays this would be verry easy bud not with “millis” I dont like to waist my time with this !
    I try to find another way, i see micro pyton seems to have match more less problems with this, I try to learn bud I’m probably to old to understand ?

    Can some body help here ?

    Thanks in advance proffesors ?


  6. Hi. When you compare intervals there is the problem of what happens when millis() rolls over when it reaches the uLong limit which happens about every 50 days. I have found many articles on how to properly code to avoid problems when the rollover happens. However I would like to test my code and I don’t want to wait for 50 days to validate my sketches behaviour 😉 . Is there a way to force the millis counter and set it just a few minutes before the limit (4294947295) ? I have found an article here https://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover that explains how to do that on Arduino, but I have an ESP32 and would like something similar for ESP32. Any suggestion ?


Leave a Comment

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.