Arduino: How do you reset millis() ?

The quick answer to “How do you reset millis()” is:  You Don’t!  And here’s why:  if you did, it would potentially break most libraries and functions that rely on it.  Generally the reason people want to reset it, is that they are concerned about rollover.  Instead of focusing on resetting millis(), here is how to use it correctly.

Need to brush up on how millis() works?  I’ve got a tutorial on how to effectively multi-task with millis() and another line-by-line tutorial on blink Without delay

Avoiding rollover and checking how much time as passed is done in a single line:

if ((unsigned long)(millis() - previousMillis) >= interval)

That single line of code is all that is really needed, to avoid rollover!  Pretty simple, huh?  So let’s go into more detail about how this works and what each of those variables do.

Update Dec 8 2013. I’ve updated the examples below to a more robust implementation.

What is millis() rollover?

Every millisecond a timer interrupt fires to increment a global variable.  When millis() is called, the value of that variable is returned.  The data type used is an unsigned long which is 4-bytes or 32-bits.  This means the maximum value it can hold is 4,294,967,295.  Convert that many milliseconds into days and you can see the rollover will occur in 49 days.

frac{4,294,967,295ms}{1000} = frac{4,294,967secs}{60} = frac{71852mins}{60} = frac{1193}{24} = 49dayshuge

In HEX the maximum value is 0xFFFFFFFF.  Add one more and it “rolls over” to zero.  Hence the name “millis() rollover.”

Will my Arduino Lock-Up?

Let’s be very clear:  when millis() rolls-over, your Arduino will not lock-up.  In fact the Arduino’s ATmega processors very rarely lock-up.    So no, when the millis() value rolls over to 0, your Arduino won’t lock up and your project won’t explode.  Ok?  [Editor’s Note:  If your project is designed to explode when millis() equals 0, then in that case, it would explode.]

How to reset millis() to avoid rollover

When it comes down to it, there’s no reason to reset millis().  In fact, it turns out this seemingly trivial task requires changing quite a few things buried deep inside of the Arduino core functions.  The worst part is, if you make those changes you may very well break libraries and other projects!  Instead, all that you need to do is simple subtraction:

