Pages: [1] 2   Go Down
Author Topic: Resetting Millis() to zero, reset clock  (Read 4493 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This topic is a little summary of the research I did this morning on the unsigned long millis(). If it doesn't add any existing knowledge, then let the post be for reference purposes only.  

Millis() function itself
Unsigned long 32bit variable. Meaning 2^32-1 milliseconds range (no negative numbers possible). This equates to: (2^32-1) / 1000ms / 60sec / 60min / 24hr = 49.71 days. Or 49 days and 17 hours. Millis() is derived from timer0_millis, and overflows result in the number returning to zero (and continuing counting from zero). Overflows do not crash the arduino, Millis() overflows do not crash your code, and the timer will not stop counting. However, if you want events to happen fewer than once every 50 days, you should use RTC and the time library.

Difficulties arising from Millis() overflow
Take a look at Gammon's explanation of the millis() issue, and what is often done wrong that causes errors. In brief, setting your stop-time ahead of the current time, and creating an if-statement that checks when the current time exceeds the "stop-time" variable will create issues near overflow. However, Using the difference between current time and stop time, and comparing that to a set interval will never create issues. (see link for code examples).

I've edited to text below to accommodate for future reference:
Is it possible to reset Millis()? Yes, though not recommended:
According to the official wiring.c source code (see here: Wire.c), Millis() is derived from timer0_millis. For this reason, timer0_millis can basically be seen as just another unsigned long, in fact it is. Thus, it can be manipulated at will by the programmer. Please realize that the code below is a "hack" that shouldn't be used unless you know and you've tested its effects on your millis()-dependent libraries. It is better to use an external RTC with the Time library for accurate time management.

Before void setup(), place:
Code:
extern volatile unsigned long timer0_millis;

Whenever you want to reset millis():
Code:
noInterrupts ();
timer0_millis = 0;
interrupts ();

Thanks for reading, hope it adds some new info to this forum

Many thanks to the forum members below, especially Gammon, for careful explanation and elaboration of the issues at hand!
« Last Edit: August 28, 2013, 03:56:02 pm by redtails » Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4086
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Examples and support for some of the statements are needed.

In some situations, it is better to reset timer0 (see below) in a controlled fashion, instead of letting it overflow at its limit.

What would some of those situations be? How is letting the timer overflow less controlled than another method?

Quote
Though due to technical limitations, arduino cannot calculate numbers larger than 2^32-1 ...

Technically not true, e.g. long long myInt;

Quote
Issues arising from not resetting Millis()
Performing timepoint1 = millis();  near the overflow can (and will) result in erroneous interval comparisons if millis() returns back to zero. A recorded timepoint1, compared to overflowed millis() will return nothing: difference = timepoint2 - timepoint1; difference = 1000 - (2^32-500).

Not true, you might try it. millis() is suitable for any interval comparison, provided that the interval is < 49.71 days.

Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 302
Posts: 26348
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I personally prefer resetting timer0_millis back to 0 manually in a periodically fashion to prevent any weird stuff from happening every 50 days.
Quote
Please refrain from giving your opinion on why it is bad code practice to reset millis();
Quote
Though due to technical limitations, arduino cannot calculate numbers larger than 2^32-1,
Quote
I'm not a good-enough programmer to understand what timer0_millis is based on
{Sigh}
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Check this stopwatch class - http://playground.arduino.cc/Code/StopWatchClass - it can be used like a resetable millis(), micros

Does this help?
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4086
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Not true, you might try it. millis() is suitable for any interval comparison, provided that the interval is < 49.71 days.

Let me rephrase that:

Rollovers notwithstanding, millis() is suitable for any interval comparison, provided that the interval is < 49.71 days.

This is simple enough to simulate, so I offer the following example. Please run it and post the results for us.

Code:
//millis() rollover simulation

void setup(void)
{
    unsigned long myMillis, t1, t2, lapse;
   
    Serial.begin(9600);

    //set the simulated millis value to 50 milliseconds before the dreaded rollover
    myMillis = 4294967245;    //this is 2^32 - 51
    t1 = myMillis;            //record the start time
   
    //simulate millis incrementing for 100 milliseconds
    for (int i = 0; i < 100; i++) myMillis++;
   
    t2 = myMillis;            //record the end time
    lapse = t2 - t1;          //calculate elapsed time
   
    //print the results
    Serial.println();
    Serial.print("Start ");
    Serial.println(t1, DEC);
    Serial.print("End ");
    Serial.println(t2, DEC);
    Serial.print("Elapsed ");
    Serial.println(lapse, DEC);
}

void loop(void)
{
}

(Yes I know this code or something like it probably already exists in a dozen threads on the forum but I'm too lazy to search for it.)
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

What would some of those situations be? How is letting the timer overflow less controlled than another method?
Example is given in OP. When dealing with large intervals, uncertainty of roll-over increases, making the possibility of negative return on comparisons (smaller number minus larger number) greater.

Technically not true, e.g. long long myInt;
Unsigned long long variables are not described in Arduino knowledge base. I did not know about this possibility.

Not true, you might try it. millis() is suitable for any interval comparison, provided that the interval is < 49.71 days.
I did try. Smaller number minus larger number results in no return on unsigned long. The reasoning is simple, as unsigned cannot contain negative numbers. So even if interval is smaller than 2^32-1, the return can be absent if millis() overflowed!!


Check this stopwatch class - http://playground.arduino.cc/Code/StopWatchClass - it can be used like a resetable millis(), micros

Does this help?
StopWatchClass is an interesting bit of code. I had a look around the code and it's been very cleanly written! I can see that it derives its time from millis() and determines lap-time on return _stoptime - _starttime;. I'm afraid it suffers from the same issues as millis() when starttime is greater than stoptime (like when stoptime is after an overflow). Interesting code, nonetheless
« Last Edit: August 26, 2013, 08:59:39 am by redtails » Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 302
Posts: 26348
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
When dealing with large intervals, uncertainty of roll-over increases,
There is no uncertainty, so it cannot increase.
Roll-over will occur.
Deal with it properly, and all will be well.
There is no need to reset the millisecond clock.

Quote
Smaller number minus larger number results in no return on unsigned long.
I don't understand that sentence.


Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Not true, you might try it. millis() is suitable for any interval comparison, provided that the interval is < 49.71 days.

Let me rephrase that:

Rollovers notwithstanding, millis() is suitable for any interval comparison, provided that the interval is < 49.71 days.

This is simple enough to simulate, so I offer the following example. Please run it and post the results for us.

Code:
//millis() rollover simulation

void setup(void)
{
    unsigned long myMillis, t1, t2, lapse;
   
    Serial.begin(9600);

    //set the simulated millis value to 50 milliseconds before the dreaded rollover
    myMillis = 4294967245;    //this is 2^32 - 51
    t1 = myMillis;            //record the start time
   
    //simulate millis incrementing for 100 milliseconds
    for (int i = 0; i < 100; i++) myMillis++;
   
    t2 = myMillis;            //record the end time
    lapse = t2 - t1;          //calculate elapsed time
   
    //print the results
    Serial.println();
    Serial.print("Start ");
    Serial.println(t1, DEC);
    Serial.print("End ");
    Serial.println(t2, DEC);
    Serial.print("Elapsed ");
    Serial.println(lapse, DEC);
}

void loop(void)
{
}

(Yes I know this code or something like it probably already exists in a dozen threads on the forum but I'm too lazy to search for it.)

Hmm this is very interesting. Even though arduino is calculating 4294967245 - 50 , it's still returning 100. Can you elaborate on this a bit? It seems to go against common logic that 4294967245 - 50 = 100. Am I missing something here? Is Arduino actually taking notes on all unsigned longs that have overflowed and dealing with them correctly arithmetically? Well I know the answer to this is yes, but can someone with deep understanding of this point me to the source-code in which this is described?
Logged

Global Moderator
Boston area, metrowest
Online Online
Brattain Member
*****
Karma: 538
Posts: 27118
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Open your calculator, to Progamming mode. Select HEX mode.
Unsigned long goes from 0000 0000 to FFFF FFFF, 32 bits.

Time comparisons are done using subtraction: current time - earlier time.
So a post rollover number, say 0000 0010, minus a pre-rollover number, FFFF FF00, will result in a correct result:
FFFF FFFF 0000 0110
Ignoring the upper 32 bits of the 64 bit result leave 0000 0110, which is the correct answer: 100 befor the rollover + 10 after = 110.
Arduino does the same - results past the lowe 32 bits fall off.
Looking in the decimal world leads to confusion - need to look at in binary world (with HEX just being a convenient to look at binary)
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
It seems to go against common logic that 4294967245 - 50 = 100.

That would be indeed, but it is in fact 50 - 4294967245 = 100 that is shown in the sketch of Jack C.

one way to look at it is to see 4294967245 as 50 less than MAX_ULONG, you might see it as -50
then 50 - -50 == 50 + 50 = 100

« Last Edit: August 27, 2013, 02:36:59 am by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4086
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hmm this is very interesting. Even though arduino is calculating 4294967245 - 50 , it's still returning 100. Can you elaborate on this a bit? It seems to go against common logic that 50 - 4294967245 = 100 (edit: jc). Am I missing something here? Is Arduino actually taking notes on all unsigned longs that have overflowed and dealing with them correctly arithmetically? Well I know the answer to this is yes, but can someone with deep understanding of this point me to the source-code in which this is described?

No notes are being taken, it is merely the magic of modular integer arithmetic. The odometer analogy is a good one. Say a six-digit odometer reads 999950 and we drive the car 100 miles. At the end, the odometer reads 50 miles. So the ending reading minus the starting reading (50 - 999950) must be 100 miles. There is no such concept as negative miles on an odometer, just as there is no concept of negative numbers with unsigned integers. The cross-check is to start with 50 miles on the odometer and drive the car backwards for 100 miles. (Assume for the sake of discussion that the odometer moves backward when the car is driven in reverse. I think this may have been true in the past when odometers were purely mechanical, but it is probably not true these days.)

Another way to look at it, add 1,000,000 to the ending reading to guarantee a positive result, then if needed, truncate the result to six digits to fit the odometer, so 1,000,050 - 999950 = 100. The fixed width of the odometer is analogous to the fixed width of an integer variable, except it's base 10 instead of base 2.
« Last Edit: August 26, 2013, 01:20:20 pm by Jack Christensen » Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 302
Posts: 26348
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
No notes are being taken, it is merely the magic of modular integer arithmetic
sp. "modulo"
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Cologne, Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello
I have another question about this topic. How about setting timer0_millis to 4 294 963 696 (1 hour before rolover)
to test if code is rollover proof?
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4086
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
No notes are being taken, it is merely the magic of modular integer arithmetic
sp. "modulo"

Another link for the OP: http://en.wikipedia.org/wiki/Modular_arithmetic
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 302
Posts: 26348
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Apologies - maybe it was taught as "modulo arithmetic" in English-speaking institutions in the 1970s.
That's my recollection anyway.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Pages: [1] 2   Go Up
Jump to: