Doing multiple timed things with Arduino: Unleash the millis()!

Have you ever wanted to do MULTIPLE timed events with Arduino? What about multiple CONCURRENT timed events? We’re “upping” our game with this lesson, so buckle in and hold on tight!

The millis story so far

This is part of a mini-series we’ve been publishing that’s all about using the Arduino millis function to create timed events. For example, you may want a servo to move every 3 seconds, or to send a status update to a web server every 3 minutes.

If you haven’t seen the previous lessons in this series, we highly recommend that you go back and check them out either now or after this lesson. Here’s a quick rundown: In part 1 we described the basics of the millis function in general, in part 2 we talked about tight loops and blocking code, in part 3 and part 4 we discussed some issues that arise when using the delay function, and in part 5 we showed how to create “once-off” events and repetitive timed events using the millis function.

In this lesson

  • Map out a program with 2 INDEPENDENT timed events
  • Code from scratch a program using millis() to time 2 repetitive events
  • Drink Kool-Aid and watch a beautiful sunset

Framing the problem with an algorithm

Every good program has an even better algorithm to go along with it, so before we start typing away at the Arduino IDE, first we want to write out our plan of action. This is more or less what we’re trying to accomplish.

We’ve got two sensors, a light-dependent resistor (or LDR) and a temperature sensor. What we want to do is read these values and display them to the Serial Monitor window but we don’t want constant readings.

Instead, we want two separate intervals. We want the light-dependent resistor to read and display every second and the temperature sensor to read and display every five seconds.Arduino LDR and tempSo now we have our two separate events. Let’s write our algorithm. We want Event 1 to read the LDR and then display that value every 1 second. Event 2 will read the temperature sensor and display it’s value every five seconds.

  • Algorithm
    • Event 1: Read/Display LDR Value (every 1 sec)
    • Event 2: Read/Display Temp Value (every 5 sec)

 

Think of the algorithm as our plan of action. Looking at this it appears we could use the Arduino millis () function to set up the timing for these events, and we could use analogRead () to read the LDR and the temperature sensor values.

Then we could use functions from the Serial library, Serial.begin(), Serial.print(), and Serial.println(), to display those values to the Serial Monitor Window on our computer.

When you’re creating a program that has repetitive timed events it doesn’t hurt to lay out the timing of the events on a piece of paper. This is especially true when you have overlapping or concurrent events.

When we lay this out on our timeline, we see that we have two events that overlap every five seconds.

Luckily for us, we are using the millis function instead of the delay function, which would make this MUCH more difficult.

Please keep in mind the focus of this lesson is demonstrating how to code timed events with Arduino, so we won’t go into detail on how to set up your actual circuit for this experiment. We’ve included a schematic below as one possible way you could set it up, or even easier, you can use our Kit-on-a-Shield which makes programs and experiments like this super easy.

Arduino LDR and temp circuit

If you’re wondering how to wire a circuit like this, definitely check out the ProgrammingElectronics.com website for lessons on setting up different circuits. 

OK, assuming you have a functioning circuit (or not, you can follow along without one), let’s jump into the Arduino IDE and start coding this bad boy from scratch.

Code… From SCRATCH!

Before we get too crazy, let’s write ourselves a little “to-do list” in comments. We can work through these one by one. This is just one technique that’s useful to split the algorithm into chunks.

// To Do: Sensor Pin Constants
// To Do: Variables for Timed Events
// To Do: Serial communication
// To Do: Event 1 timing
// To Do: Event 2 timing
// To Do: Eat fish tacos...

The first thing we need to do is set up some pins for where we’ve got our sensors attached.  The sensors are always going to be at these pins, i.e. they will never change, so let’s make these constants. We’ve assigned analog pin A2 for the LDR and analog pin A4 for temp sensor.

//To Do: Sensor Pin Constants

/* Sensors */
const byte LDR = A2; //Light Dependant Resistor
const byte tempSensor = A4; //LM34 temp sensor

// To Do: Variables for Timed Events

void setup(){
  
// To Do: Serial communication

}

void loop(){

// To Do: Event 1 timing
// To Do: Event 2 timing

}

Next let’s setup variables for the timed events. We need to create two constants and two variables. The two constants are the eventTimes. This is the interval at which we want each of these events to occur.

