Arduino Delay [Tutorial]

In this tutorial you’ll learn how to properly use the delay() function to add some delay between 2 actions in your Arduino programs. Then, you will discover why using delay() is often not a good idea when you want to make your programs scale, and how to fix that.

All right, let’s get started!

Why do you need delays in your Arduino programs?

Well, an Arduino program relies a lot on external inputs/outputs to work.

Taking a simple example: you might want to monitor the state of a push button 100 times per second, or make a LED blink every 0.5 second.

Now, how can you achieve that? An Arduino programs works like this:

  • First, the setup() function is executed once.
  • Then the loop() function is executed over and over again, until you power off your Arduino.

Any program that you write will be executed at full speed. So if you’re making an LED blink in your code, what’s going to happen is that the LED will blink at a very high rate (multiple thousands times per second at least).


You are learning Arduino programming?

Check out Arduino Programming For Beginners and learn step by step.


If you want to control time – that is, make sure the LED blinks only every 0.5 second and not at full speed – you need to add some delay in your code.

Implementing Arduino delay in your code

The Arduino delay() function

Here’s a code to make an LED blink (here we choose the built-in LED, no need to add any external LED) every 0.5 seconds – this is one of the most common examples you’ll see if you begin with Arduino.

#define LED_PIN 13

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_PIN, HIGH);
  delay(500);
  digitalWrite(LED_PIN, LOW);
  delay(500);
}

So, how does this code work?

First you use a “define” so you don’t have to hard-code the number “13” multiple times in your code and it makes your program more readable.

You setup the mode of the pin to OUTPUT with the pinMode() function.

Then, in the loop() function, you:

  1. Power on the LED
  2. Use delay(500) to make the program sleep for 500 milliseconds, or 0.5 seconds
  3. Power off the LED
  4. Use delay(500) to make the program sleep again for 0.5 seconds
  5. And go back to step 1, because the loop() function keeps being executed again and again.

The delay() function expects you to give a number of milliseconds – not seconds – to sleep. So, if you think in seconds, then multiply the number by 1000 and you have the value you need.

Make an Arduino delay for 1 minute

If you want to make your Arduino sleep for 1 minute, or for multiple minutes, then it’s quite easy.

Take the number of minutes, multiply it by 60 to get the number of seconds, and then multiply it by 1000 to get the number of milliseconds.

Ex: delay(3 * 60 * 1000); will make the program sleep for 3 minutes.

The Arduino delayMicroseconds() function

If you need to be more precise you may have to use the delayMicroseconds() function. With delayMicroseconds() you can provide a number of microseconds to sleep. The minimum duration for delay() is 1 millisecond, so if you need to sleep for only 400 microseconds, here you have a new solution.

This function can be very useful when communicating with some hardware components. For example, the Arduino needs to send some data to the component and then read some response. Let’s say the component needs 6 microseconds to process the request and deliver accurate data.

Then in your program you may have something like this:

...

void loop() {
  // send data to component
  delayMicroseconds(6);
  // reach out to component again to read data
}

Non blocking delay – why you should avoid using delay()

So, now that you understand what is a delay in Arduino, and how to implement it with delay() and delayMicroseconds(), let’s see how to use the delay functionality – but without those functions.

Why?

When you use delay(), the execution of your program will stop, and only resume after the delay is finished.

This is OK if you only have one action to do – example: blink a single LED – but what if you need to take care of multiple hardware components or communicate with other devices such as Arduino boards or your own computer?

Using the previous example, how could you make one LED blink every 0.5 second, and another one blink every 0.8 second?

In that case you will be stuck quite quickly. If you still want to use delay(), you may find a solution where you use shorter delays between multiple actions, but this will make your program more complicated every time you add a new action.

The solution to that is simply to monitor the time without using the delay() function, basically to find a way to reproduce the behavior of delay() without using it.

Code example – Arduino delay without delay()

Let’s rewrite our blink LED example without using delay().

#define LED_PIN 13

unsigned int lastTimeLedBlinked = millis();
unsigned int delayBetweenBlink = 500;
byte ledState = LOW;


void setup() {
  pinMode(LED_PIN, OUTPUT);

}

void loop() {
  unsigned int timeNow = millis();
  if (timeNow - lastTimeLedBlinked > delayBetweenBlink)
  {
    if (ledState == LOW)
    {
      ledState = HIGH;
    }
    else
    {
      ledState = LOW;
    }
    digitalWrite(LED_PIN, ledState);
    lastTimeLedBlinked = timeNow;
  }
}

Let’s analyze this code line by line.

Initialization

#define LED_PIN 13

unsigned int lastTimeLedBlinked = millis();
unsigned int delayBetweenBlink = 500;
byte ledState = LOW;

First, you initialize 3 variables in the global scope:

  • lastTimeLedBlinked: this variable will be used to store the last time the LED blinked. Basically, every time we’ll make the LED blink, we’ll update this variable with the current time – using the millis() function.
  • delayBetweenBlink: this is the time you want to wait between 2 actions – here the action is to blink the LED.
  • ledState: we will need to store the current LED’s state (HIGH or LOW) so we can know what was the previous state, and take action accordingly.

Why in the global scope? Well, what we plan to do is to update those variables in the loop() function and access them again the next time we go inside the loop(). If we create the variables inside the loop(), the variables will be local variables and thus will be destroyed when you exit the loop() function. Thus, the next time loop() is called again, all values will be lost and you’ll create variables again with no previous value inside. Here, by creating the variables outside of the function we can make them “survive” and keep their value every time we enter the loop().

Implementing the Arduino delay functionality in the loop function

