Millis() arithmetic

Hello, in all example code the comparison is of the form
millis() - millisLast >= Interval
I was wandering if the following code could break when executed while milli is at the vicinity of
2 power32

Thanks!

#define sp Serial.print
#define spln Serial.println
#define sp_ sp(F(" "))
void setup() {
  Serial.begin(38400);
  spln("starting");
  bool running=true;
  unsigned long endTime=millis()+5UL*1000UL;
  while (running) {
    if (millis()>endTime) {
      sp("finished");
      running=false;
    }
  }
}

void loop() {
  ;
}

unsigned long endTime=millis()+5UL*1000UL;

This will fail because you are using addition so millis() will not become greater than endTime when the addition goes over the rollover value.

Calculations involving millis() are safe if you use unsigned long variables and subtraction for comparisons

As you say yourself

1 Like

In addition to what UKHeliBob said, you might as well just use a delay there...unless this was a hypothetical question.

1 Like

@ UKHeliBob - Thanks. I do not fully unerstand but will use the recommended form
@ ToddL1962 - Thanks. I simplified the code to ease the discussion while maintaining the form I wished I could use

See my past posts if you like.

Maybe then put another way. Exactly 5ms before the the rollover of millis() (which, incidentally, occurs about 50 days after system start), endTime will have the value 0.
So then,when this test is performed if (millis()>endTime), millis() will of course be greater than endTime (0) so this unwanted action happens sp("finished");

This may help (or not !)

To keep things simple let's work with byte sized unsigned values named currentTime,
previousTime and period

When a byte sized variable overflows its value goes from 255 to 0

period will have a constant value and the other 2 will vary as usual during each timing cycle

Start state
period equals 5
currentTime equals 253
previousTime 248

End of cycle 1
253 minus 248 equals 5 so the period has ended
previousTime now becomes 253 and cycle 2 starts

Cycle 2
The value of currentTime will change like this
253, 254, 255, 0, 1, 2

So the comparisons look like this
253 - 253 = 0
254 - 253 = 1
255 - 253 = 2
0 - 253 = -253 but -253 is impossible with an unsigned variable so the result is really -253 + 256 = 3
1 - 253 = -252 + 256 = 4
2 - 253 = -251 + 256 = 5 so the period has ended
previousTime becomes 2 and cycle 3 starts

Cycle 3
The value of currentTime will change like this
2, 3, 4, 5, 6, 7

So the comparisons look like this
2 - 2 = 0
3 - 2 = 1
4 - 2 = 2
5 - 2 = 3
6 - 2 = 4
7 - 2 = 5 so the period has ended

and so on

Exactly the same process occurs with any unsigned variable. It is just that the numbers are larger

1 Like

@ UKHeliBob Thank you. Now assume I use my approach where the target time (say some time to act) is the current time + period, 8 bits unsigned
starting
current=248 target=248+5=253
current=249 do nothing
current=250 do nothing
current=251 do nothing
current=252 do nothing
current=253 act; target=(253+5)%256=2
current=254 do nothing
current=255 do nothing
current=0 do nothing
current=1 do nothing
current=2 act; target=7;
...

Try this

byte count = 240;
byte period = 5;
byte target = count + period;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
}

void loop()
{
  Serial.print("count:");
  Serial.print(count);
  Serial.print("\ttarget:");
  Serial.println(target);
  count++;
  if (count >= target)
  {
    Serial.println("action");
    target = count + period;
  }
  delay(1000);
}

All works OK
Try changing the initial value of count to 241 and see what happens at rollover

1 Like

Here is the same thing illustrated using unsigned ints

uint16_t count = 65522;
uint16_t period = 5;
uint16_t target = count + period;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
}

void loop()
{
  count++;
  Serial.print("count:");
  Serial.print(count);
  Serial.print("\ttarget:");
  Serial.println(target);
  if (count >= target)
  {
    Serial.println("action");
    target = count + period;
  }
  delay(1000);
}

and for comparison

uint16_t count = 65522;
uint16_t period = 5;
uint16_t start = count;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
}

void loop()
{
  count++;
  Serial.print("count:");
  Serial.print(count);
  Serial.print("\tstart:");
  Serial.println(start);
  if (count - start >= period)
  {
    Serial.println("action");
    start = count;
  }
  delay(1000);
}
1 Like

Thanks. This is convincing!