Arduino RTC does not stay in sync despite use of ArduinoCloud.getLocalTime();

Hi, I use an arduino R4 wifi to display messages on the led matrix. But every 6 messages, I want it to be used as a clock and request it to display the current time, which comes from the ArduinoCloud.getLocalTime() function.
Before each run (so roughly 1x/minute or so) , the time is imported via the ArduinoCloud.getLocalTime() function.
It seems to work well in the beginning but after a few hours I see that the time is no longer in sync, experiencing a drift of approx 10 minutes a day ( the arduino clock ticking too quickly). I read in other posts that the R4 RTC clock is far from super accurate, but I thought that importing new time from the ArduinoCloud.getLocalTime(); function - instead of relyin gon the internal RTC - would solve this.

Here the part of the code where the time is set:

if (compteur%6==0){
    time_t UnixTime;
    UnixTime = ArduinoCloud.getLocalTime();
    String SentTime = ctime(&UnixTime); 
    SentTime=SentTime.substring(4,19); 
    XXXTimeToSendXXX="     "+SentTime+"    ";
     MatrixWrite(XXXTimeToSendXXX);
 } 

Any hint on the source of the issue ? Many thanks in advance

I'm having a similar problem (Also on R4 WiFi) where the value of ArduinoCloud.getLocalTime() will increment by 1 hour every time I call RTC.setTime !
I suspect this is the timezone offset (I'm GMT+1) but it's super freaky that any function can interfere with the time retrieved from the cloud.
I have tried everything to work around this but I'm out of ideas and can't solve it.

I have read this in another topic on this forum:
"All Arduino boards are using internal RTC to store the time after the first request to the NTP server"
If that's true, we are screwed with the drifting RTC in the R4 :frowning:

The comment was made by Arduino them member @pennam
Maybe he can give us a fix to override the internal RTC and get the real time from the NTP server.

Hmmm, Not sure this is the same issue, as I do not use the RTC.setTime function. The drift I encounter is a few minutes per hour, which seem unrelated to a timezone issue.

What @pennam means is that only the first call to ArduinoCloud.getLocalTime() will call the NTP server and all following calls will use the internal RTC to get the time. This will explain the drift you are experiencing.

Aie caramba. This indeed explains the issue. Is there any way to force a resync from time to time?

I'm rewriting my code to use the NTPclient.h library to sync internal RTC every minute. It's a shame that it has to be done this way but I don't see any other solution for now. My R4 WiFi RTC drifts 2 seconds each minute !

So I'm using:

timeClient.update(); // (from NTPclient.h)
currentTime = (timeClient.getEpochTime()); // (from NTPclient.h)
RTC.setTime(currentTime);  // (from RTC.h)

hi @buzznl @jlstoefs by default the rtc get syncronized with the ntp time once per day: see ArduinoIoTCloud/src/utility/time/TimeService.cpp at d436b80f099be3bbc9132248aae24c3042836446 · arduino-libraries/ArduinoIoTCloud · GitHub

you can reduce the sync period using this function:ArduinoIoTCloud/src/utility/time/TimeService.cpp at d436b80f099be3bbc9132248aae24c3042836446 · arduino-libraries/ArduinoIoTCloud · GitHub

in this way i think you can mitigate the issue with the rtc drift.

Thank you @pennam , this looks indeed promising.
Could you explain how to use the setSyncInterval() function? I tried to add a command setSyncInterval(10*60) in the setup function but the arduino editor did not enjoy it. Should I include a specific library for it to work?

Here my full sketch:

  /*
  Sketch generated by the Arduino IoT Cloud Thing "Untitled 4"
  https://create.arduino.cc/cloud/things/33b5abdd-5b4b-40e8-b57c-d5e911e30de9

  Arduino IoT Cloud Variables description

  The following variables are automatically generated and updated when changes are made to the Thing

  String SendText;
  int compteur;
  bool syncLed;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.

  Code wifi:

  GSM:
  NBB-IOS-M5W3165CFP
  qca4g2zdyddm2

  Maison:
  Mobistar-4ac4
  471088FCF66FFA672752EA365A

*/
#include "thingProperties.h"
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
#include "WiFiS3.h"



#define Buzzer D11 //buzzer pin

#define DEBUG 0 // set 1 for debugging; mremoves debuggibg code before compiling if not required
#if DEBUG ==1
#define debug(x) Serial.print(x)
#define debugln(x) Serial.println(x)
#else
#define debug(x)
#define debugln(x)
#endif

//unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
const unsigned long TextInterval = 2500; //definit le temps entre 2 messages (en millisecondes)
String XXXToSendXXX;
String XXXTimeToSendXXX;
const bool DebugToggle = 0;
ArduinoLEDMatrix matrix;


void setup() {

  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(Buzzer, OUTPUT);
  digitalWrite(Buzzer, LOW);
  Buzz();
  matrix.begin();
  //ArduinoCloud.setSyncInterval(600); //is not accepted by the arduino IDE
  initProperties();   // Defined in thingProperties.h
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);   // Connect to Arduino IoT Cloud

  MatrixWrite(F("   Booting R4 Messenger OtA v1...   "));
  Serial.println(F("Booting Messenger OtA v1..."));
  
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
}

