ElapsedMicros/millis, how to avoid overflowing

frankzappa

Well-known member
Hello!

In stead of me trying to solve this on my own doing something unnecessary and inefficient, I figured I’d ask here first because I’m sure it’s a common problem.

I’m starting a counter of elapsedMicros when an event occurs and I really only need to count up to maybe 3ms to know how long ago it happened. If it’s older than 3000 micros then the event is ignored.

This works fine but in case of the timer overflowing it will not be useful because I’m always checking if there are any events less than say 3ms ago.

Is there any standardised way to solve this?

Like maybe stop the count?

Thanks.
 
Actually you are not starting and stopping anything with elapsedMillis/elapsedMicros...

These two things are more or less equivalent:

Code:
elapsedMillis em = 0; // probably don't need assignment here...
<Do something>
uint32_t elapsed_time = em;
Serial.print)

And:
Code:
uint32_t start_time = millis(); // probably don't need assignment here...
<Do something>
uint32_t elapsed_time = millis() - start_time;
Serial.print)
 
And when used as KurtE shows you have not to worry about overflow as long all variables are declared uint32_t. Difference will be always correct.
 
Quick follow up to my first post... As I mentioned almost identical:

Here is the elapsedMillis()
Code:
class elapsedMillis
{
private:
	unsigned long ms;
public:
	elapsedMillis(void) { ms = millis(); }
	elapsedMillis(unsigned long val) { ms = millis() - val; }
	elapsedMillis(const elapsedMillis &orig) { ms = orig.ms; }
	operator unsigned long () const { return millis() - ms; }
	elapsedMillis & operator = (const elapsedMillis &rhs) { ms = rhs.ms; return *this; }
	elapsedMillis & operator = (unsigned long val) { ms = millis() - val; return *this; }
	elapsedMillis & operator -= (unsigned long val)      { ms += val ; return *this; }
	elapsedMillis & operator += (unsigned long val)      { ms -= val ; return *this; }
	elapsedMillis operator - (int val) const           { elapsedMillis r(*this); r.ms += val; return r; }
	elapsedMillis operator - (unsigned int val) const  { elapsedMillis r(*this); r.ms += val; return r; }
	elapsedMillis operator - (long val) const          { elapsedMillis r(*this); r.ms += val; return r; }
	elapsedMillis operator - (unsigned long val) const { elapsedMillis r(*this); r.ms += val; return r; }
	elapsedMillis operator + (int val) const           { elapsedMillis r(*this); r.ms -= val; return r; }
	elapsedMillis operator + (unsigned int val) const  { elapsedMillis r(*this); r.ms -= val; return r; }
	elapsedMillis operator + (long val) const          { elapsedMillis r(*this); r.ms -= val; return r; }
	elapsedMillis operator + (unsigned long val) const { elapsedMillis r(*this); r.ms -= val; return r; }
};

But I often use the elapsedMillis or micros as it clear about the usage. And easy...

I roll my own if, for example, I want to control for example turning on and off...
So I might have things like: if(start_time && (((millis() - start_time) > TIME_OUT)) ...
So I can turn off timeout by setting start_time to 0... Yes there is a very slight chance that millis() == 0...
 
Actually you are not starting and stopping anything with elapsedMillis/elapsedMicros...

These two things are more or less equivalent:

Code:
elapsedMillis em = 0; // probably don't need assignment here...
<Do something>
uint32_t elapsed_time = em;
Serial.print)

And:
Code:
uint32_t start_time = millis(); // probably don't need assignment here...
<Do something>
uint32_t elapsed_time = millis() - start_time;
Serial.print)

This is what I’m doing: I reset the timer to zero when an analog AC value crosses zero. Then if the signal crosses a threshold I check how long ago the signal crossed zero.

If there hasn’t been a crossing for a long time and I get an overflow and then the signal goes over the threshold I might think that an old zero crossing is a current one because zero crossings less than a certain period of time are considered as recent.

It’s unlikely to happen and it will overflow once every hour or so with elapsed micros but I want to eliminate the possibility of that happening. Still with 10 analog signals it could happen once in a while.

I may be slow but I don’t see how your example would be different.
 
Its wrong, don't do that. (See the other answers, too)
Why is it wrong?
If I want to do something every minute I do something like the code below.
After I have carried out the action I set the timer back to 0, to start over again.
What is wrong with that?
Code:
    if (minuteTimeout > 60000) {

        DoWhateverIHaveToDo();
        minuteTimeout = 0;

    }
 
