NTPClient update problem

Hi everyone,

I'll get straight to the point: on startup the RTC is set by calling a NTPCLient with offset. This works fine everytime. After 24h the same client is called for an update with the same parameters to update the RTC. However now the RTC is set without (it would seem) the gmtOffset.

What's happening?

The full code is over 2000 lines, if required I can post that as well, but I think it should be in this part of the code:

//NTP and RTC 
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
NTPClient ntpClient(Udp, "nl.pool.ntp.org"); //, 3600); moved to gmtOffset
const long gmtOffset = 3600; // Offset from UTC in seconds (3600 seconds = 1h) -- UTC+1 (Central European Winter Time)
unsigned long daylightOffset = 0; // 0 for wintertime 3600 for summertime
unsigned long nextUpdate;
unsigned long printNow;
bool setRTC = false;

TimeChangeRule mySTD = {"EST", First, Sun, Nov, 2, 60};     // Standard time = UTC +1 hours
TimeChangeRule myDST = {"EDT", Second, Sun, Mar, 2, 120};    // Daylight saving time = UTC +2 hours
Timezone myTZ(myDST, mySTD);

void setup() {
  Serial.begin(115200);
  delay(1000);    
  ntpClient.begin();   
}

void loop() {

  if (ArduinoCloud.connected() && setRTC == false) {  // Set RTC once on startup
      updateTime();
      Serial.println("RTC is set");
      setRTC = true;
    }

    if (millis() - nextUpdate >= 60 * 60 * 24 * 1000) {  // Update RTC every 24hrs
      updateTime();
      nextUpdate = millis();
    }
}

void updateTime() {

  ntpClient.update(); 
  setDST();

  const unsigned long epoch = (ntpClient.getEpochTime() + gmtOffset + daylightOffset);  
  
  set_time(epoch);
  //Serial.println(getLocalTime());
}

void setDST() { 
  
  time_t utcTime = now(); // Get current UTC time
 
  TimeChangeRule *tcr;   // Convert UTC time to local time
  time_t localTime = myTZ.toLocal(utcTime, &tcr);
    
  if (tcr == &myDST) { //check TimeChangeRule
    daylightOffset = 3600; 
  }
  if (tcr == &mySTD) { 
    daylightOffset = 0; 
  }  
}

The code is running on an Arduino Giga. I've read the timezone library might not work correctly with the mbed of the giga but it is purely used to determine daylight saving time. I'm not sure yet if it works but regardless, it should not influence the gmtOffset or set_time(epoch);

I was using NTPClient ntpClient(Udp, "nl.pool.ntp.org", 3600) before, but with the same result. After 24 hours my clock goes back to UTC time.

Can anyone spot what I'm missing?

I posted this in the French tutorial part of the forum, the code is pretty self explanatory. May be that could give you some ideas? (it's for the ESP32, not sure if the Giga has the same WiFi callbacks capabilities)

These two statements may not be doing what you perhaps might think they are. TimeChangeRule is a struct and tcr is a pointer holding the memory address of that struct. These statements compare the value of the pointer tcr with the addresses of the structs called myDST and mySTD. The results will always be false since the addresses of where each struct is held in memory are different. Comparing the content of structs is a little more complex:

^comparing structs

However, possibly all that is needed is to compare the value of the utcTime with the derived and possibly adjusted value held in localTime?

if (utcTime == localTime) {
  daylightOffset = 0;
}else{
  daylightOffset = 3600;
}

If the two values are not equal then a daylight offset must have been applied.

@J-M-L
I'm having trouble getting the esp_sntp.h library to work. I'm using the IDE library manager and installed the only hit (ESPPerfectTime.h) but this does not compile because it's missing something else. I'm now trying to tie in the file from the esp-idf/components/lwip/include/apps/esp_sntp.h at v5.2 · espressif/esp-idf · GitHub link but it seems I need to install the whole esp-idf library?

@BitSeeker
I was indeed unaware of this. Back to the drawinboard :sweat_smile:

But if I compare utcTime to localTime, and I might have this backwards, won't that require me to set DST before knowing if DST should be applied or not? Since etcTime will never account for DST, that would mean my localTime would be the indicator. But how would it be adjusted for DST unless I tell it to?

Unless the RTC/localTime does indeed know and automatically apply DST, and I have been oblivious to this and wasting many, many hours trying to do something pointless...

My understanding is that once a timezone rule has been set up, as is the case at the start of your code, then .toLocal() will apply that timezone rule to the supplied time value and therefore return the corresponding local time value. I might, of course, be wrong....

interesting ....

so then if I get epoch time from the ntpClient.update() and change that to localtime with the timeZone .toLocal it should be all I need.

something like (i'm being hasty and posting my idea before testing)

void updateTime() {

  ntpClient.update(); 

  const unsigned long epoch = ntpClient.getEpochTime();  
  
  set_time(epoch);
  //Serial.println(epoch);

  time_t utcTime = now(); // Get current UTC time from the RTC
 
  TimeChangeRule *tcr;   // Convert UTC time to local time
  time_t localTime = myTZ.toLocal(utcTime, &tcr);  
  //Serial.println(localTime);

}

I'll starting testing this

May be it’s not compatible with the Giga

After a few hours of fiddling I've landed on the un-elegant workaround of:

void updateTime() {

  ntpClient.update(); 
  const unsigned long epoch = ntpClient.getEpochTime();    
  set_time(epoch);
  delay(100);
  //Serial.println(epoch);  
  time_t localTime = myTZ.toLocal(time(NULL), &tcr);    
  set_time(localTime);
  //printDateTime(localTime, tcr -> abbrev);  //from timezone.h examples
}

So many things didn't work that I came to the conclusion the simplest way would be to set the RTC with the unix time, convert it with myTZ.toLocal() and immediately overwrite the RTC again.

I don't know why but it makes me a little sad to implement it like this. I lack the skill to find a better solution but if it works, it works. I'll get over it eventually :sweat_smile:

Edit: actually it wasn't that hard :joy:

ntpClient.update(); 
  const unsigned long epoch = ntpClient.getEpochTime();    
  time_t localTime = myTZ.toLocal(epoch, &tcr);   
  set_time(localTime);
1 Like

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