unsigned long currentMillis = millis();
if ((unsigned long)(currentMillis - previousMillis) >= interval) {

That if-statement is a simple way to detect when time a predetermined amount of time has past and it handles rollover! With such a simple statement, why bother with trying to reset millis()?

Here’s an extended example of how to handle millis rollover [pastebin]:

// Interval is how long we wait
// add const if this should never change
int interval=1000;
// Tracks the time since last event fired
unsigned long previousMillis=0;

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

void loop() {
   // Get snapshot of time
   unsigned long currentMillis = millis();

   // How much time has passed, accounting for rollover with subtraction!
   if ((unsigned long)(currentMillis - previousMillis) >= interval) {
      // It's time to do something!
      digitalWrite(13, !digitalRead(13)); // Toggle the LED on Pin 13

      // Use the snapshot to set track time until next event
      previousMillis = currentMillis;
   }
}

Why does this work?

Long story short, it is what happens when do math with unsigned variables. Don’t trust that this will work? Okay, consider the following example using an unsigned char data type which is a single byte or 8 bits.

The maximum the data type can hold in HEX is 0xFF which means in DECimal it is 255.  Let’s select an arbitrary value of decimal 47.  This code will use “counter” to simulate millis(). Every 255 counts it will rollover to zero. Every time we count up 47 steps, we will print the message “Trigger Event!”.

//Pretend "counter" is "millis"
unsigned char counter = 0;

unsigned char previousCounter = 0;
unsigned int interval=47;

void setup() {
   Serial.begin(9600);
   delay(5000);  // give enough time to open the serial monitor after uploading
   Serial.println("Starting...");
}
void loop() {
   for (int x=0; x<1000; x++) { // run through uchar a few times
// ******* simulate millis()
       counter++; // simulate millis()
       Serial.println(counter);
// ******
       unsigned char currentCounter = counter;
       if ((unsigned char)(currentCounter - previousCounter) >= interval) { // check for rollover
         Serial.println("Trigger Event!");
         previousCounter = currentCounter;
      }
   }
Serial.println("...stopping!");
while(1); // Stop the Serial monitor output
}

When you look at the serial output of that code, make sure you open the serial monitor before it opens itself.  You want to start look at the top of the output right after “Starting”.  The first time through 255, you’ll see a trigger after 47, as expected.  The second time through 255 it’ll be after 26.  The value of 47 was picked in this example because it won’t always be in the same place in the count.  However, it will always be 47 steps after the previous “Trigger.”  That’s how we know this code is accounting for rollover.

Conclusion

Instead of trying to reset millis(), just use subtraction to handle the millis() time detection and rollover. It is a simple method that won’t involve modifying any code in the Arduino 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.

Long comments, URLs, and code tend to get flagged for spam moderation. No need to resubmit.

ALL comments submitted with fake or throw-away services are deleted, regardless of content.

Don't be a dweeb.

Leave a comment

69 thoughts on “Arduino: How do you reset millis() ?

  1. I was so shocked at first, I didn’t believe it even though the arduino code really worked.
    So I did another checking by hand:

    ex1)
    prev = 240, cur = 255
    cur – prev = 11111111 – 11110000

    Computers do subtraction by adding a 2’s complement number.
    -11110000 in 2’s complement = 00010000

    cur – prev = 11111111 + 00010000

     11111111
    +00010000
    ---------
    100001111

    Since it’s an 8-bit number, the last 1 is omitted.

    = 00001111 = 15, as expected.

    ex2)
    prev = 240, cur = 0 (overflowed)
    cur – prev = 00000000 – 11110000

    -11110000 in 2’s complement = 00010000

    cur – prev = 00000000 + 00010000

     00000000
    +00010000
    ---------
     00010000

    = 00010000 = 16

    unsigned 32-bit subtraction also works with the same principle.

    I learned this subtraction recently, but had no idea why this would work.. Still so amazing and elegant solution. Thanks for sharing!

  2. Hi sir!

    I have a Millis-based algorithm. before when the millis max time up, i was think to use software reset. i simuleted your example about millis and really very succesful. i want to ask you;

    when the millis max time up,
    which case activate firstly? is it according to the ranking written in the algorithm? this point is very important for the correct operation of the algorithm.

    should I put a delay between case 1 to case 2 for the correct operation of the equipment because this outputs control the relays and transistors. i doubt that

    when the millis max time up, it must be passive the relay of case2();

    unsigned long eventTime_1 = 7.200.000 // 2 hours
    unsigned long eventTime_2 = 3.600.000 // 1 hours
    etc..

    unsigned long CurrentTime = millis();

    //*******************************************************

    if((unsigned long)CurrentTime – previousTime_1 >=eventTime_1)
    {

    case1(); // relay active

    previousTime_1=CurrentTime;

    }

    if((unsigned long)CurrentTime – previousTime_3 >=eventTime_2)
    {

    case2(); relay passive

    previousTime_3= CurrentTime;

    }

    if((unsigned long)CurrentTime – previousTime_33 >=eventTime_3)
    {

    case3();

    previousTime_33= CurrentTime;
    }

  3. > if ((unsigned long)(currentMillis – previousMillis) >= interval)

    I’m not quite sure if I understand your explanation correctly on why this works exactly. Is the typecast part of the magic? or is it just that substraction does a rollover if the result is less than 0?

    currentMillis and previousMillis are both unsigned long, so the result will be an unsigned long as well – why do you cast it into an unsigned long then?

    • Hi Thomas,

      You’re not wrong, if you subtract two unsigned long variables you will get an unsigned long. What was done here is defensive programming. Even though you’re expecting both currentMillis and previousMillis to be unsigned long variables, you’re also absolutely relying on them being so for this to work. By performing the cast, you’re guaranteeing your code would work, even if someone came along and changed one of the variables to something other than unsigned long. It’s better to be explicit and it’s clearer to the reader what your intentions were.

  4. Hello, I have to do code.
    I have a flow meter and an arduino.
    Regardless of the amount of water that passes through the sensor, I need to know if the sensor has been running for 30 seconds without interruption.
    I’m using millis () and interrupts to count the pulses.
    I would like you to give me some idea of ​​how to do it.
    Thank you

  5. Thank you for your quick response sir.

    However, I think I might be going about my problem the wrong way.

    Here is what I actually intend doing .

    I have two switches A and B , their function is to activate a timer when both switches are held down.

    If either A is held down and B is released or vice versa reset the timer. Also if both are released rest the timer.

    In other words , the timer only starts counting when both A and B are pressed and held in place.

    The timer stops and resets if either A or B is released or both are released.

    Now if the timer gets to a certain number, say 20 seconds, turn on an LED.

    The LED if on, will turn off when either A,B or both switches are released.

    This whole sequence is supposed to be a multitask action, I do not want to stop the other codes from running while performing this task.

    • This is beyond the level of help I can provide here. I suggest posting your question in a forum like “Programming Questions” on the arduino.cc forum. If you include a link here, I’ll try to checkout the post when I can.

    • unsigned long last = 0;
      
      void setup(){
          pinMmode(13,OUTPUT);
          pinMmode(10,INPUT);
          pinMmode(11,INPUT);
      }
      
      void loop(){
         unsigned long current = millis();
       
         if(digitalRead(10) && digitalRead(11)){
            if((current - last) >= 20000){
                digitalWrite(13,0);
            }
         } else{
             digitalWrite(13,0);
             last = current;
         }
      }
      
  6. Hello sir good day.

    Please I would like a way to determine how long a button is pressed (held down) and peform an action once ( say turn on led) while the Button is still held down.

    Let’s say I want an led to turn on and off (blink) only ones while the Button is held down for 10 seconds.

    If the Button is released, go back to the beginning and do the same thing when the Button is held down again.

    All of this action using millis ().

    I have gone through several of your tutorials about millis (), but I haven’t been able to do the above task.

    • Two things might go wrong:

      What if the loop isn’t called at least once every millisecond or if it’s called more than once per millisecond? Then you might miss a beat if it took more than a millisecond to process the previous loop and so your condition isn’t satisfied on the next loop. Or if the loop gets called 100 times per millisecond then you perform your action associated with the condition 100 times with no interval at all.

      Also, about once every 50 days, millis() rolls over. When millis() reaches 4,294,967,294 it goes back to zero. Let’s say your interval is 10, then you’ll satisfy the condition you’re suggesting at 4,294,967,290 and then, when millis() reaches the number above, only 4 milliseconds later, it resets to zero and satisfies the condition again. That interval wasn’t the 10 milliseconds you wanted so you’ll always have a little odd behaviour once every 50 days. This might not matter, but that’s why you might go out of your way to do it properly.

  7. Thank you Stuart! I owe you one! That was really good education for me. 🙂 Did need to reply here because I cant on last answer.

    Cheers
    Hannes

  8. Hi guys,

    first thank you for the great explanation. Its still kind of easy to understand even for non english and avid programmer. 🙂

    I’m unsure how this will end up. Because I take the previousMillis as startMillis to get an “emergency” timeout inside my code if no digitalRead is coming. But if I get a rollover in this code it would take a long time to get back to the Interval. Am I right?

    http://pastebin.com/EZt7SLm3

    Thanks
    Hannes

    • Hi Hannes. No a rollover won’t make you wait a long time. A very small number (after rollover) minus a very large number (millis before rollover) is another small number because the subtraction also rolls over in the same way. So the time gap will be the same regardless.

      Also, your code uses lots of integers as flags. You could use booleans for those.

      • Hi Stuart,

        thank you for your great answer.

        1.) Sorry still do not get it. Let’s say my “startMillis” = 50, “interval” = 30 the loop does a rollover at 55, then I have next loop a 0-50 = -50 ?

        2.) They are “volatile boolean f_done=0” is there a difference when I write false / true or 0 and 1 ?

        Cheers
        Hannes

        • Hi Hannes, not to worry it’s a hard concept at first, but you’ve got the right idea. Let’s say the variable rolls over or “overflows” as it’s called at 55 and your last interval ended at 50 like you said.

          So you’re doing the subtraction and at 54 you obviously get 4 (54 – 50) as you expected. When the variable rolls over to 5, say, then the subtraction becomes 5 – 50 which is -45 but you can’t have -45 and what you’ve done is underflowed because less than 0 wraps back up to 55 so -45 is actually represented as 10 (-45 + 55) which is the correct number of millis. The interval will be met when the variable reaches 25 and you get 25 – 50 = -25 which is actually 30 (-25 + 55).

          Don’t think of the numbers as very small and very big. The numbers wrap around every time they leave the range 0 – 55 so 0 and 55 are only 1 apart from each other.

          If you’re already using Boolean that’s great. You should definitely use true and false instead of numbers though. They will be clearer to the reader. When you want to check a Boolean in an if statement, you don’t need to compare with numbers so checking for true is if(variableName) {} and checking for false is if(!variableName) {} where the ! means “not”. This will make your code much more readable.

          • Hi Stuart, thats an amazing logic.

            Is this a normal behavior of the long type or just a special case in this one, when an overflow happens?

            For the true and false you probably right, I will change it around.

            Btw. this code is for a beehive scale project I am building.
            https://github.com/HannesOberreiter/bScale

            Cheers
            Hannes

          • Hi Hannes,

            This is the normal behaviour for all numeric types. int, long, float, double they all do it. Each type has a finite amount of memory they can take up so there’s a maximum value they can hold. Remember all numbers are stored in memory as 0s and 1s and the largest number is all 1s (not actually true but it’s easier to explain) then when you add 1 to that it’s like when a car with mechanical dials for the mileage goes above the highest number and it rolls over to all 0s which is the smallest number.

            The main difference between all the variable types is how many 0s and 1s they have available. Smaller types overflow at a smaller number. Unsigned variable types don’t store negative numbers so overflow to 0. Signed variable types overflow to minus whatever their maximum value is, plus 1. They both have the same range, so an unsigned variable can go to twice the positive number that a signed variable can.