Several things at the same time and millis() overflows

Hi all, first time poster and complete Arduino newbie here (in fact, not even that: still waiting for my first Uno to be delivered to start messing around... ).

However, I have a bit of C++ programming background and, reading the mythical "several things at the same time" thread (thanks Robin2, very useful!) I noticed overflows are never mentioned (plenty of indenting style and if vs. switch rants, though... :slight_smile: ).

My question is: what happens when millis() overflows (i.e. exceeds unsigned long capacity = 2^32, circa 49 days and 17 hours after loop starts) and, per reference, resets to 0?

I can see 2 boundary conditions (referring to the original demonstration code from this thread):

  1. one of the variables used to schedule next status updates (previousOnBoardLedMillis, previousButtonMillis, etc.) overflows before millis(), e.g. previousOnBoardLedMillis += blinkDuration > 2^32 but millis() has not yet reached 2^32. What happens in these conditions will depend on how compiled operators (e.g. ++ or +=) handle overflows of unsigned long variables: is the target variable defaulted to zero? Is it assigned the remainder of the overflow? Or is it left with a "garbage" value (e.g. least significant 32 bits of the operator's result)?

  2. millis() overflows before any of the variables used to schedule next status updates. E.g. when previousOnBoardLedMillis is very close to but still under 2^32 and updateOnBoardLedState() happens to be called a bit "late", after millis() has already been reset to zero. To be sure, this would be a rare combination of events in the real world (depending on how often update functions are called a few millisecond "late" vs. scheduled time, i.e. on how much other stuff is going on concurrently), but still possible.

Either condition (1 or 2) is guaranteed to happen roughly every couple of months, and update checks (e.g. if(currentMillis - previousOnBoardLedMillis >= onBoardLedIntervals)) will not return the intended result.

I am looking for kind suggestions on how to safely detect and handle these overflow conditions.

Thanks and take care

Hi,
Welcome to the forum.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html

This link will help.

https://playground.arduino.cc/Code/TimingRollover

Tom... :slight_smile:

Hi Jollo and welcome here,

You can as well configure an interrupt on overflow on timer 0 (which is used to handles the millis() function).
And there, you could add a handling routine when going back to 0.

Here is an interesting topic on the subject: http://forum.arduino.cc/index.php?topic=42412.0

(or as already mentioned in the previous step, simply check the value of the previous millis() cycle)

Jollo:
My question is: what happens when millis() overflows (i.e. exceeds unsigned long capacity = 2^32, circa 49 days and 17 hours after loop starts) and, per reference, resets to 0?

When you use subtraction as in

if (millis() - prevMillis >= interval)

the maths continues to work properly when millis() overflows.

Thanks to the magic of integer arithemetic there is no need for any other precautions.

…R

When using subtraction on unsigned integers. the subtraction will automatically handle it.

millis() - startTime >= interval Lets try it using byte to make the math easier

10 - 245 >= 20 Millis() has just rolled over startTime was before rollover

if you start at 10 count back to 0 then 255 on down you will see that the answer is exactly 20.

subtraction will always give the right answer when using unsigned integers hence no problems with rollover.

EDIT; @Robin2 beat me to it but it should be:

Thanks to the magic of UNSIGNED integer arithemetic there is no need for any other precautions

Thanks a lot for the warm welcome and very quick and relevant replies. Looks like you run an excellent community here! Very glad to join and looking forward to making full use of your friendly support.

Please let me wrap up what I have understood as a check (still referring to Robin2's now locked thread):

  • previousOnBoardLedMillis += onBoardLedInterval will gracefully roll over an overflow because unsigned integer arithmetics applies even though onBoardLedInterval is defined as a signed int (will be implicitly be converted to unsigned long by the compiler per C++11 standard)
  • currentMillis - previousOnBoardLedMillis >= onBoardLedInterval will also always work as intended across overflows of one or both unsigned long variables (implicit conversion of onBoardLedInterval applies here too)
  • therefore, Robin2's original demo code works correctly - without missed or untimely blinks - regardless of millis() rollovers (I doubt that anyone actually watched leds blinking for longer than 2 months, but the principle stands :slight_smile: ), and I was worrying about a whole lot of nothing
  • suggestions about defining an interrupt handler sound like overkill to me, but they got me into the internal mechanics of millis() and it's all a bit confusing: probably some of the linked threads are a little out of date (mentioning ATmega168 and 8 clock cycles per timer0 increment, whereas other sources cite 64 clock cycles per timer0 increment). Can anyone kindly point me to an up-to-date explanation?

Thanks again, cheers

Jollo:
(I doubt that anyone actually watched leds blinking for longer than 2 months,

You can easily test the concept using byte variables that are updated at (say) 0.25 second intervals.

...R

Jollo:
I doubt that anyone actually watched leds blinking for longer than 2 months,

Robin2:
You can easily test the concept using byte variables that are updated at (say) 0.25 second intervals.

As for myself, I pretty much don't use millis(). I use micros(), which will roll over within an hour and a quarter. (More precisely, it rolls over in 1 hour, 11 minutes, and just under 35 seconds.) If I want to measure a longer time interval, I have my own tricks for counting seconds, minutes, and hours.

odometer:
As for myself, I pretty much don't use millis(). I use micros(), which will roll over within an hour and a quarter. (More precisely, it rolls over in 1 hour, 11 minutes, and just under 35 seconds.) If I want to measure a longer time interval, I have my own tricks for counting seconds, minutes, and hours.

What do you need to time that lasts hours, yet requires microsecond precision?

ChrisTenone:
What do you need to time that lasts hours, yet requires microsecond precision?

For example, if I am making a stopwatch, I might have it count off 100000 microseconds as 1/10 of a second. If my stopwatch runs too fast, I might slow it down by having it count 100050 microseconds instead of 100000. If it runs too slow, then I might have it count 99950 microseconds instead of 100000. If I were to use milliseconds instead of microseconds, then I would not be able to make fine adjustments this way.

Also, if there is a rollover bug, I want things to fail fast, rather than working fine for months before failing.