Loop dies after ~32 seconds

I put together a quick test of an idea I had to keep tracks of things like timers without blocking the loop. My code is as follows, but I don't understand why the LED only blinks 16 times, and then stops (off).

#include <loopTimer.h>
int led13setup = 0;
int led13last_time = 0;
int led13next_time = 0;
int led13status = 0;

void setup() {
  // put your setup code here, to run once:
}

void loop() {
  // put your main code here, to run repeatedly:
  blinkLed13();
  
}

void blinkLed13() {
  if (led13setup == 0) {
     pinMode(13, OUTPUT);
     led13setup = 1;   
  }

  if (led13last_time == 0) {
    led13last_time = millis();
    led13next_time  = millis() + 1000;
    digitalWrite(13, HIGH);
  } else {
    if (millis() > led13next_time) {
      led13last_time = led13next_time;
      led13next_time = millis() + 1000;
       if (led13status == 1) {
        led13status=0;
        digitalWrite(13, LOW);        
       } else {
        led13status=1;
        digitalWrite(13, HIGH);
       }
    }     
  }

  
}

Should be unsigned long, which is the type the millis function returns
.

That makes perfect sense.
Thank you!

Welcome to the forum

Change your timing variables to unsigned long

The maximum values if the signed int variables are causing the problem and you have made it worse by using addition instead of subtraction in the calculations

Use

if (currentTime - lastTime >= period)

to determine whether the period has elapsed

See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

Thank you.
I was actually reading up on the millis rollover and better ways to implement that a moment ago!

see the example

Blink Without Delay

They even have put in the example in the IDE - just for you :wink:

I was struggling to wrap my head around why this doesn't break on rollover.

Assumptions / understanding:

  • Unsigned long variables have a range from 0 to 4,294,967,295.
  • When an unsigned long reaches this limit, it rolls over to zero.
  • IF statement checks to see if currentMillis - previousMillis >= interval

Example:
If interval is 1000
and previousMillis = 4,294,967,200
and currentMillis = 805

805 - 4,294,967,200 = -4,294,966,395.
But the result is cast as an unsigned long!

if ((unsigned long)(currentMillis - previousMillis) >= interval)

Since an unsigned long cannot hold a negative value, I believe it would store the absolute value instead. This would be greater than or equal to the interval. However, it would reach this status as soon as the millis rolled over to zero. This would result in an incorrect interval pulse every 49ish days.

Am I understanding this correctly?

The difference of two unsigned values is also unsigned; there is no need for a cast.

I am still left with an anomaly at rollover, but I think it can be a non-issue if handled properly. Blinking an LED is just a simple task to illustrate the concept of non-blocking wait.

The current task is to utilize various counters (scaled in minutes) and act once they reach a target threshold. The counters initialize and reset based on a switch position. In this case, it would be the ignition switch position.

Arduino wakes and enables power to different areas if switch is turned "on", and begins timers to disable power to areas at intervals after switch is turned "off". For example, the power windows might retain power for 5 minutes after the vehicle has been powered off, but interior lights are powered for 30 mintes.

If I set an interval of 1000, and a timer process uses that interval to count up 5 minutes, then a rollover anomaly would have almost zero impact as it would only truncate a single 1000ms interval out of 300,000.

Does this sound reasonable and/or correct?

If you use unsigned variables and subtraction to determine whether the period has ended there will not be an anomaly at rollover

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

Got it. That was the missing piece for me. How subtraction is handled in an unsigned rollover.

Thank you very much.

It is simple.
Lets a clock for example.
If your interval start at 58 minutes of an hour, but end at 12 minutes of next hour, so the difference between times wion't (- 46) minutes (12 - 58) and even not a 46 minutes (absolute value of 12 -58), but 14 minutes.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.