As previously mentioned, we want to read and display the light-dependent resistor value every 1,000 milliseconds. We want to read and display the temperature sensor every 5,000 milliseconds. Those intervals aren’t going to change, so we can make them constants. Let’s make them unsigned long constants as well.Arduino unsigned longsThe variables we’ve created are named previousTime_1 and previousTime_2 and again they’re both unsigned longs. The reason we’re using the unsigned long data type is because the value for millis gets really big. We want to ensure that we’ve got enough storage in that variable data to hold that really big number.

//To Do: Sensor Pin Constants

/* Sensors */
const byte LDR = A2; //Light Dependant Resistor
const byte tempSensor = A4; //LM34 temp sensor

// To Do: Variables for Timed Events

/* Two independant timed evenets */
const unsigned long eventTime_1_LDR = 1000; // interval in ms
const unsigned long eventTime_2_temp = 5000;

unsigned long previousTime_1 = 0;
unsigned long previousTime_2 = 0;

void setup(){
  
// To Do: Serial communication

}

void loop(){

// To Do: Event 1 timing
// To Do: Event 2 timing

}

So, these “pervious time” variables allow us to help track the time as these events occur. If any of this is looking foreign to you, please make sure to check out the previous lessons because a lot of this code has already been explained.

Next, we need to setup Serial communication. We will use the Serial.begin() function from the Serial library to initiate Serial communication. Let’s use the standard 9600 baud rate.

Now let’s get into the loop. We want to update the current time, and we want to do it frequently, hence why we’re using the loop section. We’re going to create a currentTime variable and it’s going to be constantly updated via the millis function.

void loop(){

/* Updates frequently */
unsigned long currentTime = millis();

// To Do: Event 1 timing

// To Do: Event 2 timing

}

So, we’ve created a variable named currentTime and it is equal to the return value of millis. Therefore, currentTime holds a snapshot value of millis, which will always be counting up from when you powered up the Arduino board.

Every time through the loop it’s going to be updating it again and again. Because it’s an unsigned long it can hold a really big number, like we’ve talked about before.

Next, we are going to set up the timing for the first event. The key to this event timing is the first ‘if statement’ condition.

void loop(){

/* Updates frequently */
unsigned long currentTime = millis();

/* This is event 1 stuff */
if( currentTime - previousTime_1 >= eventTime_1_LDR ){
  Serial.print ("LDR: ");
  Serial.println( analogRead(LDR) );

  /* Update the timing for the next event */
  previousTime_1 = currentTime;
  
}

// To Do: Event 2 timing

}

What we’re doing here is comparing the difference between the current time and the previous time with our event interval. Remember our eventTime_1 is constant and will always be 1,000.

Essentially, what we’re doing is looking at the currentTime, which is always updating, and the ‘if statement’ is waiting until the difference between these two is equal to (or greater than) 1,000.

Once this condition becomes true we then run our event code, displaying the current analogRead of the LDR to the serial monitor.

At the bottom of  our code is a very important statement. This is how we are updating our previousTime so that in another 1,000 milliseconds, we can get this code to run again.

If we don’t do this, then previousTime_1 will always be set to 0, and from here forward our if statement will always be true, and the LDR will just continuously be read and displayed.

By setting the previousTime equal to the currentTime we restore the gap between currentTime and previousTime, allowing for a sustainable repetitive event.

If this first line of code is confusing try running some numbers through it. See what happens when millis returns 0, 100, 500, and 1000.

If you’re still confused, definitely check out our last lesson, Arduino Sketch with Millis () instead of Delay (), which explains this explicitly.

Arduino serial monitor screenshot

So let’s upload this and test it out.

It looks like about every second we’re getting a reading from our light-dependent resistor. If we shine a light on it we can see an increase in readings, so it looks like it’s working as advertised. Excellent, we have our first timed event.

Next, we want to add another timed event. How are we going to do that?

Really, we’re just going to repeat the code from Event 1 almost exactly, just changing the names of the variables.

/* This is event 1 stuff */
if( currentTime - previousTime_2 >= eventTime_2_temp ){
  
  Serial.print ("Temp: ");
  Serial.println( analogRead(tempSensor) );

  /* Update the timing for the next event */
  previousTime_2 = currentTime;
  
}

So let’s upload the sketch and test it out. We open the Serial Monitor and we can see that again this is working as advertised. This is pretty sweet!

