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. The first time through 255, you’ll see a trigger after 47, as expected. [yep, Got it.] 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.

    Hmmm, maybe this is obvious to some, but, maybe a little more explanation here could help the rest of us. Thanks

    • So yes, it’s not easy to get your head around at first, but the type being talked about only uses 8 bits in memory to store the number. This means the lowest number it can hold is 0 and the highest is 255 (2 to the power of 8 (= 256) then subtract 1 because we started at zero). When an arithmetic operation takes the number above 255, the thing starts counting from zero again instead of going into 256, 257 etc.

      Looking at the 47 times table, incrementing by 47 gets us:

      47, 94, 141, 188, 235, 282 ….. but wait, we can’t store 282 because it’s over 255. So instead of going from 235 to 282, it actually goes from 235 to 26. The reason is because while we can count up to 255, that still leaves 26 not yet added on. So we go from 235 up to 255 with 20 of our 47, then we go one more up and end up at zero. 21 away from 47 leaves 26. So add another 26 onto zero and you are at 26. Next time it overflows back to zero it will go from 214 (which is 47 * 4 + 26) to 5 which is 214 + 47 – 256.