Hey, I have a problem that I have been thinking about for a very long time. I have a lot of circuits based on ESP8266, where I use millis() function and unsigned long variables to timer to activate the operation of certain parts of the program code. I have many circuits that run for more than 49 days, so I am looking for the best solution.
For example, I have a lot of conditions (such as the one below) that, once the maximum value of millis() is exceeded, are likely to stop working (the millisTimmer5 variable will not be refreshed).
In this case, how to make the circuit function properly after the maximum value of millis() is exceeded? At the moment my only idea is to reset the ESP8266 chip after, for example, 45 days, but I don't think this is the best solution. Does anyone perhaps know of any better ones or should I edit the conditions (how?)?
Huh. Once the millis() timer rolls over, the substraction will still give a proper result. millis() will be near 0, millisTimer5 will be bigger, making the result negative of an unsigned variable therefor will be more than half the variable and therefore the condition will be true. The issue does not exist.
The difference between two unsigned numbers is… unsigned.
As long as you use unsigned long integers, and subtract to calculate durations, millis() rollover is not a problem unless you are trying to time really long intervals like 49 days long.
basically there isn't one since it's an unsigned variable, so the actual value will be the (maximum 32-bit value + 1) - the calculated value resulting in something . the maximum 32-bit value is 0xFFFFFFFF in hexadecimal, which equals 4.294.967.295 + 1 = 4.294.967.296 - 48000 = 4.294.919.296
I did encounter that issue once, where i do pattern calculation from a given starting point in time, but given that it was, for one thing a lighting program and the skipping would only occur once every 49 days or so, i was confident no one would look at it in that very moment. Just in case i did a recalculation of the starting point, whenever millis() would exceed 0x7FFFFFFF, and would do another recalculation whenever millis would roll over passed 0 again. so the difference would never roll over.
I have a remote thermometer that sends the temperature in my well house every minute (60000 millis) using a Pro Mini, DS18B20 sensor and nRF905 radio. Been working more than 8 years continuously (except for mains outages) without rollover problems. Temp in well house was 26C last minute.
Think of a round clock with only a minute hand. What time it is, the future is move right and the past is move left. If now is 15 minutes, how long ago was the last time it was 30 minutes? From 15 I subtract 30 by moving the minute hand 30 left and it points to 45. The time was last 30, 45 mins ago.
I could add 45 to 30 and with unsigned math get 15 but the crux is how to test for 15 when current mins is > 15 until rollover when it becomes < 15 leaving no single simple test works where the end - start >= interval test always works up to the max interval.
You can use 16 bit unsigned vars for timing. Get 65.5 second intervals using half the RAM and taking half as long to calculate.
Another way is to think of the odometer on a car and how the digits turn.
Another thing you could do to test rollover is use your own millis-like function that returns a value that starts close to the rollover point:
unsigned long myMillis()
{
return millis() - 10000;
}
Anywhere you use millis() in your code, call myMillis() instead. When millis() is 0, myMillis() returns 4294957296 (0xFFFFD8F0), so it will roll over in 10 seconds. You can pick a longer or shorter rollover time depending on how you want to test it.
(Incidentally, the Linux kernel has a "jiffies" counter that works similarly to millis() and is set to an initial value that causes it to roll over 5 minutes after boot, which is designed to catch any rollover bugs.)
Here's a sawed off version of millis() that rolls over every 65.536 seconds and blinks the built in LED for one second every five. Run it and watch the displayed numbers. Written for UNO / Nano.
This to show that when you assign an unsigned long to a word, the word gets the 2 low order bytes. It masks itself.
unsigned long fakemillis = 0x1234;
word t; // you can type unsigned int, etc, or in Arduino, word.
void setup()
{
Serial.bagin( 115200 );
t = fakemillis;
Serial.println( fakemillis, HEX );
Serial.println( t, HEX );
}
void loop();