Now, let’s see what happens in the loop() function.

void loop() {
  unsigned int timeNow = millis();

First, you read the current time with the millis() function.

  if (timeNow - lastTimeLedBlinked > delayBetweenBlink)
  {

And now you compare the current time you’ve just read, with the previous time the LED blinked. If enough time has passed (more than the value stored in delayBetweenBlink), then it means you can enter the if().

This code structure – reading the time and comparing it to the previous time you’ve done an action – is how you replace the delay() function. Basically you’re just computing a duration here. And as you can see it means your program contains more lines for a simple application, but it gives you much more control and it’s much more scalable.

So, what will happen? Well, the loop() will keep being executed at full speed. Every time, your program will check if enough time has passed. If not, then the loop() ends here, because it won’t enter the if structure.

And, when just enough time has passed, we enter the if().

Executing the action

    if (ledState == LOW)
    {
      ledState = HIGH;
    }
    else
    {
      ledState = LOW;
    }
    digitalWrite(LED_PIN, ledState);

OK, you’ve just entered the if(), and this where you’ll do whatever action you need to do.

Here we make the LED blink. And because we can’t know directly what was the previous state for the LED (since we’ve entered the loop() many times and lost all local variables created inside), we get that state from a global variable.

What we do with this global variable is simple: if it was LOW, we set it to HIGH, and if it was HIGH, we set it to LOW. And then, of course, we update the physical LED state accordingly, with digitalWrite().

    lastTimeLedBlinked = timeNow;
  }
}

Finally, and this is super important, we save the current time as the last time we blinked the LED. If we don’t do that then the program will blink the LED at full speed because the condition inside the if() will always be true.

By setting the previous time to the current time we “reset the timer”.

So, in that example, the if() will only be entered every 500 milliseconds, or 0.5 seconds.

And now this is great because your program doesn’t stop, so you can continue to execute different independent actions while still “waiting” to blink the LED.

2 actions “at the same time”

For example, let’s say you want to blink an LED every 0.5 second, and another one every 0.8 second.

#define LED_1_PIN 13
#define LED_2_PIN 10

unsigned int lastTimeLed1Blinked = millis();
unsigned int delayBetweenBlink1 = 500;
byte led1State = LOW;

unsigned int lastTimeLed2Blinked = millis();
unsigned int delayBetweenBlink2 = 800;
byte led2State = LOW;


void setup() {
  pinMode(LED_1_PIN, OUTPUT);
  pinMode(LED_2_PIN, OUTPUT);
}

void loop() {
  unsigned int timeNow = millis();

  // Action 1 - Blink LED 1
  if (timeNow - lastTimeLed1Blinked > delayBetweenBlink1)
  {
    if (led1State == LOW)
    {
      led1State = HIGH;
    }
    else
    {
      led1State = LOW;
    }
    digitalWrite(LED_1_PIN, led1State);
    lastTimeLed1Blinked = timeNow;
  }

  // Action 2 - Blink LED 2
  if (timeNow - lastTimeLed2Blinked > delayBetweenBlink2)
  {
    if (led2State == LOW)
    {
      led2State = HIGH;
    }
    else
    {
      led2State = LOW;
    }
    digitalWrite(LED_2_PIN, led2State);
    lastTimeLed2Blinked = timeNow;
  }
}

As you can see, we repeat the code structure for the second action we’ve added. And both actions won’t disturb each other!

Recap

If you want to make an Arduino delay without using delay():

  1. Create a global variable to store the last time you did a specific action.
  2. Create another global variable to store the duration you want between 2 actions.
  3. In the loop() function, read the current time with millis().
  4. Just after that, use a if structure, and use the condition (timeNow - lastTimeActionWasExecuted > delayBetweenActions).
  5. Once you’ve entered the if(), do the action.
  6. And still in the if(), store the current time as the previous time.

You can repeat those steps for every action for which you need an Arduino delay.

When it’s ok to use delay() and delayMicroseconds()

There are specific occasions when using delay() in your code is still OK.

Here are 2 of those:

  • You need to initialize a component during the setup of your program, and this component needs some time to get initialized – for example 1.5 seconds. In this case, using delay(1500) in your setup() function is perfectly fine. Basically, any delay() in the setup() function of your Arduino program won’t be a problem.
  • As explained in the delayMicroseconds() part, you need to wait for a few microseconds (not milliseconds!) when communicating with an external component. If you find that using delayMicroseconds() with a small number (for example 10) doesn’t disturb the rest of your program, you can still use it without having to worry too much. But consider it as an exception, not the common rule.

Conclusion – Use Arduino delay() with care

Due to the nature of Arduino programs, you will often need to add delays in your code, so you can choose when to execute some actions, and how often you want to execute them.

The delay() and delayMicroseconds() functions are very simple functions to use and were added to the Arduino language so that complete beginners could start with something simple.

However, as you saw in this tutorial, using delay() can make you stuck really quickly. So, it is best to first understand why you need it, how to use it, and then how to get the same behavior without using the Arduino delay() function directly.

Once you’ve understood the code structure to get rid of delay(), you will be able to improve your Arduino programs a lot, and multitasking will become quite easy.

Did you find this tutorial useful?

Do you want to learn how to program with Arduino?

If yes, this course is for you:

Arduino Programming For Beginners Course

>> Arduino Programming For Beginners <<

LEARN HOW TO PROGRAM ROBOTS

Did you find this tutorial useful?

Do you want to become better at programming robots, with Arduino, Raspberry Pi, or ROS2?

If yes, subscribe to receive exclusive content and special offers!