ESP32 Get regular updates from NTP

For sometime I have been using an ESP32 where I need accurate time information. I have been using the ESP32 time.h functions. I was not aware until recently that once the ESP32 has synchronised with the NTP time the time functions rely on the internal ESP32 RTC and if not updated from time to time can drift from the NTP time. I have modified the ESP32>Examples>Time>Simple Time example to include a method to regularly synchronise the ESP32 RTC to NTP time. I have also included an example of how to configure the TimeZone and daylight savings time information using the method ConfigTzTime(); The timedelay is purely for testing. Longer times of 12 hours would be sufficient.

// Modified ESP32 Simple Time Example 
// To demonstrate how to set the time zone information using ConfigTzTime() function ( I am based in UK and on GMT? so to demonstrate the timezone function I have set it for CET (UTC+1);
// and to demonstrate how to obtain an SNTP time update at regular intervals
// 

#include <WiFi.h>
#include "time.h" // included with Arduino ESP32 package
#include "sntp.h" // included with Arduino ESP32 package

const char* ssid       = "**********";//your ssid
const char* password   = "********";// your password
// addresses of NTP servers
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";// more than one can be used 

const char* TZ_CET = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone rule for Europe/Rome including daylight adjustment rules
const char* TZ_UK = "BST0GMT,M3.5.0/1,M10.5.0";  // Timezone rule for UK including daylight offset rules

void printLocalTime()// routine to get the local time and print it in day date and time format
{
   struct tm timeinfo; // used to strore the latest time/date information used with time.h functions
  // function to get the system time and print it in date and time format
  if(!getLocalTime(&timeinfo)){
    Serial.println("No time available (yet)");
    return;
  }
  Serial.print("Local Time: ");Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

// Callback function (gets called when time adjusts via NTP) 
//triggered by sntp_set_time_sync_notification_cb( timeavailable );
void timeavailable(struct timeval *t)
{
  Serial.println("Got time adjustment from NTP!");
  //Put any code in here that you want executed when NTP updates
  //e.g.
  //printLocalTime();
}

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

  // set notification call-back function
  sntp_set_time_sync_notification_cb( timeavailable );// tiggers function timeavailable when SNTP update has taken place
  /**
   * NTP server address could be aquired via DHCP,
   *
   * NOTE: This call should be made BEFORE esp32 aquires IP address via DHCP,
   * otherwise SNTP option 42 would be rejected by default.
   * NOTE: configTime() function call if made AFTER DHCP-client run
   * will OVERRIDE aquired NTP server address
   */
  sntp_servermode_dhcp(1);    // (optional)
  /**
   * Thde method to handle TimeZones with daylightOffset 
   * is to specify a environmnet variable with TimeZone definition including daylight adjustmnet rules.
   * A list of rules for your zone could be obtained from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
  * e.g 
  * 
  */
  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  Serial.println(" CONNECTED");
  //set the SNTP update time interval
  // The following line will request an SNTP update at regular intervals (time in milliseconds - minimum 15s)
  sntp_set_sync_interval(60*60*1000); //set time period after which a snpt server request for time update is made. 
//NOTE: This method could be used if the default sync interval of 3Hrs needs to be changed.
  // Set the timezone and NTP server information
  // this is the most convenient method of setting the timezone information and the address of the server
  // and will connect to the server
  configTzTime(TZ_CET, ntpServer1);
}

void loop()
{
  
  time_t time_now;// 
  time(&time_now);// get the current time from the system
  Serial.print("epoch time: ");Serial.println(time_now);// seconds passsed since 1st Jan 1970 (UNIX TIME)
  struct tm gmtimeinfo; // structure to hold the GMT equivalent time
  gmtime_r(&time_now,&gmtimeinfo);// time.h function to obtain the current UTC/GMT calendar time
  Serial.print("UTC Time: ");Serial.println(&gmtimeinfo, "%A, %B %d %Y %H:%M:%S");
  printLocalTime();     // it will take some time to sync time :)
  delay(5000);
}

the output is: (I am based in the UK hence time on the left is GMT)
14:36:07.358 -> Got time adjustment from NTP!
14:36:12.353 -> epoch time: 1736346972
14:36:12.353 -> UTC Time: Wednesday, January 08 2025 14:36:12
14:36:12.353 -> Local Time: Wednesday, January 08 2025 15:36:12
14:36:17.335 -> epoch time: 1736346977
14:36:17.335 -> UTC Time: Wednesday, January 08 2025 14:36:17
14:36:17.335 -> Local Time: Wednesday, January 08 2025 15:36:17
14:36:22.354 -> epoch time: 1736346982
14:36:22.354 -> UTC Time: Wednesday, January 08 2025 14:36:22
14:36:22.354 -> Local Time: Wednesday, January 08 2025 15:36:22
14:36:22.391 -> Got time adjustment from NTP!
14:36:27.352 -> epoch time: 1736346987
14:36:27.352 -> UTC Time: Wednesday, January 08 2025 14:36:27
14:36:27.352 -> Local Time: Wednesday, January 08 2025 15:36:27

