Getting UTC time with ms resolution

I need to get UTC with millisecond resolution, which shouldn’t be hard because the RTOS operates on a ms resolution and this timer is what updates Time.now(). But somehow it seems to be quite challenging-- or at the very least undocumented-- to get the ms resolution.

As a temporary stand-in, I have written the kludge below. This is a dumb function because it totally ignores all kinds of edge cases with millis(), such as the fact that it does not update while the device is asleep, and it rolls over after 49 days, etc…

uint64_t getUtc_ms()
{
   // Get timestamp
   uint32_t tv_msec = millis();
   time_t tv_sec = Time.now();

   // Convert timestamp into UTC, in [ms]. This is a slow operation, do
   // it separately from capturing the timestamp
   uint64_t utc_ms= tv_sec * 1000UL + (tv_msec % 1000);

   return utc_ms;
}

How can I do a better job of getting the internal state of the counter which Time.now() must depend on?

The native resolution of the RTC is seconds, not matter how quickly you ask for the time.
If you need milliseconds you'd need to synchronise with the flip of the second in the RTC, record the millis() and combine the two.

You won't need to account for that as unsigned subtractions won't care about the roll-over.

However, as millis() is only derived off the system ticks via an interrupt you might lose some milliseconds over time, but then the RTC is also not precise to the millis over extended periods.

What do you need that precision for?
Can you use GPS time?

1 Like

Oh, is there a hardware RTC implementation used in the nrf52840? I had thought that it was a software thing.

That's not exactly true in our case. The roll-over causes millis() to have non-monotonic behavior. In particular, it makes the milliseconds component decrement by 296.

At little outside the scope of this conversation, but it would be nice if millis() rolled over at 4294967000, instead of 4294967296.

Event tracking. We some interrupt events occur more quickly than 1Hz. Stamping them with milliseconds gives us the resolution we need to disambiguate. However, the events are published with a UTC timestamp, so we can't simply rely on millis().

Alas, no, there's no GPS available. Architecturally that would be hard because when the device isn't communicating, it's sleepy and tries to spend as little time awake as necessary to service the interrupt.

I see, you are using a Gen3 device - on those it's not a Real Time Clock but merely a Real Time Counter which I don't know a lot about, but since the Time object originally was tailored for the Real Time Clock on the STM32F devices even with a (maybe?) higher resolution on Gen3 that object wouldn't give you any more.

That's not possible as it would break tons of other exisiting code that relies on a binary bound roll-over.
But you should be able to accommodate for that by adding the missing 296 (or 704 however you see it) when you detect prevMillis > millis().

However, since the nRF RTC is quite prone to drifting (and even more so millis()) while sleeping, you could keep updating your millis() base line regularly (e.g. whenever you resync the Time base) and only ever use the difference (millis() - msBaseLine) as your disambiguation (fake) "milliseconds". That way you'll never have a decimal roll-over in your calculation (unless you resync only every 49+ days :wink: ).

Yup. It's also not great in practice because it'd be absurd to encumber the loop with an if then every single time. millis() is beautiful in its simplicity. But for my case, I'd still like it if it wrapped at a 000 value.

Yeah, I'm doing something like that. It's just not pretty code and TBH there's no real hope it's more accurate because I don't know how to sync it to Time.now(). My hope is that the nRF's real-time counter can be accessed on some level so that I can extract the data from it.

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