millis() Tutorial: Arduino Multitasking


After learning how to flash a single LED on your Arduino, you are probably looking for a way to make cool patterns, but feel limited by the use of delay().  If you ask in the forums you get told to look at the “Blink Without Delay” example.  This example introduces the idea of replacing delay() with a state machine.  If you’re confused how to use it, this tutorial is setup to take you from blinking two LEDs with delay, to using an alternate method, right down to how you can use millis().

The millis() function is one of the most powerful functions of the Arduino library. This function returns the number of milliseconds the current sketch has been running since the last reset.  At first you might be thinking, well that’s not every useful!  But consider how you tell time during the day.  Effectively, you look at how many minutes have elapsed since midnight.  That’s the idea behind millis()!

Instead of “waiting a certain amount of time” like you do with delay(), you can use millis() to ask “how much time has passed”?  Let’s start by looking at a couple of ways you can use delay() to flash LEDs.

Example #1: Basic Delay

This is the basic delay example you are probably already familiar with.  It is included in the Arduino IDE’s Examples as “Blink.”

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
}

Reading each line of loop() in sequence, the sketch turns on Pin 13′s LED, waits 1 second (or 1000milliseconds), turns off Pin 13′s LED, and waits 1 second. Then the entire sequence repeats.

The potential issue is that while you are sitting at the delay(), your code can’t be doing anything else.  So let’s look at an example where you aren’t “blocking” for that entire 1000 milliseconds.

Example #2: Basic Delay with for() loops

For our 2nd example, we are only going to delay for 1ms, but do so inside of a for() loop.

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.   A clever trick would be to call other functions inside of that for() loop.  However, your timing will be off because those instructions will add additional delay.

Example #3: for() loops with 2 LEDs

In this example, we’ve added a second LED on Pin 12 (with a current limiting resistor!).  Now let’s see how we could write the code from Example #2 to flash the 2nd LED.

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);
      }
   }
}

Starting with the first for() loop, while Pin 13 is high, Pin 12 will turn on after  500 times through the for() loop.  It would appear that Pin 13 and Pin 12 were flashing in Sequence.  Pin 12 would turn on 1/2 second after Pin 13 turns on.  1/2 second later Pin 13 turns off, followed by Pin 12 another 1/2 second.

This is pretty complicated looking though isn’t it?

If you wanted to add other LEDs or change the sequence, you have to start getting really clever with all of the if-statements.  Not to mention the comment from example #2.  The timing of these LEDs is going to be off, because the if() statement and digitalWrite() function all take time, adding to the “delay()”.

So… let’s look at how millis() gets around this problem.

The millis() Function

Going back to the definition of the millis() function: it counts the number of milliseconds the sketch has been running.

Step back and think about that for a second.  In Example #3, we are trying to flash LEDs based on a certain amount of time.  Every 500ms we want one of the LEDs to do something different.  So what if we write the code to see how much time as passed, instead of, waiting for time to pass?

Example #4: Blink with millis()

This is the same “Blink” example from #1 re-written to make use of millis().  A slightly more complicated design, because you have to include a couple of state variables.  One to know how long to wait, and one to know the state of LED on Pin 13 needs to be.  (Technically you don’t actually need that variable, but you’ll see what that is good design in #5.)

unsigned long interval=1000;  // the time we need to wait
unsigned long waitUntil=interval; // millis() returns an unsigned long.

void setup() {
 pinMode(13, OUTPUT);
 digitalWrite(13, LOW);
}

void loop() {
 // Nothing will change until millis() increments by 1000
 if ((unsigned long)(millis() - waitUntil) >= interval) {
  waitUntil =+ interval; // Increase our wait for another 1000ms.
  digitalWrite(13, !digitalRead(13));  // Ooooh magic toggle code!
 }
}

Let’s look at the code from the very beginning.

unsigned long interval=1000;  // the time we need to wait
unsigned long waitUntil=interval; // millis() returns an unsigned long.

