Solving the millis() reset problem

So this issue came up on my other project thread and I would like a good answer if I can find one. I started out with code that would fail when millis() resets automatically at about 50 days. I have added the code (first if statement) below. I believe this will correct the issue.

I know delay() would work but not for me as this is not the only thing I want to do in my loop.

I am open to suggestions on better ways to do this a couple users on my project thread were going back and forth so I wanted to make this it's own thread.

void loop(void)
{


//if millis() has been reset this will correct the error
	if (time > millis())
	{
		time = millis();
	}
	
	//only read sensor if 5sec has passed
	if(millis() > time + timeBetweenReadings)
	{
                //Do stuff here I removed the code so this would be easy to follow
        }

}

You never need to worry about millis() resetting for timing purposes. As long as you use unsigned long to hold all times in milliseconds, and do subtractions, everything will work out correctly. See this explanation.

If you are keeping a log file that has to keep track of time over a period of more than 50 days, use an RTC. The clock on the Arduino isn't accurate.

If millisec() rolls back over to zero, and your time variable is also unsigned long, it seems that if you add 5000 ms. to the last time value right before millisec() rolls over, your time variable should roll over too, and never skip a beat.

asherflynt:
So this issue came up on my other project thread and I would like a good answer if I can find one. I started out with code that would fail when millis() resets automatically at about 50 days. I have added the code (first if statement) below. I believe this will correct the issue.

I know delay() would work but not for me as this is not the only thing I want to do in my loop.

I am open to suggestions on better ways to do this a couple users on my project thread were going back and forth so I wanted to make this it's own thread.

the 50day overflow can catch you if your code is something like this:

unsigned long timeout=0;

void loop(){
if(millis()>timeout) { 
   // do some action
   timeout = millis() +1000; // do the action in one second
   }
}

Now that code looks pretty bullet proof doesn't it?
but,
if (millis() + 1000 ) causes a rollover, the action will trigger every loop, as long as (millis()+1000) causes a rollover and millis() has not rolled over, timeout will always be less than millis().

So, you have to code for this condition, either reset the millisecond counter, or set your own rollover flag.

bool rollover=false;
unsigned long timeout=0, timeout1=0;
void loop(){
  unsigned long timenow = millis();
  if(timenow<timeout1) rollover = false; // reset rollover flag

  if((timenow>timeout)&&(!rollover)){
    do something
    timeout = timenow + 1000;
    timeout1 = timenow; // check for rollover
    rollover = (timeout<timenow);

    }

other important code

}

Now this code looks even better, but there is another gotya here !

if timeout is set to a large value and millis() rolls, because 'other important code' took a while to complete, the timeout would not trigger for 50 days!

So, you need to code for this possibility: timeout is some big value and millis() has slipped by to roll.

bool rollover=false;
unsigned long timeout=0, timeout1=0;
void loop(){
  unsigned long timenow = millis();
  if(timenow<timeout1) {// millis rolled!
     if(!rollover) timeout=timenow; // cant be timenow-1 because timenow could be 0
                                            // catch the timeout on the next loop!
     timeout1 = timenow; // clear the millis rollover check.
     rollover = false; // reset rollover flag
     }

  if((timenow>=timeout)&&(!rollover)){
    do something
    timeout = timenow + 1000;
    timeout1 = timenow; // check for rollover
    rollover = (timeout<timenow);
    }

other important code

}

So this is what I think needs to be done to handle Millis() rollovers for timeout coding.

Chuck.

asherflynt:
So this issue came up on my other project thread and I would like a good answer if I can find one. I started out with code that would fail when millis() resets automatically at about 50 days. I have added the code (first if statement) below. I believe this will correct the issue.

I know delay() would work but not for me as this is not the only thing I want to do in my loop.

I am open to suggestions on better ways to do this a couple users on my project thread were going back and forth so I wanted to make this it's own thread.

void loop(void)

{

//if millis() has been reset this will correct the error
if (time > millis())
{
time = millis();
}

//only read sensor if 5sec has passed
if(millis() > time + timeBetweenReadings)
{
               //Do stuff here I removed the code so this would be easy to follow
       }

}

if millis() is near max, and time is less than millis() and (time + timeBetweenReading) rolls, it will repeatly trigger as long as millis() hasn't rolled.

So if timebetweenReading is 5000 (five seconds) and the loop processes in 10 milliseconds, for the last five seconds of the fifty day period, the sensor will be read 1000ms/10ms = 100 times per seconds for 5 seconds or 500 times.

That is the glitch I see with this code. Check my other post to see if my solution hold water.

Chuck.

There is no problem with millis() rollover thanks to the natural features of binary integer maths - just ensure all the variables are unsigned long

if (millis() - prevMIllis >= interval) {
prevMillis += interval;
// other stuff
}

will work perfectly. The important feature is the use of subtraction.

You can try this on a piece of paper more easily with unsigned 8-bit values. 252 + 7 = 4 and 4 - 252 = 7
(or something very close to that)

...R