Go Down

### Topic: safe way to prevent millis() wrap? (Read 5697 times)previous topic - next topic

#### scottmcphee

##### Oct 31, 2009, 06:30 pm
I use ( millis() + 1000 ) to set a future time that I loop until reaching, and in the odd case when millis is at the high end of its range, this is not going to work well... like every 59 days or whatever.

Nothing else in my code is timer sensitive, so I'd just as soon reset millis() to zero just prior to my need of adding 1000 to it, ever time.  Then I am guaranteed not to blow its variable size limits, and there is no concern about wrapping on the high end.

What's the command to set millis() back to zero?

Thanks

#1
##### Nov 01, 2009, 01:36 am
Quote
like every 59 49 days or whatever

Quote
What's the command to set millis() back to zero?

If you do the math and comparisons in a certain way it isn't necessary to reset millis.  Post the code that has you concerned.

#### BenF

#2
##### Nov 01, 2009, 09:57 am
When we only care about time intervals, overflow can be used to our advantage as the simple arithmetic difference between two sampled time intervals will remain correct irrespective of overflow.

In the code snippet below, millis() gets truncated to a 16 bit unsigned quantity which will overflow every 65'ish seconds. The calculated time difference however will remain correct as long as LOOP_INTERVAL is less than half the overflow period (32 seconds or so).

Code: [Select]
`// Synchronization loop for short intervals (less than 32 seconds)#define TimeLap(t1) (int)((word)millis()-(word)t1)#define LOOP_INTERVAL 1000 // One secondvoid loop(){  static word loop_time;    if (TimeLap(loop_time)>=0) {    // time synchronized code goes here    loop_time+=LOOP_INTERVAL; // schedule next run  }/*if*/}/*loop*/`

Applications that run idle for longer intervals (more than half a minute) should use the full 32 bit millis() value. This version works equally well for short intervals, but consumes more resources than the alternative above.

Code: [Select]
`// Synchronization loop for long intervals (more than 32 seconds)#define TimeLap(t1) (long)((unsigned long)millis()-(unsigned long)t1)#define LOOP_INTERVAL 60000 // One minutevoid loop(){  static unsigned long loop_time;    if (TimeLap(loop_time)>=0) {    // time synchronized code goes here    loop_time+=LOOP_INTERVAL; // schedule next run  }/*if*/}/*loop*/`

#### scottmcphee

#3
##### Nov 02, 2009, 12:49 amLast Edit: Nov 02, 2009, 12:52 am by scottmcphee Reason: 1
The code I'm concerned about is millis() + 1000 exceeding the range of the variable.

I'm doing a one-shot fuse, not looping for pacing code in even intervals.

I do this at the end of setup():

fuse = millis() + 1000;

...

Then, in loop() I have an if statement comparing fuse to current millis().  When millis() exceeds fuse I put the CPU to sleep.

I'm lazy and don't want to do a bunch of conditional logic checking for boundary cases of millis().   I just want to whack millis() to zero in place of setting fuse.  Then in my loop() code, I'll just use
if ( millis() > 1000 )...

Anybody know how to whack millis() ?

#4
##### Nov 02, 2009, 04:32 am
Quote
Anybody know how to whack millis() ?

Yes but "whacking millis" will be implementation dependent.  In other words, at any point in the future it may stop working because the folks who work on the Arduino core may change the implmentation.  I strongly suggest you do something like this instead...

Code: [Select]
`unsigned long fuseStart;void setup( void ){  fuseStart = millis();}void loop( void ){  if ( millis() - fuseStart >= 1000 )  {    // Sleep here  }}`

In this code I assume the processor will never wake from sleeping.  If that isn't the case it will need a change.

#### scottmcphee

#5
##### Nov 02, 2009, 05:41 amLast Edit: Nov 02, 2009, 05:43 am by scottmcphee Reason: 1
Your code snippet illustrates my point, it breaks when millis is near the top end of it range.  You'll end up getting a huge negative number in the comparison and it will never trigger... er ah, well in 49 days it will trigger.

So, given Arduino v0017, what's the commands to whack millis ?
I'm running on a 328P chip.

#6
##### Nov 02, 2009, 06:32 am
Quote
it breaks when millis is near the top end of it range

If you can prove that you're correct, I'll give you the snippet of code to reset millis for Arduino 15 and I'll download and install Arduino 17 to ensure the snippet is still valid.

#### BenF

#7
##### Nov 02, 2009, 07:55 am
Quote
The code I'm concerned about is millis() + 1000 exceeding the range of the variable.

.. and that's what you got a solution for.

