Non-Blocking Delays

Introduction

This page discusses the concept of non-blocking delays as a way to allow multiple periodic events to occur while allowing other functions to run during the time between calls. We implement this idea in a library.

Non-Blocking Delays

The main reason to use non-blocking delays is because delay() blocks code execution until it returns, which means nothing else can run while delay() is delaying. Non-blocking delays only run after enough time has passed, and then they reset. So from the perspective of the code inside the non-blocking delay, it has been delayed, but the code outside the non-blocking delay has not.

There is a trade off to using non-blocking delays, and that is implementing non-blocking delays is much more complex than just using delay(). This is because for every block of code that needs its own non-blocking delay, it requires a previous time variable, a current time variable, and an if statement. The example code demonstrates these differences.

Using delay():

					void loop()
{
if(button_state == HIGH){
digitalWrite(led_pin,HIGH);
}else{
digitalWrite(led_pin,LOW);
}
delay(1000);
}


Using non-blocking delays:

					void loop()
{
static int last_time = millis();
int current_time = millis();

// This is the "delay" block, runs until 1
// second elapsed since the previous time it ran.
// The code in this section is equivalent to the
// delay() code above.
if(current_time - last_time >= 1000){
if(button_state == HIGH){
digitalWrite(led1_pin, HIGH);
}else{
digitalWrite(led1_pin, LOW);
}
last_time = current_time;
}

if(button_state == HIGH){
digitalWrite(led2_pin, HIGH);
}else{
digitalWrite(led2_pin, LOW);
}
}


We added some additional code to the non-blocking delay example to show that only the code in the “delay” block acts as if delay() was used, but any code outside of that block is not delayed. By this point you should be able to take these code snippets, add the things we left out, and build a simple circuit to experience the similarities and differences between them.

So the example code using delay() changed the LED once a second, depending on whether the button was pressed or released at that time. With our non-blocking delay example we have two LEDs, one that acts like the example with delay() and the other that changes “instantly” with the button instead of once a second.

However, as you can see from the example, there is more complexity in how the code is structured, which can make it more difficult to debug as there are more variables and conditionals which could subtly change the intended behavior if a small error was made. For example, forgetting to set the last (or previous) time with the current. This would remove the delay that was intended, but the code would look correct and compile just fine.