Arduino: How do you reset millis() ?

stopwatch_000018042439
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.

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} = 49days

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.

Be positive with comments, or they may fit themselves in the bit bucket.

Leave a Reply

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

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

  1. As far as I understand your code, it will not work on a rollover. Let’s look at the case, where `counter` is close to rolling over (`counter = 250`) and `waitUntil` equals `counter`. `waitUntil` will be set to `waitUntil + interval == 250 + 47`. Because of rolling over, `waitUntil` will become `250 + 47- 256 = 41`. When the for loop checks the if condition the next time, `counter` will be 251 and the condition will give true (251 – 41 == 210 and 210 >= 0), although the wanted delay of 47 wasn’t followed.

    Please correct me if I’m wrong.

    • You’re right. The code as posted is incorrect. It was from an earlier revision of the post when I mixed up “correct” versus “incorrect” way. Recent reload my wp database and I forgot to update it.

      Should be more along the lines of ” if ((counter – start_time) >= interval) { // check for rollover”

  2. The code above works fine but has the disadvantage that if some processing takes too long time, the “work” next time is executed more than one time without delay.

    Example:
    interval=1000, waitUntil=3000, millis()=3000.
    now some processing takes 5 seconds
    interval=1000, waitUntil=4000, millis()=8000.
    now the condition returns true until “waitUntil” reaches 8000.

    So it’s better to add interval to waitUntil until the condition is false.

    • Good point. While the delay will be (most likely) deterministic, it will be longer than expected. I’ll look at modifying the examples.

  3. It seems to me that this code simply transfers the roll-over problem from the millis() function to the waitUntil variable. The waitUntil variable holds 4294967295 values which means that for an interval of 1000 it will run out after 49.7 days. What am I missing?

    • First roll-over isn’t a “problem”. It is just how millis() works.

      In order to detect roll-over, it doesn’t matter what the actual maximum value either waitUntil can hold or millis() can return. The key is that they both need have the same limit. By setting the interval, even if roll-over occurs, you can detect if “interval” number of ticks have occurred.

      The only limitation becomes you can’t detect more than the amount you can hold in waitUntil or millis() can return, which is 49 days. As long as your “interval” is less than 49 days, you’ll never know how many times roll-over have occurred (and you probably won’t care.)

  4. I want to nitpick one thing you said:

    And here’s why: if you did, it would potentially break most libraries and functions that rely on it.

    But don’t the same problems happen when millis naturally rolls over? So while it may require some planning if you did it manually, it is still the same issue.

    I personally would love if I could reset millis. I have a project I am designing where the attiny I am using syncs back to its host at least once a week. When it does, it gets a refreshed seconds since the epoch to calculate the current time. If I could reset the millis count when I do this (without resetting the CPU) then would simplify my code greatly. It isn’t like it is horrible right now, but it would clean things up nicely.

    Now I do not recommend anyone do this, but it would be as simple as resetting three variables found in wiring.c.

    Instead it is probably better to set up the watchdog timer to go off every second and increment a counter. So even if you are in low power sleep you can maintain time without a lot of current draw.

    • Sorry, but I don’t agree with your points. Libraries that use millis() will use the practice of store the current time and compare to a future time.

      Resetting millis() completely breaks that.

      There is absolutely no reason to reset millis. Comparing a timer to a previous value is the right way to handle “roll over.”

      • Please name these libraries.

        I just went on a tour of several and the ones I found that stored millis did so in local variable. None of those functions would be suitable for use in a interrupt handler so the odds of them getting confused by an external change to millis is essentially nonexistent.

        But you know what else I found, none of them were doing anything to handle milli rollover. So why is it ok if the get confused because of that, and not if it is though an external change?

        And even if there are some libraries that have a problem with it, then they should simply document it and leave me the developer to make the choice of how to handle that. It isn’t like we don’t have to deal with that right now with things like interrupt handles, timers, etc.

        To me — without hard evidence of heavily used libraries that would fail in the way you mention — your argument feels like an inversion of the Improbability Factor antipattern.

          • Fair enough, though I wouldn’t mind if your clarified the opening paragraph to this post as the situation isn’t as dire as it implies. Perhaps “if you did, it would potentially break those libraries and functions that save millis() a global variable”

            Since our last exchange I have scanned more libraries (around 30 in total), and I have yet to find one that would break with a reset. What I have found is that NONE of them detect and handle rollover at all (where delta time is calculated). And that is the real problem. All of of those libraries will fail in one way or another if rollover happens.

      • So just to add some actual data, I did a detailed analysis of the Arduino/Wiring codebase, along with the standard libraries that come shipped with the Arduino IDE.

        There is only one class that stores millis in a global variable to later comparison, and that is Stream. Carefully reading the code however shows that the four functions that take advantage of that (findUntil(), parseInt(), parseFloat(), and readBytes()) would not be vulnerable to resetting millis between Stream operations.

        The only way that it could cause problems is if millis were reset in a interrupt handler, which I don’t think anyone is suggesting.

        I am not trying to pile on, I just think we were both talking hypothetically and some hard data would be useful.

  5. Thanks for this technique. Quick question (and a warning that my programming skills are very poor, as will become painfully obvious): usng your example code can I declare currentMillis at the top, rather than in the loop? I’d need to use the value of currentMillis in other functions and of course it’ll be out of scope if I declare it in the loop as per your example. So for example putting this at the top:

    unsigned long previousMillis=0;
    unsigned long currentMillis;

    and in the main loop replacing:
    unsigned long currentMillis = millis();
    with:
    currentMillis = millis();

    compiles and runs fine but I wasn’t sure if I’d broken some core concept of this approach in doing so. If so I guess I can always assign a global variable to the value of currentMillis, but I was intrigued to know if my ‘solution’ does actually work. All advice very welcome.

    • Your “solution” works because in this example code it doesn’t matter if currentMillis is global or not.

      currentMillis() needs to be in-scope wherever you need to use it. The idea is to capture the current millis() count and then do something with it immediately.

      I’m not sure I understand why another part of your program would need to know the “current” value of millis… since it is very unlikely to be “current” anymore.

      • OK, thanks. So if I define my question rather more tightly, ie “Does the way that variables are declared affect your technique for avoiding rollover?” the answer is “no, it doesn’t matter provided the relevant variables are in-scope”, right? I understand that there may be other consequences depending on the particular application, but that’s in the hands of the user.

        To answer your question, I write my loops the other way round to your example, eg

        if ((unsigned long)(currentMillis – previousMillis) < interval) {
        call function to do stuff before the interval time is over
        call another function to do more stuff before the interval time is over
        }

        The functions may use currentMillis for a range of purposes, eg to calculate the %age of the interval which has elapsed, etc. For my purposes the fact that the value of currentMillis is frozen for the duration of the loop iteration isn't an issue.

        • Ahhh okay, got it now.

          Your first statement is correct: How you declare the variables “doesn’t matter” from a functional point of view.

          Now that I see what you’re doing, I take back my previous “why?” statement. Makes sense, I wasn’t being open minded enough there!

          • No problem, would be boring if we all approached things the same way! I use the (currentmillis – previousMillis) approach all the time. Up to now I’d relied on trapping the rollover with something like ‘if (currentMillis < previousMillis) reset everything in sight and hide under the table with your fingers in your ears' but your solution is much more elegant, so many thanks for sharing it. I'll be rewriting my code to include it. I'll probably keep the 'if (currentMillis < previousMillis) reset' in there as well as it'll make me feel better…