If you need to time an interval between sleep cycles, the code would read as follows:

Code: [Select]
`// Synchronization loop for short intervals (less than 32 seconds)#define TimeLap(t1) (int)((word)millis()-(word)t1)#define LOOP_INTERVAL 1000 // One secondvoid loop(){  // first sleep cycle after 1s  static word loop_time = LOOP_INTERVAL;    if (TimeLap(loop_time)>=0) {    // put Arduino to sleep here    loop_time=(word)millis()+LOOP_INTERVAL; // schedule next sleep  }/*if*/}/*loop*/`

#### scottmcphee

#8
##### Nov 02, 2009, 05:36 pmLast Edit: Nov 02, 2009, 05:56 pm by scottmcphee Reason: 1
BenF - aha!  With all that type casting I must have missed the fact you are working with the least significant bits of the counter by truncating it.

For what I need to do, I think I have to move the following lines of code you show up to setup() so it only runs once, and get it out of the loop.

// first sleep cycle after 1s
static word loop_time = LOOP_INTERVAL;

What I see happening here is the first interval is not likely going to be a whole interval, but thereafter, I'll get the full 1s.  This means a glitch on the first cycle.

My application sleeps mostly.  External interrupt button wakes it up, it does something, then stays awake for a short period of time listening for a response which may or may not come, then goes back to sleep again.  So it's more of a one-shot timer per run, than a cyclic design.   Timing that first cycle, or one-shot, is the most important cycle.

Anyway, I get the message of ignoring millis() boundary conditions if you play in a safe zone of lower bits only.   I'll play with this idea.

It would be far easier to whack millis to zero for me, so if that secret is revealed shortly, I'll be in good shape.

#### BenF

#9
##### Nov 02, 2009, 08:03 pm
Quote
For what I need to do, I think I have to move the following lines of code you show up to setup() so it only runs once, and get it out of the loop.

// first sleep cycle after 1s
static word loop_time = LOOP_INTERVAL;

Actually - no. The "static" keyword makes "loop_time" the equivalent of a global variable with a one time initializer. The only difference is that the scope (visibility) of the variable is limited to the loop function. If you need a different first time interval - you just keep the code as is, but change the following:

// first sleep cycle
static word loop_time = FIRST_RUN_INTERVAL;

#### scottmcphee

#10
##### Nov 03, 2009, 02:15 amLast Edit: Nov 03, 2009, 02:20 am by scottmcphee Reason: 1
Even if it's static, won't the next iteration of loop overwrite it?

#### scottmcphee

#11
##### Nov 03, 2009, 09:09 pmLast Edit: Nov 03, 2009, 09:23 pm by scottmcphee Reason: 1
Here is the variable declaration Arduino uses to persist millis:
volatile unsigned long timer0_millis = 0;

So I presume this is how I'd whack it, in my code:
timer0_millis = 0;

So my problem is reduced to this:

setup()
{
// quiesce chip features, change I/O pins, and enable power saves
// setup external interrupt buttons (low level interrupt)
// sleep here (power down mode)
// wakes up here
...
// setup the way I need things
// do my job (transmit information - one shot)

timer0_millis = 0;
}

loop()
{
// listen for echo (if serial.Available() > 0)...
...
if ( millis() >= 1000 )  {
}
}

#### Fjornir

#12
##### Nov 03, 2009, 09:20 pm
So what I don't understand is why you want to prevent the wrap in millis() vs the easier solution where you could just accommodate in your logic for the fact that it does wrap.

#### scottmcphee

#13
##### Nov 03, 2009, 09:43 pm
/quote  So what I don't understand is why you want to prevent the wrap in millis() vs the easier solution where you could just accommodate in your logic for the fact that it does wrap.  /endquote

Where's the sport in that?

I think you'll agree the code I just posted above is about the easiest thing that can be done here with a one-shot use of a time, that happens to hinge on using millis(), a timer is already built in arduino base.

I'll agree that I'm not using millis() for the purposes it was intended to be used.  But so what?  I don't need to have running uptime in my application.  If arduino provided a general timer library, I'd use that instead.    I'd prefer to have a countdown timer and set its value in milliseconds, and then check it for zero.

This is the closest thing to that.

#14
##### Nov 03, 2009, 10:07 pmLast Edit: Nov 03, 2009, 10:09 pm by bcook Reason: 1
Quote
So I presume this is how I'd whack it, in my code:
timer0_millis = 0;

That is NOT correct.  Your code suffers from a race condition that will eventually corrupt the value of timer0_millis.

Go Up

Please enter a valid email to subscribe