as far as I know the default update time is 1h anyway.
15 secs is a really overkill. Please modify your example to something more reasonable.

1 Like

I note that your local time appears to be on BST (GMT + 1 hour daylight saving).
I have the same issue with my ESP32s using NTP in the UK. They only show the correct time when we are in the daylight saving period.
I've not bothered trying to fix it, because the only way I can think of testing a fix (other than waiting for months) is to have a local NTP server that I can use for testing. If / when you get around to fixing it, I'd be interested to know how you did it.

Not necessary, this is already done automatically by the library. As @noiasca mentioned, this is every hour, by default. 15s is too frequent and could be considered abuse by the NTP servers.

@fairfield87 is using "TZ_CET" in his example. If you change that to the other, unused setting "TZ_UK", it will switch time zone automatically between BST and GMT.

1 Like

According to

the correct timezone definition for Europe/London is

Europe/London	GMT0BST,M3.5.0/1,M10.5.0
2 Likes

Which @fairfield87 's code has but doesn't use:

Thank you for that. I'll change it when I work up the motivation to rebuild code that's a few years old and from many projects ago.
I do remembering Googling a lot, but I don't remember where I got this from, nor how much I changed whatever I started from.

int Ntp::init()
{
  // this doesn't return anything so can't check
  // configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  configTime(0, 0, ntpServer); // Can use 0,0 because using TZ below now
  setenv("TZ","GMTGMT-1,M3.4.0/01,M10.4.0/02",1); // London
  // setenv("TZ","CET-1CEST,M3.5.0,M10.5.0/3",1); // Paris - can use this to check time is +1 hour
  tzset();
  initialised = true;
  return SUCCESS;
}

that's my "NTP bare minimum" version:
https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm

1 Like

This has AFAIK been changed to 3 hours.
Leo..

Hi noisca - the 15s was for testing purposes. I have changed the code for a longer period. Interesting about the default 1 hour I will check it out.

Hi noisca

The 1, in GMT0BST,M3.5.0/1,M10.5.0 is not required as the default value for daylight saving is 1 hour

Hi Dave, I am in the UK but to test the localtime() and gmtime() functions I configured the timezone to CET which is UTC+1. So print local time is one hour ahead of where I am. I don't think it matters where the NTP time servers are based as they work on UTC/UNIX time and then adjusted to the Time Zone information.

When I said "local NTP server", I meant my own NTP server that I could mess with to set the date / time the server sends. Then I could set it to the time just before a change from GMT to BST or vice-versa, and see if the ESP code correctly switches time by +/- 1 hour. Sorry for not being clear.

Thanks for the correction -

Thanks I removed the sntp_set_sync_interval(interval in ms) function and got the following result:
12:37:51.931 -> Connecting to ********** .. CONNECTED
12:37:53.622 -> Got time adjustment from NTP!
12:37:53.622 -> Local Time: Thursday, January 09 2025 13:37:53
15:04:15.376 -> Got time adjustment from NTP!
15:04:15.376 -> Local Time: Thursday, January 09 2025 16:04:15

So the default SNTP up interval is about 2.5 hours

Which core Version are you using?

v 2.0.17

you are right. its 180 minutes tested with

Serial.print("Update Interval="); Serial.println(sntp_get_sync_interval());

That's actually my ESP32 doing with Core 2.0.16

11:11:23.136 -> NTP TZ DST - bare minimum
11:11:23.136 -> Update Interval=10800000
11:11:23.419 -> ......
11:11:24.435 -> WiFi connected
11:11:24.435 -> year:1970 month:1 day:1 hour:1 min:0 sec:1 wday:4 standard
11:11:26.892 -> NTP time set
11:12:24.410 -> year:2025 month:1 day:11 hour:11 min:12 sec:22 wday:6 standard

14:11:24.176 -> year:2025 month:1 day:11 hour:14 min:11 sec:22 wday:6 standard
14:11:26.774 -> NTP time set
14:12:24.154 -> year:2025 month:1 day:11 hour:14 min:12 sec:22 wday:6 standard

17:11:23.864 -> year:2025 month:1 day:11 hour:17 min:11 sec:22 wday:6 standard
17:11:26.578 -> NTP time set

20:11:23.614 -> year:2025 month:1 day:11 hour:20 min:11 sec:22 wday:6 standard
20:11:26.381 -> NTP time set
20:12:23.608 -> year:2025 month:1 day:11 hour:20 min:12 sec:22 wday:6 standard

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