How does the Time library adjust when millis() rolls over?

Hi!

I'm new to Arduino. I wrote a sketch using the Time library (which works great, though my DS1307 seems to run very fast -- that's another topic...) and I became curious and dug into the library a bit to see how it works. I think I mostly get what is going on, but I can't figure out how it handles the situation where millis() rolls back to zero. Can somebody explain it to me?

Here's the meat & potatoes function that gets the time. Doesn't the same value of sysTime just get returned over and over again since it's not being incremented anymore? What am I screwing up? I'm new to C/C++ so hit me with the cluestick please.

time_t now(){
  while( millis() - prevMillis >= 1000){      
    sysTime++;
    prevMillis += 1000; 
#ifdef TIME_DRIFT_INFO
    sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift     
#endif  
  }
  if(nextSyncTime <= sysTime){
        if(getTimePtr != 0){
          time_t t = getTimePtr();
      if( t != 0)
        setTime(t);
      else
        Status = (Status == timeNotSet) ?  timeNotSet : timeNeedsSync;        
    }
  }  
  return sysTime;
}

Thanks!

  while( millis() - prevMillis >= 1000){

This works even when millis () rolls over back to zero.

Try it on a hex calculator (discarding the high-order bytes which don't fit into an unsigned long).

I don't know about the rest of your code. It depends on how prevMillis is defined, etc.

But the concept is there - doing the subtraction handles the roll-over.

Well that's damn sneaky. I hadn't really had any exposure to integer overflow before. Thanks!

Nick, will that also work for the Y2038 problem ? ( which I only read about yesterday - it will only effect my projects if I get to be 90 :slight_smile: )

Hmm, good point. I'm not sure whether to hope I have to worry about that, or hope I don't have to worry about it. :wink:

I doubt somewhat that every Unix programmer has used subtraction rather than addition, so the problem will probably arise sporadically. I think when the day gets closer libraries will be rewritten to use 64 bits rather than 32, so the problem will basically go away.

So double-longs will have to be declared then??

I think so.

In arduino terms they are: int64_t

see - avr-libc: <stdint.h>: Standard Integer Types -

Warning, doing math with them is Sssssslllllllloooooowwwwww :wink:

The type "long long" compiles.

Example:

volatile long long foo = 42;

void setup () {}

void loop () 
{
  foo++;
}

It generates a laughably large amount of code to do foo++ :

void loop () 
  a8:	1f 93       	push	r17
{
  foo++;
  aa:	80 91 00 01 	lds	r24, 0x0100
  ae:	20 91 01 01 	lds	r18, 0x0101
  b2:	30 91 02 01 	lds	r19, 0x0102
  b6:	40 91 03 01 	lds	r20, 0x0103
  ba:	60 91 04 01 	lds	r22, 0x0104
  be:	e0 91 05 01 	lds	r30, 0x0105
  c2:	a0 91 06 01 	lds	r26, 0x0106
  c6:	10 91 07 01 	lds	r17, 0x0107
  ca:	b8 2f       	mov	r27, r24
  cc:	bf 5f       	subi	r27, 0xFF	; 255
  ce:	91 e0       	ldi	r25, 0x01	; 1
  d0:	b8 17       	cp	r27, r24
  d2:	08 f0       	brcs	.+2      	; 0xd6 <loop+0x2e>
  d4:	90 e0       	ldi	r25, 0x00	; 0
  d6:	f9 2f       	mov	r31, r25
  d8:	f2 0f       	add	r31, r18
  da:	81 e0       	ldi	r24, 0x01	; 1
  dc:	f2 17       	cp	r31, r18
  de:	08 f0       	brcs	.+2      	; 0xe2 <loop+0x3a>
  e0:	80 e0       	ldi	r24, 0x00	; 0
  e2:	78 2f       	mov	r23, r24
  e4:	73 0f       	add	r23, r19
  e6:	81 e0       	ldi	r24, 0x01	; 1
  e8:	73 17       	cp	r23, r19
  ea:	08 f0       	brcs	.+2      	; 0xee <loop+0x46>
  ec:	80 e0       	ldi	r24, 0x00	; 0
  ee:	58 2f       	mov	r21, r24
  f0:	54 0f       	add	r21, r20
  f2:	81 e0       	ldi	r24, 0x01	; 1
  f4:	54 17       	cp	r21, r20
  f6:	08 f0       	brcs	.+2      	; 0xfa <loop+0x52>
  f8:	80 e0       	ldi	r24, 0x00	; 0
  fa:	38 2f       	mov	r19, r24
  fc:	36 0f       	add	r19, r22
  fe:	81 e0       	ldi	r24, 0x01	; 1
 100:	36 17       	cp	r19, r22
 102:	08 f0       	brcs	.+2      	; 0x106 <loop+0x5e>
 104:	80 e0       	ldi	r24, 0x00	; 0
 106:	28 2f       	mov	r18, r24
 108:	2e 0f       	add	r18, r30
 10a:	81 e0       	ldi	r24, 0x01	; 1
 10c:	2e 17       	cp	r18, r30
 10e:	08 f0       	brcs	.+2      	; 0x112 <loop+0x6a>
 110:	80 e0       	ldi	r24, 0x00	; 0
 112:	98 2f       	mov	r25, r24
 114:	9a 0f       	add	r25, r26
 116:	81 e0       	ldi	r24, 0x01	; 1
 118:	9a 17       	cp	r25, r26
 11a:	08 f0       	brcs	.+2      	; 0x11e <loop+0x76>
 11c:	80 e0       	ldi	r24, 0x00	; 0
 11e:	81 0f       	add	r24, r17
 120:	b0 93 00 01 	sts	0x0100, r27
 124:	f0 93 01 01 	sts	0x0101, r31
 128:	70 93 02 01 	sts	0x0102, r23
 12c:	50 93 03 01 	sts	0x0103, r21
 130:	30 93 04 01 	sts	0x0104, r19
 134:	20 93 05 01 	sts	0x0105, r18
 138:	90 93 06 01 	sts	0x0106, r25
 13c:	80 93 07 01 	sts	0x0107, r24
} 
 140:	1f 91       	pop	r17
 142:	08 95       	ret

So I don't see that as a viable way of tracking time on the Arduino. However of course, we can still use millis () and micros (). We can let the clock chips worry about how they store the time.