Motivation (The Problem)
You’re programming an Arduino based microcontroller and you’ve got a few things going on. Say you have an autonomous robot that drives by line follow, it senses things and carries out actions. You want to carry out an action but at the same time you want to be able to line follow and this can be difficult to do because you may need a delay and as you may already know, that delay just sits there wasting clock cycles until the desired delay time is up.
Solution
The best solution is to avoid delays at all costs. Of course there may be some cases where it’s just quick and easy to use delays and it’s not a concern and that’s all well and good because as I always say, engineering is a balancing act. There are always pros and cons to every approach and you need to pick the one that best suits your needs for your specific situation.
Goal
Out goal is going to be to take the simplest example of Arduino programming and remove the delays so that other stuff can be performed in the loop too.
Let’s take a simple example of a blink program. We simply want to blink an LED but eventually we want to do other things as well.
/* Blink Turns on an LED on for one second, then off for one second, repeatedly. */ // Assume the LED is intially off boolean ledState = false; // the setup routine runs once when you press reset: void setup() { // initialize the pin as an output using the constant LED_BUILTIN pinMode(LED_BUILTIN, OUTPUT); } // the loop routine runs over and over again forever: void loop() { toggleLED(); // Toggle the LED delay(1000); // Wait for one second } void toggleLED() { // Change the state of the LED ledState = !ledState; // Write that state to the pin digitalWrite(LED_BUILTIN, ledState); }
Did you notice the delay in lineĀ 18? There is a 1000 millisecond delay and this means that the CPU is doing nothing except cycling over and over checking if 1000 milliseconds has passed. If we want to create a robot that line follows and blinks an LED we’d be out of luck, your line follow program would only be updated once every second at most, by that time your robot might have already crashed into a wall let alone still be on the line for the next reading.
How do we avoid delays?
The easiest way to to track how much time has passed and only perform an action if the appropriate time has passed. Arduino offers two functions for easily checking the system time and that is millis() and micros(). Millis will provide you with the milliseconds that have passed and micros will provide you with the microseconds passed. Personally, I always like to use micros but (as stated previously about engineering) each case is different. We don’t need micros and using micros for one second would be 1000000 which is difficult to read so to make the numbers a little more manageable, let’s stick with millis since we don’t need micros precision.
So, thinking about the Blink loop… here is our pseudocode
- Toggle the LED
- Wait 1 second
In order to keep track of time, we would first need a variable that stores the previous time so let’s assume we have thatĀ in a variable named lastLEDTime when writing our pseudocode for the loop and we’ll cover the details later.
- if millis() >= 1000
- lastLEDTime = millis()
- Toggle the LED
Our new code might look something like this:
/* Blink Turns on an LED on for one second, then off for one second, repeatedly. */ // Assume the LED is intially off boolean ledState = false; // A variable to record the last LED state change unsigned long lastLEDTime = millis(); // the setup routine runs once when you press reset: void setup() { // initialize the pin as an output using the constant LED_BUILTIN pinMode(LED_BUILTIN, OUTPUT); } // the loop routine runs over and over again forever: void loop() { // Only execute if 1000 milliseconds has passed if (millis() - lastLEDTime > 1000) { lastLEDTime = millis(); // We need to make sure we update the last time toggleLED(); // Toggle the LED } // Else... Execute some other code here } void toggleLED() { // Change the state of the LED ledState = !ledState; // Write that state to the pin digitalWrite(LED_BUILTIN, ledState); }
This certainly can be cleaned up a bit to improve efficiency but the basic premise is there. Now we get the same result but code that isn’t waiting unnecessarily for anything. If one second has passed, the LED will toggle as it did before but if one second has not passed, now our program can do something else like make sure you don’t smash into a wall while waiting to toggle the LED.