millis() Tutorial: Arduino Multitasking

Shortly after learning how to flash one LED, most new Arduino users move on to flashing two or more LEDs.
The millis() function is one of the most powerful functions of the Arduino platform. This function returns the number of milliseconds the current sketch has been running since the last reset. This may not seem to be highly useful until you consider it as a replacement for the delay() function. Now millis() itself cannot cause a delay, but you can use it create a virtual delay.
Example #1: Basic Delay
Before getting into that, look at the following delay() based example.
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH); // set the LED on
delay(1000); // wait for a second
digitalWrite(13, LOW); // set the LED off
delay(1000); // wait for a second
}
This is the “Blink” example included with the Arduino IDE. Reading each line in sequence, the sketch turns on Pin 13′s LED, waits 1 second, turns off Pin 13′s LED, and waits 1 second. Then the entire sequence repeats. An issue arises if you want to do something during that 1 second the Arduino is doing nothing.
Example #2: Basic Delay with for() loops
Now let’s change the code to the following instead.
void setup() {
pinMode(13, OUTPUT)
}
void loop() {
digitalWrite(13, HIGH); // set the LED on
for (int x=0; x < 1000; x++) { // Wait for 1 second
delay(1);
}
digitalWrite(13, LOW); // set the LED on
for (int x=0; x <; 1000; x++) { // Wait for 1 second
delay(1);
}
}
This new sketch will accomplish the same sequence as Example #1. The difference is that the Arduino is only “delayed” for 1 millisecond at a time. So you could put other commands inside of the for statement. Those command would get executed while leaving the Pin 13 LED on for 1 second.
Example #3: for() loops with 2 LEDs
This example adds a 2nd LED and flashes it in sequence with the first. It is based on the for() loops in Example #2.
void setup() {
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH); // set the LED on
for (int x=0; x < 1000; x++) { // wait for a secoond
delay(1);
if (x==500) {
digitalWrite(12, HIGH);
}
}
digitalWrite(13, LOW); // set the LED off
for (int x=0; x < 1000; x++) { // wait for a secoond
delay(1);
if (x==500) {
digitalWrite(12, LOW);
}
}
}
After 500 times through the for loop, Pin 12 gets turned on. It stays on until 500 times through the second loop. If there was a LED (with current limiting resistor) attached to this pin, it would appear that Pin 13 and Pin 12 were flashing in Sequence.
This example has some significant issues. First, it is still relying on the delay() function, which means the Arduino is doing (almost) nothing else. Second, because of the overhead of the loop and the call to delay, we aren’t really delaying for 1 second. It will actually delay for slightly longer. The millis() function allows the sketch to get around these limitations.
The millis() Function
Going back to the definition of the millis() function: it counts the number of milliseconds the sketch has been running. Hey, that’s exactly what we are doing here! Each “wait for a second” section counts 1000 milliseconds. In fact, while counting we do something at a certain time. Since the Arduino already does this work for us, why not use it instead?
By using the millis() function, we will be setting up a simple state machine in our sketch. This first example code duplicates the same as the very first blink example. When using the millis() function, code will generally need to get a little more complex.
Example #4: Blink with millis()
In this example, a BOOLEAN variable is created to track the state of the LED on Pin 13 and an variable called waitUntil of long type is added to count time.
long waitUntil=0;
boolean LED13state = true;
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
// Each time through loop(), set Pin 13 to the same value as the state variable
digitalWrite(13, LED13state);
// Nothing will change until millis() increments by 1000
if (millis() >= waitUntil) {
LED13state = !(LED13state); // Toggle the LED's state
waitUntil += 1000; // Make sure we wait for another 1000 milliseconds.
}
}
A useful feature of the Arduino digitalWrite() function is that it checks the state of a pin before applying a change. So if you use digitalWrite() to “write” a HIGH and the pin is already HIGH, then nothing happens. This means it is possible to constantly write a HIGH (or LOW) each time loop() iterates.
Example #5: Adding a 2nd LED with millis()
To add a second flashing LED, the code needs to be made just a little bit smarter. In this example a second waitUntil and LEDstate variable have been added.
long sequenceDelay = 500;
long flashDelay = 1000;
boolean LED13state = false; // the LED will turn ON in the first iteration of loop()
boolean LED12state = false; // need to seed the light to be OFF
long waitUntil13 = 0;
long waitUntil12 = sequenceDelay; // the seed will determine time between LEDs
void setup() {
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
}
void loop() {
digitalWrite(13, LED13state); // each iteration of loop() will set the IO pins,
digitalWrite(12, LED12state);
// checking to see if enough time has elapsed
if (millis() >= waitUntil13) {
LED13state = !(LED13state);
waitUntil13 += flashDelay;
// this if-statement will not execute for another 1000 milliseconds
}
// keep in mind, waitUntil12 was already seeded with a value of 500
if (millis() >= waitUntil12) {
LED12state = !(LED12state);
waitUntil12 += flashDelay;
}
}
This code has some small changes from the previous example. There are now variables for the LED state and wait time on Pin12. Additionally two new variables have been added: sequenceDelay and flashDelay. The variable sequenceDelay will determine how long it takes each LED to visually react to each other. While the variable flashDelay is the amount of time each LED stays on.
Visually it will appear the LEDs are chasing each other. Programmatically, each LED is flashing for the amount of time defined in flashDelay. In other words, as shown here each LED will individually flash for 1 second.
Other Example
Police Light Strobe Effect: http://www.cmiyc.com/tutorials/arduino-example-police-lights-with-millis/
Conclusion
At first it seems that using the millis() function will make a sketch more complicated than using delay(). In some ways, this is true. However, the trade off is that the sketch becomes significantly more flexible. Animations become much simpler. Talking to LCDs while reading buttons becomes no problem. Virtually, you can make your Arduino multitask.