We’re getting five light-dependent resistor readings and we’re also getting temperature sensor readings and the events are happening at the time we wanted them to. So, we’ve got two independent repetitive events working concurrently.

Arduino IDE and monitor screenshot2

 

As you can see this wasn’t too painful to write out. The toughest part is just wrapping your head around the ‘if statement’ condition.

If you can nail that down and understand the comparison that’s happening, then you’ll officially have this in your tool bag and you can use this in future programs.

To ensure you’ve got it down pat, try coming up with another program with different events, maybe turning LEDs on and off at different intervals.

 

Here is all the code together now…

/* Sensors */
const byte LDR = A2; //Light Dependant Resistor
const byte tempSensor = A4; //LM34 temp sensor

/* Two "independant" timed events */
const long eventTime_1_LDR = 1000; //in ms
const long eventTime_2_temp = 5000; //in ms

/* When did they start the race? */
unsigned long previousTime_1 = 0;
unsigned long previousTime_2 = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {

  /* Updates frequently */
  unsigned long currentTime = millis();

  /* This is my event_1 */
  if ( currentTime - previousTime_1 >= eventTime_1_LDR) {
    Serial.print("LDR: ");
    Serial.println( analogRead(LDR) );
    
    /* Update the timing for the next event*/
    previousTime_1 = currentTime;
  }

    /* This is my event_2 */
  if ( currentTime - previousTime_2 >= eventTime_2_temp) {

    Serial.print("Temp: ");
    Serial.println( analogRead(tempSensor) );

    /* Update the timing for the next event*/
    previousTime_2 = currentTime;
  }

}

Summary

First, we mapped out what we were doing just by writing out our algorithm before we actually get into the code.

Then, we went into the Arduino IDE and wrote a program that created two separate timed events from scratch.

We hope you enjoyed this lesson. Again, there’s a whole series on using this millis function. Definitely check out the other videos in the series. We look forward to seeing you next time!

Arduino drinking cocktails with sunset

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

14 Comments

  1. Susabhan on May 7, 2019 at 10:28 am

    Very nice teaching style, any one can get clear conception.

  2. Mark on May 28, 2019 at 1:05 pm

    Michael:

    Very nice video series on the millis. I would like to plug this in to a few timing projects. Is the source code available? If not, no worries!!

    Thank you for some great work!

    Mark

    • Michael James on May 31, 2019 at 10:51 am

      Hi Mark, I am glad you found the lesson helpful. I have updated the post to include the full sketch (it’s toward the bottom of the post.)

      Best of luck!

  3. joe on July 10, 2019 at 8:47 am

    great!! nice tutorial !! hope others interested in programming (not only arduino) can discover this site…
    its good to tackle in details , good for noobs and to those who wants to share their knowledge…

  4. […] Doing multiple timed things with Arduino: Unleash the millis()! […]

  5. John Van Koeveringe on August 22, 2019 at 5:47 pm

    great work Michael , really have to work my old brain , but after working through these 6 Videos feel I have a good handle on it
    but I am sure I will have lost some of it by tomorrow morning.
    but I will give it a real good work out on the Tinker sim.
    Thanks again for the great work.

    • Michael James on August 22, 2019 at 5:54 pm

      Glad you found it helpful John!

      I feel like this timing stuff takes a lot of playing around with it before it really sets in.

      Best of luck on your efforts!

  6. Naji on June 5, 2020 at 1:53 pm

    Please I have a puestion???
    I want to show two reads of two sensoron seven segment alternately in ten second.
    How can I do this?

  7. Tim on September 30, 2020 at 10:29 pm

    GREAT THANKS!!! Lesson is awesome! But I dont get it where is our carton 3rd eyed alien?
    And how to add button?
    Or 2 buttons for control servo and LED independently ?

  8. Ray C. on May 29, 2021 at 6:52 pm

    Thank You, Thank You, Thank You. My aging brain is getting difficult to teach. This mini-series really brought it home for me. Just what I needed!

  9. Callum on July 23, 2021 at 5:22 am

    Thank you! this helped me so much! However, how for example could i make my sensor take readings for 5 minutes, every 15 minutes? I have a temperature sensor as well as others, but would like to take readings every 15 minutes to produce 5 minutes worth of data!

Leave a Comment