I was looking at some very awkward code that was trying to use elapsed times longer than 49 days. It had millis() rollover detection and millis() resetting with adjustments to timer0_millis, along with a ton of delay(1000)
s. I thought that a seconds
variable would solve many of the workarounds this code used.
Consider:
// https://wokwi.com/projects/387510129380013057
// code for creating a "seconds" variable from millis()
// that won't roll over for 136 years
const long SecondsInDay = 3600L * 24;
const long SecondsInYear = SecondsInDay * 365;
// time variables to track elapsed seconds for
// 2^32/3600/24/365=136 years before secondsrollover
unsigned long seconds = 0, lastSecondMs = 0;
unsigned long currentMillis = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
}
void loop() {
if (timeStuff()) { // 1ms tick
report();
blinkLedQuarterly();
}
}
bool timeStuff(void) {
bool retval = false;
const unsigned int interval = 1;
static unsigned long last = 0;
currentMillis = millis();
if (currentMillis - last >= interval) {
retval = true;
last += interval;
if (currentMillis - lastSecondMs >= 1000) {
++seconds;
lastSecondMs += 1000;
}
}
return retval;
}
void report(void) {
const unsigned long interval = 500;
static unsigned long last = 0;
if (currentMillis - last >= interval) {
last += interval;
Serial.print("ms:");
Serial.print(currentMillis);
Serial.print(" Sec:");
Serial.print(seconds);
Serial.print(" min:");
Serial.print(seconds / 60);
Serial.println();
};
}
void blinkLedQuarterly(void) {
static unsigned long last = 0;
unsigned long interval = 3600L * 24 * 365 / 4;
if (seconds - last >= interval) {
last = seconds;
digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == HIGH ? LOW : HIGH);
}
}
... with the commented Wokwi demo at:
https://wokwi.com/projects/387510129380013057
By maintaining a seconds
variable, you can do low resolution timing with the same BWOD-like mechanism as with millis(), but with periods and intervals up to 136years rather than millis()'s ~49 day limit. By using the lastSecondsMs variable you can get millisecond precision resolution over 136 years if you like, although the Arduino clocks aren't accurate enough to make that resolution meaningful.
With seconds
you can do something like:
void blinkLedQuarterly(void) {
static unsigned long last = 0;
unsigned long interval = 3600L * 24 * 365 / 4;
if (seconds - last >= interval) {
last = seconds;
digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == HIGH ? LOW : HIGH);
}
}
If you find yourself coding around millis() for intervals exceeding 49 days, consider working in seconds instead of millis()--It's 1000x better/simpler for long durations.
(In the other code I was looking at, using seconds will simplify lots of ugliness.)