I’m extra slow today. I understand now. I didn’t consider that a negative value will be represented differently with an unsigned integer.

So if the time when something happens is 990 and it starts over at 1000 and when I check the time it is maybe 10. If I say 10-990 the answer is -980 but since it’s an unsigned integer -980 is 20 which is the correct elapsed time.

Is this understood correct?
 
So what happens if something occurs at 990, it starts over at 1000 and then nothing happens until it starts over again and then I check and it’s at say 10? The time will still be 20 but 1020 has passed?

Overflow will still be an issue.
 
Last edited:
Why is it wrong?
If I want to do something every minute I do something like the code below.
After I have carried out the action I set the timer back to 0, to start over again.
What is wrong with that?
Code:
    if (minuteTimeout > 60000) {

        DoWhateverIHaveToDo();
        minuteTimeout = 0;

    }

The code would be called every minute+ the time DoWhateverIHaveTo takes. Better do
Code:
      if (minuteTimeout > 60000) {

        DoWhateverIHaveToDo();
        minuteTimeout -= 60000;

    }

if you want to make sure that you call your function every minute. Other than this I'd say your code is perfectly OK (assuming that minuteTimeout is of type elapsedMillis)
 
How about you make your own extension to micros()?

I did something like that at some point. In the main loop, I would check, whether the current ellapsed Micros is below the one in the previous loop, if so, increment 'the upper byte'

Code:
#define microMax ((1<<32)-1))/1000000.0
uint32_t currentMicros = 0;
uint32_t lastMicros = 0;
uint32_t myTimer = 0;

void main() {
  currentMicros = micros();
  if (currentMicros < lastMicros) myTimer++;

  Serial.print("time since start: "); Serial.print((double)currentMicros/1000000.0 + myTimer * microMax);  Serial.println("sec.");  // should work up to 136 years

  lastMicros = currentMicros ;
}
 
How about you make your own extension to micros()?

I did something like that at some point. In the main loop, I would check, whether the current ellapsed Micros is below the one in the previous loop, if so, increment 'the upper byte'

Code:
#define microMax ((1<<32)-1))/1000000.0
uint32_t currentMicros = 0;
uint32_t lastMicros = 0;
uint32_t myTimer = 0;

void main() {
  currentMicros = micros();
  if (currentMicros < lastMicros) myTimer++;

  Serial.print("time since start: "); Serial.print((double)currentMicros/1000000.0 + myTimer * microMax);  Serial.println("sec.");  // should work up to 136 years

  lastMicros = currentMicros ;
}

Yeah I guess I’ll have to do something like that. Thanks.
 
If there hasn’t been a crossing for a long time and I get an overflow and then the signal goes over the threshold I might think that an old zero crossing is a current one because zero crossings less than a certain period of time are considered as recent.

It’s unlikely to happen and it will overflow once every hour or so with elapsed micros but I want to eliminate the possibility of that happening. Still with 10 analog signals it could happen once in a while.

Assuming your code is able to check much more frequently than 1.19 hours needed for 32 bit overflow, you could check if the elapsedMicro number is "large" and getting "too close" to 0xFFFFFFFF. Obviously you don't want to set if back to zero, since that's what you do when detecting a zero crossing. But if it's more than some amount, you could just set it back to that "large, but not too large" amount. Then it will never overflow, as long as your code always performs this check more frequently than the number of microseconds from your "large" threshold to the 32 bit overflow.
 
Assuming your code is able to check much more frequently than 1.19 hours needed for 32 bit overflow, you could check if the elapsedMicro number is "large" and getting "too close" to 0xFFFFFFFF. Obviously you don't want to set if back to zero, since that's what you do when detecting a zero crossing. But if it's more than some amount, you could just set it back to that "large, but not too large" amount. Then it will never overflow, as long as your code always performs this check more frequently than the number of microseconds from your "large" threshold to the 32 bit overflow.

Paul this is exactly how I've thought about solvning it. I just have to check the 10 timers I have for this from time to time and if it's too large reset it to a value that's above what my program thinks is reasonable. In my case if the timer is more than 2000 micros the program thinks the value is too old so I just reset it above that.

It's extremely rare that this problem would occur anyway, there is a window of 2ms per hour and there are 10 sensors but I still want to avoid it if possible.
 
Back
Top