RTC getTime Understanding

Hello!

Using an unmodified RTC_NTPSync sketch example, I am able to get RTC.setTime(value) to complete the operation per expectations. The serial window displays the following (Time zone is UTC -5):

20:25:12.250 -> The RTC was just set to: 2024-07-18T01:25:12

This output is aligned with expectations.

However, when I run the following code in another sketch:

  RTC.begin();
  RTCTime savedTime;
  RTC.getTime(savedTime);

I get the following output (parsed for output):

21:03:08.832 -> Current time: 1/1/2000 - 0:0:0
The timestamp changes duly but the value returned by the getTime() call is no longer in sync.

What fundamental concept am I missing?

Thanks.
P.S. The VRTC pin receives 2.6 VDC from a battery box and GND is connected to the -ve terminal of the battery.

Timezone?

@baqwas , what the RTC library do you use?

In most libraries the function getTime() just returns the time as its return value rather than setting it to argument:

Were you successful in compiling your sketch with the statement you illustrated?

Regards.

I didn't tried because you didn't stated the library you used

I'm trying to read the docs.

I'm fine with UTC. I changed the offset in RTC_NTPSync (Arduino official example to 0) for the value retrieved from the NTP server pool. When I use getTime() in a subsequent sketch, the returned value is not in the ballpark (UTC or local TZ). Thereafter, the successive calls do not return updated values.

My basic assumption is that setTime() should have worked, and that my use of getTime() is suspect. Of course, since I'm using an unmodified RTC_NTPSync, I am assuming that setTime() is fine (and the RTC has continuous direct power between the running of the two sketches).

Regards.

The timestamp at the start of each line in the Serial Monitor is the computer's clock, not the MCU's.

It seems that after calling RTC.begin, you must also call setTime or the clock does not actually start "ticking". This is implied in the RTC_PeriodicExample example

  // Initialize the RTC
  RTC.begin();

  // RTC.setTime() must be called for RTC.setPeriodicCallback to work, but it doesn't matter
  // what date and time it's set to

With the first restart (with begin but no setTime), getTime would constantly return the last time that had advanced on the clock (after being set via NTP); but on the next restart, it "zeroed" to January 2000 by itself.

That was the whole purpose of including the timestamp: to illustrate that despite having set the UNO R4 RTC using the NTP sketch, the disparity ensued. :grinning:

It seems that after calling RTC.begin, you must also call setTime or the clock does not actually start "ticking". This is implied in the RTC_PeriodicExample example

That's the confusion in my simple mind. The RTC uses the "cooked up" time ensuring the ensuing control statements after the instantiation. A little more documentation would help to clear my feeble mind. Thanks.

Regards.

Hello @kenb4!

Tried your suggestion:

  • Used RTC_NTPSync example sketch to set the time; I used 0 for the time offset
  • Ran another sketch as follows:
  RTCTime startTime,  currentTime;
  RTC.begin();
  RTC.setTime(startTime);  // startTime is unknown when passed to setTime
  RTC.getTime(currentTime);
  // print currentTime

The serial monitor displays the following:

08:28:44.995 -> The RTC was just set to: 2024-07-18T13:28:44

08:29:16.563 -> 1/15/2016 - 0:0:21

The setTime() from the RTC_NTPSync example has the correct value (per timestamp) but the getTime(currentTime) from the secondary sketch is not in the ballpark. What changes do I need to make to ensure that subsequent sketches can use the RTC (assuming no disconnection in RTC power supply)? Thanks.

Regards.

P.S.
Since I use RTC HATs extensively on SBCs, perhaps my understanding of their use in MCUs requires more learning on my part?

No, the operation of the RTC here is pretty lame. You have to begin it, and then set it.

The internal fields of an RTCTime are initialized as 2020-01-01 00:00:00, so without doing anything else, when it gets converted to String to print, that's what you see. But the internal struct tm, which is

class RTCTime {
    private:
    stime_t stime;

is not initialized. (Its effective value can be seen with getUnixTime.) If passed to getTime

bool RTClock::getTime(RTCTime &t) {
    struct tm present;
    if(is_initialized) {
        if( getRtcTime(present) ) {
            t.setTM(present);
            return true;
        }
    }
    return false;
}

nothing happens to it if the RTC is not initialized

bool RTClock::begin() {
    if(openRtc()) {
        is_initialized = true;
    }
    else {
        is_initialized = false;
    }
    return is_initialized;
}

by "opening" successfully. And as we've seen, the clock does not "tick" until it has been set. If you care that the time is the actual time -- instead of just using the RTC to fire periodically -- then you need to set the actual time, which has to come from somewhere. So that's

  • manually, like a (non-smart) watch, microwave, or VCR
    • e.g. fun exercise: attach a microphone and write code to detect clapping out the time in Morse code
  • over WiFi or Ethernet
    • with an internet connection, NTP is the obvious choice
    • any decent web server response contains a Date header, which you can parse and then the clock will be off by a second or so
    • some more custom pull or push on your local intranet

which has to be done every time the MCU restarts. Lame.

But as I mentioned earlier, the time (from NTP) set on the RTC survived one restart. In fact, with the loop printing the time, the first time after restart was several seconds after the last time shown. The RTC is ticking up until it the next begin, at which point it stops. With that in mind, how about "reasserting" the time at startup, as with this modification of RTC_NTPSync

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

  RTC.begin();
  RTCTime timeToSet;
  RTC.getTime(timeToSet);
  if (timeToSet.getYear() < 2024) {
    Serial.print("\nIt's probably not ");
    Serial.print(timeToSet);
    Serial.println("\nStarting connection to server...");
    connectToWiFi();
    timeClient.begin();
    timeClient.update();

    // Get the current date and time from an NTP server and convert
    // it to UTC +2 by passing the time zone offset in hours.
    // You may change the time zone offset to your local one.
    auto timeZoneOffsetHours = 2;
    auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600);
    Serial.print("Unix time = ");
    Serial.println(unixTime);
    timeToSet = RTCTime(unixTime);
  } else {
    Serial.print("\nContinuing with existing RTC time ");
    Serial.println(timeToSet);
  }
  RTC.setTime(timeToSet);
}

void loop(){
  RTCTime currentTime;
  RTC.getTime(currentTime); 
  Serial.println(currentTime);
  delay(1000);
}

I even made the connectToWiFi conditional. If you use WiFi anyway, then leave it where it is. Anyway, the check condition < 2024 is pretty simple. Can the RTC return a bogus time in the future? It seems that after a power cycle, it returns a proper 2000-Jan-1.

Also, on my particular R4, the RTC is not good at keeping time. Here's the initial time from NTP

12:24:44.873 -> Unix time = 1721337884
12:24:44.873 -> 2024-07-18T21:24:44

after restart, a minute later

12:25:59.854 -> 2024-07-18T21:26:00
12:26:00.886 -> 2024-07-18T21:26:01

After running for 44 minutes

13:10:00.238 -> 2024-07-18T22:10:53

the RTC has advanced 53 extra seconds. So if the time is important, you may need to do regular NTP syncs anyway.

1 Like

Thanks a bunch for going the distance, @kenb4. It is beginning to make sense. I am experiencing the readings exactly as you describe when I don't make the initial setTime() call.

I'll have a ublox GPS module (no, not the M6!) attached to this specific board. So I should be able to get a good value for time. If I have to use additional R4 boards, I'll follow your advice and probably make NTP calls. Thanks again.

Regards.

P.S.
Yes, I know that the initial GPS fix may take a long time.

Thanks, @Delta_G!

I'll tinker with your suggestions but as you can understand from my initial query I'll have to tinker a lot!

Regards.