delay() Arduino Function: Tight Loops and Blocking Code
Have you ever been making an Arudino project and you want something to occur at a timed interval? Maybe every 3 seconds you want a servo to move, or maybe every 1 minute you want to send a status update to a web server.
How do you do that? Is there a function that is simple and straightforward that helps us with this? Yes there is! We will discuss the delay function, as well as the millis() function, in the video below:
This is part 2 of our millis() function mini-series. Part 1 helps us understand what the millis() function does, part 2 discusses tight loops and blocking code, and part 3 discusses when the millis() function outshines the delay() function.
Topics in this lesson
- Tight loops
- Blocking code
- Ancient recipe for inspiring music
Millis() versus Delay()
So you want something to occur at a set interval and you’re looking for a solution. If your answer is using the delay function, well, you’re kind of right. But there is another way.
The cooler, snazzier option is the Arduino millis() function. And the more familiar you are with using the millis function, to help you time events in your Arduino code, the easier it will be to incorporate other parts into your program later on.
Once you start using the millis() function, you’ll be happier, more cheerful, and gosh darn it, people will like you.
First let’s discuss the concept of a tight loop. And when we say a tight loop, what does that mean?
Let’s take a look at an Arduino sketch for a demonstration of a tight loop. Starting with the most basic sketch, we’ve only got two functions: void setup, and void loop. And as you may know, void setup only runs once, and then it hands the show over to void loop.
Void loop then goes through every line of code that might be inside the loop (inside these curly brackets).
It executes the first line, then it executes the second, and then the third, and so on and so forth, until it gets to the bottom. And then it goes back to the top.
How fast does it execute the loop? It depends on which Arduino board you’re using, but an Arduino Uno has a clock speed of 16 megahertz. So that means that 16 million instructions are happening every second on the Arduino!
Each line of code isn’t necessarily one instruction. In fact, it’s most likely that it’s multiple instructions. But still, that’s relatively fast (your computer processor is likely running at Gigahertz speeds… that’s billions).
So would you consider that empty sketch a tight loop? Definitely, that’s as fast and as tight as you can make a loop. Since there’s nothing inside of the loop to execute, the time it takes to go thru the sketch is practically zilch. Said another way, the interval from the start of the loop to the finish is short (therefore it is fast, or “tight”).
Let’s add some lines of code. We will start serial communication, and then print something to the serial monitor window.
Is this a tight loop? That is, from the start of the loop to the end of the loop, does that take a lot of time? It takes very little time, so that’s a fast, tight loop.
It’s worth noting, however, that this loop is not as tight as the previous example. In the previous example, we had no code. So it was just racing through the loop. Now that we have a function here, serial print, it will take (a tiny) bit of time to print “Ice Ice Baby” to serial monitor.
But this is still a pretty quick loop. So let’s add a little bit more code. We’ll have the program check to see if a button is pressed, and if it is, we’ll have something new sent to the serial monitor
So we have declared a button and used an if statement to check and see if the button has been pressed. Is the voltage at pin five high? If so, then we print something else to the serial monitor window.
Is this a tight loop? So, from the start of the loop, to the end of the loop, is that pretty quick?
Yes, it’s still quite quick. This is a pretty tight loop. We’ve got four lines of code. We’re printing to the serial monitor, and then we’re doing a quick check to see if a button is being pressed. And if it is, we print something out. Still tight, still fast.
Next let’s add a delay to this program using the Arduino delay() function. You can see below we’ve added a thousand millisecond (1 second) delay to the loop.
Is this still a tight loop? Is the time, from the start of the loop to the end of the loop, a lot of time? No, this is definitely not a tight loop. The code starts fast, we do the serial print, but then we get halted right there at the delay function.
The whole program comes to a standstill while we wait for this delay code to finish.
When the Arduino gets to this line of code, it’s kind of like going to the grocery store. You get into the 12 items or less line, but then the guy in front of you pulls out his checkbook, and starts writing a check. You know you’re gonna be there for a minute. It’s the same deal here.
So this is not a tight loop. The time from the start of the loop to the end of the loop is pretty significant. Especially compared to the last couple programs. The order of magnitude of time is huge.
Now what we’ve tried to demonstrate here is that this whole idea about tight loops is relative. It all depends on your application. If you need to check the status of a sensor every 10 millionth of a second, then a program that has three lines of code may not be tight enough, it just depends.
Another point to make is that not all lines of code take the same amount of time to execute. If you’re calling a function that does a bunch of stuff, like serial print for example, then that one line of code may take a whole lot longer than 10 other lines of code.
So the tightness of a loop is a relative idea.
When a program stops at some point, and takes some time to execute some code, that code can be called blocking code. This is a general term.
In our program we’ve got the delay function acting as blocking code. None of the code after delay can run until the delay is over, so it’s getting blocked.
Blocking code is not, however, just when we use the delay() function.
Let’s take our program and get rid of delay, but we’ll add a for loop. Our for loop will print out numbers and text to the serial port.
So how long does this loop run for? It’s going to run for a while because it has to go through 100 iterations before it stops.
And what about the code after this for loop? Is it able to run? No, it has to wait because it’s being blocked by the for loop.
This for loop is nested inside the main loop. Is the for loop a “tight” loop? Before you answer, let’s re-emphasize how you should think about it: does it take a lot of time to go through this for loop, from the top to the bottom?
Well, not really. It’s only got two lines of code. So this is a pretty tight loop.
But it’s a tight loop that has to go through a lot of iterations before the program can get to the code below it. So, even a tight loop, if forced to run through several iterations, can block our code.
More on the delay() function
Let’s talk a little bit more about this delay function. What have we established so far?
First we’ve said the delay function decreases the tightness of a loop. If you have a tight loop and you add the delay function, it’s going to take more time and make it less tight. That is, the amount of time it takes to get from the start of the loop to the bottom, will increase with the delay function.
We also know that the delay function blocks code. This goes hand in hand with what we just said. When the delay function is running, it is blocking other code from running while it’s delaying.
You might think this delay function is a total slacker! It’s never going to amount to anything in our projects. But for a lot of simple programs that we write, the delay function works fantastic. It’s simple to use, it’s really easy to spell, and it does just what it says.So we shouldn’t necessarily ostracize the delay() function from our programming toolbox. We should just recognize that it’s a simple programming function that can work in a lot of instances.
There is a time when you start to run into issues, however. That has to do with the blocking effect that the delay function has in our program.
In the next lesson in the series, part 3, we’re going to identify where this really becomes an issue. You’ll learn when it makes sense to use the delay() function in a program, and when it’s time to switch to using the millis() function.
First, we talked about the tightness of a loop. We said that the tightness of a loop is relative. It depends on what your application is.
Second, we talked about blocking code, or code that blocks. Essentially, it’s a generic term we can give to code that’s going to take some time to execute, and it’s going to stop other parts of our program from running while it executes.
We hope enjoyed this lesson! In the next part of this series, we’re going to continue our journey learning how to use the millis function to create timed, repetitive events in our Arduino code. We’ll see you next time!