void loop() {
  ArduinoCloud.update();
  //syncingLed();
  sendText();
}

void sendText() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis2 > TextInterval) //toutes les 10 secondes
  {

    if (compteur % 6 == 0) {
      time_t UnixTime;
      UnixTime = ArduinoCloud.getLocalTime();
      String SentTime = ctime(&UnixTime);
      debugln(SentTime);

      SentTime = SentTime.substring(4, 19);
      XXXTimeToSendXXX = "     " + SentTime + "    ";
      MatrixWrite(XXXTimeToSendXXX);
    }
    else
    {
      MatrixWrite(XXXToSendXXX);
    }

    debug(F("compteur:"));
    debugln(compteur);
    compteur++;
    previousMillis2 = millis();
    digitalWrite(LED_BUILTIN, syncLed);
    syncLed = 1 - syncLed;
  }
}

void MatrixWrite(String text) //texte qui défile
{
  matrix.beginDraw();
  matrix.stroke(0xFFFFFFFF);
  matrix.textScrollSpeed(25);
  matrix.textFont(Font_5x7);
  matrix.beginText(0, 1, 0xFFFFFF);
  matrix.println(text);
  matrix.endText(SCROLL_LEFT);
  matrix.endDraw();

  debugln(text);
}

void Buzz() {
  for (int i = 0; i < 3; i++)
  {
    digitalWrite(Buzzer, HIGH);
    delay(50);
    digitalWrite(Buzzer, LOW);
    delay(50);
  }
  debugln(F("Buzz!"));
}

void onSendTextChange()  { //qd un nouveau message est reçu...
  compteur = 0;
  debugln(SendText);
  if (SendText == "") XXXToSendXXX = SendText;
  else XXXToSendXXX = "   " + SendText + "    ";
  Buzz();
}

void onCompteurChange()  {

}






@jlstoefs

you should call TimeService.setSyncInterval(600)

@pennam Does this have to be declared prior to

or can it be declared once later on in the loop() function?

Hi @mr_ngineer TimeService is a global instance declared here:

You can change the syncInterval also after ArduinoCloud.begin(ArduinoIoTPreferredConnection);

but i would not put it in the loop() unless you need to change it at runtime.

Thanks a lot! I have added this line at the end of the setup and it indeed solved the issue.:


  ArduinoCloud.begin(ArduinoIoTPreferredConnection);  
TimeService.setSyncInterval(600);

Was this the only line you added? I experience the same issue and tried to solve it with a DS3231, but unsuccesfull. This looks much easier.

Indeed only this line did the trick

I added the line and my cloud connected clock still runs on time. Thanks... I was trying to fix it for days...

I was having a lot of trouble with the RTC and it would seem the solution lies here.

After setting the RTC using the ntpClient library (adjusting for timezone and daylight saving) the time on my project would often revert to GMT time and I'd have to call the NTP again.

Is there a way to disable the IoT TimeService? I'd like my code to keep running with my local GMT offset and DST.

@arneko sorry i miss your message. To disable the IoT TimeService should be enough to setup a custom sync function that does nothing.

unsigned long customSyncFunction() {}

void setup() {
...
TimeService.setSyncFunction(customSyncFunction);
...
}

edit: empty customSyncFunction() won't work. You shoud return your adjusted localtime.

btw you know that you can configure your thing timezone using sketch metadata?

I know, the metadata is set correctly. And that makes it all the more strange the automatic sync is 2 hours off.

I'm not sure I understand. Should I use my local time as an argument instead of customSyncFunction() ?

This is all correct with local time. And still the sync sets the clock back 2 hours.