millis() returns an unsigned long. It does not return an int. It does not return a long.

When using variables associated with millis() or micros(), ALWAYS declare them as an unsigned long

The variable interval is the amount of time we are going to wait. Since millis() is used to keep track of time, the variable waitUntil is how many milliseconds must pass before something happens.

setup() is pretty standard, so let’s skip directly to the loop():

void loop() {
  if ((unsigned long)(millis() - waitUntil) >= interval) {

Holy cow does that look complicated! Actually, it really isn’t, so don’t be afraid to just “copy and paste” this one. First, it is an if()-statement. Second the (unsigned long) really isn’t necessary (but I’m not going to sit around for 49 days to make sure). Third “(millis() – waitUntil) >= interval)” is the magic.

What it boils down to, this code will only become true after millis() is at least “interval” larger than waitUntil. In other words, nothing in the if()-statement will execute until millis() gets 1000 milliseconds larger than waitUntil.

 waitUntil =+ interval; // Increase our wait for another 1000ms.
  digitalWrite(13, !digitalRead(13));  // Ooooh magic toggle code!

The first line inside of the if-statement might be obvious. We want the next if-statement to happen 1000 milliseconds from now (the value of “interval”).

The second line simply checks the current condition of Pin 13, and sets it to the opposite value. Read this article on how to toggle OUTPUTs with digitalRead() for more information.

Believe it or not, that’s all it takes to convert the “Blink” example into non-blocking code. Any code outside of that if-statement

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 Examples

Check out this list of millis() examples: http://www.baldengineer.com/tutorials/millis-cookbook/
There are examples to create a Police Light Strobe Effect, Time your Code, Change the ON-time and OFF-time for a LED, and others.

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.

written by

Capacitor Expert by Day, Enginerd at Night. On paper I have a EE which I use to make things blink, beep, and fly. I created and enjoy making the AddOhms Tutorials at addohms.com. You can follow James on Twitter @baldengineer or on Google+.
Related Posts

29 Responses to "millis() Tutorial: Arduino Multitasking"

  1. Chad LaFarge says:

    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

    Reply
    • James Lewis says:

      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.

      Reply
  2. Don DeGregori says:

    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

    Reply
  3. Sudarshan S K says:

    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

    Reply
  4. James says:

    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.

    Reply
  5. Arduino: How do you reset millis() ? - CMiYC Labs, Inc. says:

    [...] 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, [...]

    Reply
  6. Andrew says:

    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.

    Reply
    • James says:

      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?

      Reply
  7. Art Whaley says:

    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.

    Reply
  8. Matt says:

    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

    Reply
  9. Ovidiu says:

    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 &amp; 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);
         
       } 
      
       
    }
       
    }
    
    Reply
    • James says:

      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.

      Reply
  10. Diy'r mak'r says:

    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?

    Reply
  11. Mark says:

    Nice explanations. I’m a NOOB.
    I tried to upload some of the sketches above and kept getting warnings such as “gt was not declared in this scope. (example 5) and “&lt ” in example 4.
    Could this be because I have a newer model Arduino and those were written in 2011?
    Thanks

    Reply
    • James says:

      No, it is a problem with WordPress. Occasionally it converts the greater-than and less-than symbols in the code into their HTML-escaped code. I’ve updated the examples to show the correct symbol.

      Reply
  12. Mark says:

    Thanks a bunch James.
    Its working now!
    This is the coolest thing ever. Don’t ask me why but it is! Hah.
    Now I can get rid of my obviously infantile delay script and possibly add another LED to the chain. I am creating a sign that has a (IR) remote control and I got that to work. I am using case/switch statements so I can chose between a couple blinking patters, but they stop after one pass through the case scenario. In other words, It will blink the leds in sequence I programmed then stop. I suppose I make them continue with some sort of “while” statement or something like that. Eh?
    Thanks again,,
    Mark
    PS. Happy Holidays.

    Reply
    • James says:

      It sounds like you need a state machine for each pattern. If the patterns have the same number of “states” (or you can fit them into the name of states, e.g. repeat states) you can use one state variable. Use your switch() statement to select the correct pattern and then use another switch statement to execute the correct pattern state.

      case pattern1:
        switch(pattern state) {
          case 0;
          break;
          case 1;
          break;
        }
        break;
      case pattern2:
      //...
      
      Reply
  13. marcus says:

    Hi, I need help please. I am trying to remove the delays from the codes below:

    int northRed = 0; //first of all associate a pin number with the Northern red light, in this case 13
    int northAmber = 1; // associate pin 12 with the amber light
    int northGreen = 2; //and finally the green light with pin 11

    int eastRed = 3; //a pin for the east red light, 8
    int eastAmber = 4; //a pin for the east amber light , 9
    int eastGreen = 5; //a pin for the east green light, 10

    int maindelay = 1500; //The timings of the lights are based around this number.

    void setup() {
    pinMode(northRed,OUTPUT); //set the north red pin as an output
    pinMode(northAmber,OUTPUT); //set the north amber pin as an output
    pinMode(northGreen,OUTPUT); //set the north green pin as an output
    pinMode(eastRed,OUTPUT); //set the east red pin as an output
    pinMode(eastAmber,OUTPUT); //set the east amber pin as an output
    pinMode(eastGreen,OUTPUT); //set the east green pin as an output

    digitalWrite(northRed,HIGH); //Switch the red light on so we have something to start with
    digitalWrite(eastRed,HIGH); //switch the east red light on as well
    }

    void loop(){
    northtogreen(); //change the north signal to green
    delay(maindelay*4); //wait for the traffic to pass
    northtored(); //change the north signal back to red
    delay(maindelay/2); //small delay to allow traffic to clear junction
    easttogreen(); //change the east signal to green
    delay(maindelay*4);//wait for the traffic to pass
    easttored(); //change the east signal back to red
    delay(maindelay/2); //small delay to allow the traffic to clear junction

    }
    void northtogreen(){ //sequence of north lights going to green

    digitalWrite(northAmber,HIGH); //Amber on, prepare to go
    delay(maindelay); //Time for traffic to see amber
    digitalWrite(northRed,LOW); //red off, finished with
    digitalWrite(northAmber,LOW); //amber off, finished with
    digitalWrite(northGreen,HIGH); //green on, go

    }
    void northtored(){ //sequence of north lights going to red

    digitalWrite(northAmber,HIGH); //Amber on, prepare to stop
    digitalWrite(northGreen,LOW); //Green off, finished with
    delay(maindelay); //Time for traffic to stop
    digitalWrite(northAmber,LOW); //Amber off
    digitalWrite(northRed,HIGH); //Red on, stop

    }

    void easttogreen(){ //sequence of east lights going to green

    digitalWrite(eastAmber,HIGH); //Amber on, prepare to go
    delay(maindelay); //Time for traffic to see amber
    digitalWrite(eastRed,LOW); //red off, finished with
    digitalWrite(eastAmber,LOW); //amber off, finished with
    digitalWrite(eastGreen,HIGH); //green on, go

    }

    void easttored(){//sequence of east lights going to red

    digitalWrite(eastAmber,HIGH); //Amber on, prepare to stop
    digitalWrite(eastGreen,LOW); //Green off, finished with
    delay(maindelay); //Time for traffic to stop
    digitalWrite(eastAmber,LOW); //Amber off, finished with
    digitalWrite(eastRed,HIGH); //Red on, stop

    }

    can someone help please?

    Reply
  14. Mark says:

    Hello James,

    Something odd is happening with example #4 and my arduino. When it executes, there is a short interval, then the LED lights (but dimly). Then it brightens. Then it goes back to dim. And that’s it. It doesn’t brighten again, nor does it turn off.

    I cut and pasted #4, verified it, and uploaded it correctly, as far as I can see. I’m mystified.

    Any help would be much appreciated.

    Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

©2014 Baldengineer Productions, Inc.