Future time stamps with millis()

Howl with rage if you like! Future time stamps do make the logic more straightforward. Here I've just created a millis() like function that uses 'unsigned long long' type to postpone overflow. Please only post admiration and awe. No criticism. Just kidding.

/*
  overflow-safe Blink without Delay
  2022-12-17 Aarg on Arduino forum

Adapted from:
  https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
*/

// introduce a millis() replacement safeMillis()
// allows future time stamps by postponing overflow
// until the restaurant at the end of the universe closes

typedef unsigned long long millis_t;

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

millis_t nextMillis = 1000;        // will store next time LED should be updated

// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)

// This free running counter function won't overflow anytime soon.
millis_t safeMillis() {
  static millis_t safeCounter = 0;
  static unsigned long nextMillis = 0;
  while (millis() - nextMillis > 0) {
    nextMillis++;
    safeCounter++;
  }
  return safeCounter;
}

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  millis_t currentMillis = safeMillis();

  // check to see if it's time to blink the LED; that is, if the current time
  // has passed the future time stamp
  if (currentMillis > nextMillis ) {
    // set the next time you will blink the LED
    nextMillis = currentMillis + interval;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}
3 Likes

// 64 bit
typedef unsigned long long millis_t;

I was going for a bowing giphy but it was too big.

Give my people a call and they'll take care of it.

Running a test as we speak:
:thinking:

1 Like

Well, the Watts are Hi! Should run for millenia...

Forum category descriptions are not always a good-fit. Just flag and ask a moderator to validate post category.

It is more of a demo, I see the problem but then what sub category does it actually belong in? I had a look and can't find a good match.

Ah, okay, but I always understood that to mean, "asking for advice on..."

1 Like

Am I missing something here?

Yes, I should have commented it. In truth I pushed this out way too hastily. Also the name I gave the variable sucked.

  // every millisecond, increment the 64 bit counter:
  while (millis() - prevMillis > 0) {
    prevMillis++;
    safeCounter++;
  }

No, I mean that condition can only cause the while loop to exit when the left hand expression is equal to zero.
A one in four billion chance, no?

What is the problem with future timestamps anyway? (I mean ones that aren't more than seven weeks+ in advance)

No. The chances are actually strongly in favour, since most of the time a millisecond has not elapsed since the last check. A check is performed each time through loop().

And, the code is tested and running on an Uno...

With a 32 bit counter, future timestamps fail when millis() rolls over.

How so?
Unsigned arithmetic overflow is well-defined in C++ unlike signed overflow.

1 Like

Sure it's well defined. That's why, millis() overflow is not a problem when you use past time stamps.

What if someone wants to measure the time between button presses, and that time can be a year. If the safeMillis() is called when the button is pressed, then the result is wrong because safeMillis() has not been updated enough.

Why has safeMillis() not been updated enough? It's called each time through loop().

By the way, this is an advantage I didn't mention. Unlike millis(), safeMillis() has no effective maximum interval measurement length. So it can time years, decades etc.

It's undefined how often it is updated.
It depends what the rest of the code is doing.

No. It's perfectly well defined, as long as it is called unconditionally from loop(). That is why I used 'while' instead of 'if'. In some case where significant time elapses before the function is called again, the 'while' loop will run repeatedly and the time stamp is incremented until it is fully up to date.

That will happen any time the function is called, so it is guaranteed to be up to date, any time you request the time.

If you don't call it, it will lose sync, but that doesn't matter because in that case you are not asking it for the time.

Besides that, while using millis() or safeMillis() timing, any code that prevents the loop from executing at speed, is a bad programming practice and you deserve what you get if you do it.