Pages: [1] 2   Go Down
Author Topic: safe way to prevent millis() wrap?  (Read 3246 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 209
Posts: 13015
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
// Synchronization loop for short intervals (less than 32 seconds)
#define TimeLap(t1) (int)((word)millis()-(word)t1)
#define LOOP_INTERVAL 1000 // One second

void 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:
// Synchronization loop for long intervals (more than 32 seconds)
#define TimeLap(t1) (long)((unsigned long)millis()-(unsigned long)t1)
#define LOOP_INTERVAL 60000 // One minute

void 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*/
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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() ?


« Last Edit: November 01, 2009, 06:52:55 pm by scottmcphee » Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 209
Posts: 13015
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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.
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: November 01, 2009, 11:43:37 pm by scottmcphee » Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 209
Posts: 13015
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
// Synchronization loop for short intervals (less than 32 seconds)
#define TimeLap(t1) (int)((word)millis()-(word)t1)
#define LOOP_INTERVAL 1000 // One second

void 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*/
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.


« Last Edit: November 02, 2009, 11:56:56 am by scottmcphee » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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;
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Even if it's static, won't the next iteration of loop overwrite it?
« Last Edit: November 02, 2009, 08:20:47 pm by scottmcphee » Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I found my own answer, reading code tells all.
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 )  {
    // reset (jump to address zero)
  }
}




« Last Edit: November 03, 2009, 03:23:35 pm by scottmcphee » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 163
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

/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.



Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 209
Posts: 13015
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: November 03, 2009, 04:09:53 pm by bcook » Logged

Pages: [1] 2   Go Up
Jump to: