When delay() should be millis() instead: Part 4

How do we create repetitive timed events with Arduino? Do we use delay(), do we use millis()? We continue to explore when and where we use each function in the following lesson.

Intro

This is part 4 of our millis() function mini-series. Part 1 helps us understand what the millis() function does, and part 2 discusses tight loops and blocking code. Part 3 discusses some issues with the delay function. If you haven’t had a chance yet to look at the previous you should check them out right now (especially part 3 because in part 4 we pick up right where we left off).

Specifically in this lesson, we’re going to look at an issue that arises when we want to have a repetitive timed event but also have an input into our system.

Adding Inputs

Recall our last project from part 3. We we were throwing a party and we had a giant cardboard cut out of you at the front door to welcome people. We had the eyes as LEDs, and the LEDs were blinking back and forth, and then we added a servo to the arm so that it would wave.

Arduino blinking waving project

Since we’ve had issues getting these two repetitive events to overlap, we’ve decided to get rid of the servo and instead add a third eye to our forehead. We’ll have a button on your chest that if somebody presses it, the third eye lights up. Maybe this third eye can hypnotizes anyone who pushes the button.

Let’s jump back into the code. Again, this is the same sketch from the last lesson, part 3, so we highly recommend you check that out if you haven’t yet.

First let’s get rid of the servo code. That leaves us with two LEDs, one at pin seven, and one at pin eight. To summarize the code, we first turn the left LED high, and right LED low, we delay 100 ms, then the right LED high and the left LED low, we delay 100 ms, and we run through this iteration 10 times.

 

/* LED circuits attached here */
const byte LED_eye_L = 7;
const byte LED_eye_R = 8;

void setup() {

  pinMode(LED_eye_L, OUTPUT);
  pinMode(LED_eye_R, OUTPUT);

}

void loop() {

  for (int i = 0; i < 10; i++) {
    digitalWrite(LED_eye_L, HIGH);
    digitalWrite(LED_eye_R, LOW);

    delay(100);

    digitalWrite(LED_eye_L, LOW);
    digitalWrite(LED_eye_R, HIGH);

    delay(100);
  }

}

Now we want to add an additional LED, a third LED, which will be the third eye for our prop. We also want to add a button to the circuit where every time we press it, the third LED comes on.

To do this we first have to designate some pins for the additional LED and button. Let’s add two more constants, LED_3rd_eye (pin 6) and button_pin (pin 5).

In void setup let’s set that LED_3rd_eye pin to an output.

Arduino const byte and output

Down in the loop, after our for loop (the flickering LED event), let’s add an if statement. We want this if statement to turn the LED on if the button is pushed. Otherwise we want the LED to be off.

Arduino code for LED button push

 

Uploading and testing

Let’s see what happens when we upload the code. A schematic is provided below if you want to see the results for yourself.

Arduino schematic for 3 LEDs

The LED eyes are flickering back and forth as advertised, so far so good. Then we press that button and something doesn’t seem quite right.

Sometimes when we press the button, the third LED lights up quickly. Other times we have to wait. There’s quite a substantial delay before the LED actually turns on. Then we find when we let go of the button, sometimes instead of the LED turning off immediately, the LED lags a little bit and stays on for a while.

So what’s going on here? What’s the issue? All we want to do is get an input into our code, to have a LED light up, but it doesn’t seem to be working…

Confused about Arduino problem

If we look at the code that we’ve written, and specifically look at the void loop, let’s ask ourselves “is this a tight loop?” It’s easy to say that this is not a tight loop and the reason we can say that is because we have a for loop right at the beginning. Remember from before we identified delay and for loops that take a significant amount of time to complete as blocking code.

So the for loop is blocking… for how long? To figure this out, let’s look at how many delays we have. We’ve got two delays, each 100 milliseconds each. So that’s 200 milliseconds.

And how many times does the for loop iterate? 10 times. So that’s 10 times 200 ms, which is 2000 milliseconds, or two seconds. So for two seconds, nothing else is happening except what’s in the for loop, which is the flickering LED effect.

Arduino blocking code for loop

What happens when we press the button during that two seconds? Zilch. That’s because we’re not getting down to the part of the code where we digitally read the button_pin to see if that button has actually been pressed.

What if we hold the button? When the for loop ends, the if statement will say “Yep, button_pin is being pressed (i.e. it returns a “HIGH” value)” and it writes the third LED to HIGH, and the third eye comes on.

Then when the code ends, the third LED will remain on, because it was last written HIGH. As we go through the for loop, it remains on. If we let go of the button at this point, the third LED will continue to remain on, until we get all the way through those 2 seconds, finally get to the if statement, and then our code will realize that the button is no longer being pressed, and it will go out.

So the for loop is blocking the input that we want to read.

Arduino blocking code via the for loop

So if we have a program where we want a repetitive timed event, but we also want to have an input into our system, then we may find that using the delay function isn’t going to be the best fit. We should seek an alternative method to programming, perhaps using the millis function.

We might also consider using an interrupt to execute some routine while another for loop is going on, but for the purpose of this series, we’re going to be exploring why the millis function can be a great option.

Summary

let’s recap the last two lessons so we can get really clear on when it makes sense to use a delay function and when it might be better to seek an alternative solution, perhaps using the millis function.

If you have one or more repetitive timed events, and they don’t overlap in time (i.e. they are sequential), then using the delay function is a great solution. The delay function allows us to keep the program flow really simple and it’s just an easy function to use.

Now there’s two circumstances where using the delay function can get a little tricky. The first, which we talked about in the last lesson, is when we have two timed events that overlap. When that happens we run into issues with delay.

The other time is the one that we’ve talked about in this lesson, where we are trying to read and use an input in our system. As we’ve demonstrated here, any blocking code, whether it be a delay function, a for loop, or a combination of the two, can make it difficult to read an input frequently enough to update the input for your program.

Arduino delay vs millis

If you have a repetitive timed event, and you also need to be monitoring inputs, then you might want to consider dumping the delay function for something else, for example, the millis function.

Are you ready to start utilizing the millis function? That’s exactly what we’re going to do in the next lesson. We’ll use the millis function to overcome the issues we just identified in these last two lessons.

We hope you look forward to the next lesson and we’ll see you then! Bye!

Arduino millis vs delay knights

installing Arduino libraries

Installing Arduino Libraries | Beginners Guide

IoT sewage project

Pumping poo! An IoT sewage project

ESP32 webOTA updates

How to update ESP32 firmware using web OTA [Guide + Code]

error message Brackets Thumbnail V1

expected declaration before ‘}’ token [SOLVED]

Compilation SOLVED | 1

Compilation error: expected ‘;’ before [SOLVED]

Learn how to structure your code

5 Comments

  1. Gustavo Morales on April 22, 2019 at 1:13 pm

    I need help, your videos from Millis helped me and I am waitng for the other video I need to make a school project with multitask and your 5th video was my answer but I am still waitin for It and I ha no more time when are you publishing it?

  2. […] When delay() should be millis() instead: Part 4 […]

  3. […] millis() vs delay(): Part 4 […]

  4. Peter Langridge on June 6, 2021 at 12:05 am

    Obviously you got tired of […] When delay() should be millis() instead: and never completed the lesson onto part 5 so the whole series was a waste of time. Not a very good example

Leave a Comment