Looks like the last example sketch comments do not agree with the values of the variables sequenceDelay (100 is referred to as 1000) and flashDelay (250 is referred to as 500).
Or did I misunderstand?
Thank you for the excellent tutorial.
Chad
Chad, you are correct. The comments did not agree, the listing has been updated. In debugging the original code I started changing the Delays to make sure the lights (and the code) acted as I expected. I forgot to change them back before posting.
It is an interesting exercise to change each on independently, to see how the code reacts differently. It is even more fun when you have a few more LEDs.
Could I send you a sketch using the mills() function? I have been working on the problem for days with no success. It involves the Leah Buechley turn signal sketch, which works fine unmodified. I’m trying to add “chasing light” to the turn signals.I think other delays are interfering with the mills() function.
Thanks, Don
Hi,
I am curious is the time changes between the fist call to millis() to the second call to millis() within the if statements. In this case there will be a random increment in the duration. Isn’t this true.
Thanks,
Sudarshan
No, the increment isn’t random. The same instructions are executed between the two calls every time.
There is an accruacy issue with the code abive. For completeness the code should either: a) assign millis() a variable, check that variable, and increment waitUntilX based on that variable. Or b) adjust flashDelay to account for the time between instructions.
In practice, if timing isn’t absolutely critical then the above method is okay.
[...] libraries. (This also works with micros() too!) Also, if you want to learn more about how to how to use millis to multitask or replace delay(), checkout this tutorial. Share this:Share Tags: arduino, [...]
Hi James. Thanks for your tutorials. I am just starting with Arduino. I looked at the above sketch and added another LED which I got to flash at it’s own rate. But where I am stumped is I want it to flash twice quickly and then another led to flash twice quickly then back to the last LED. What I am going for is a police car type flasher where the headlights wig wag on their own and the blue and reds flash on their own and strobes flash on their own, normally all at different rates. I don’t expect you to write code for me but can you point me in the right direction?? I can’t seem to get a handle on this. But it’s a great learning tool and I look forward to cracking it finally! LOL…
Thank you.
Hi Andrew, I think the key is going to be creating a State variable for each LED and tracking how many times the LED has strobed. Once you’ve strobed enough times, set a wait state for the amount of time the other LED will take to strobe. Does that make sense?
This is a great tutorial! I’ve got one quick suggestion for an addition though –
The way it’s presently coded it always makes sure that at least one second passes between times the code executes, but that means that the actual cycle time is the time the code takes to execute plus one second, and if you’re doing something intensive elsewhere in the program this lost time can add up. If you change
waitUntil = millis() + 1000;
to
waitUntil = waitUntil + 1000;
then the program always tries to execute your code ‘on time.’ One iteration may start late if the program was doing something else, but that doesn’t delay every other time through the code.
Hi Art,
You are absolutely correct. I’ve updated examples 4 and 5 to reflect this change.
Cheers,
James
I was wondering if you could help me? I would like to use use millis() with two different delays say light is on for 2 sec and off for 1 sec. On pin 13. Is this possible with millis? I’m sure it is im just not sure how to do it. Could you do an example like that? I would be very grateful.
Thanks,
Matt
I’m trying to get the code for 2 butons controlling 1 led(when they are pressed simultaneously the led turns on) end a buton, when is presed 2 times , turns a led on for a couple of seconds.My problem is that i ned them to work simultaneos, de delay() function esent ideal, the mili() function , i can not get my head around it.
Here is my code:
const int Pin5 = 5; const int Pin6 = 6; const int Pin3 = 3; const int ledPin = 9; const int ledPin1 = 10; const int ledPin2 = 11; int buttonPushCounter = 0; // counter for the number of button presses int buttonState = 0; // current state of the button int lastButtonState = 0; // previous state of the button void setup() { pinMode(ledPin, OUTPUT); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); pinMode(Pin5, INPUT); pinMode(Pin6, INPUT); pinMode(Pin3, INPUT); } void loop() { int Value = digitalRead(Pin5); int Value1 = digitalRead(Pin6); int Value2 = digitalRead(Pin3); buttonState = digitalRead(Pin3); if (Value == HIGH & Value1 == HIGH) { digitalWrite(ledPin, HIGH); delay(2000); } else { digitalWrite(ledPin,LOW); } if (Value2 != lastButtonState) { if (buttonState == HIGH) { buttonPushCounter++; } lastButtonState = buttonState; if (buttonPushCounter % 2 == 0) { digitalWrite(ledPin1, HIGH); delay(3000); digitalWrite(ledPin1, LOW); } } }You are really trying to combine two different things. Using the Button library might help with managing when or how the buttons are pressed. You should avoid variable names like “Value” and certainly don’t want to use conventions like “Value” and “Value1″. That makes it difficult to follow code later on because they don’t describe what you are trying to store. Variables like “LeftButtonValue” or “RedLED” are far more descriptive.
Using millis() is all about creating a state machine. You need to constantly be checking what is happening with the buttons and reacting when they change. Then you need to have some state variables for your LEDs and constantly calling digitalWrite() using those state variables.
didn’t have time to try but thanks for the advice.
I’m trying to use a plurality of leds as a six string strobe tuner for guitar… Aparently this works with micros too, and I’m sure I’ll require the micro resolution… Rounding to the nearest 4th micro has me kinda spooked. Is there no way around this?