NTP with Day Light Saving Time for the ESP32

I didn't find a complete and simple example how to retrive NTP information including daylight saving time for the ESP32, and lot of "tutorials" for the ESP32 just hardcode the daylight saving time or use functions to determine the offset interval. But as daylight saving time is quite easy on the ESP8266, I tried to figure out, how to transfer that code to he ESP32. And as a result: You don't need any third party libraries, you don't need manual SNTP calls and you only need some lines of code for the daylight saving time.

I started with my code "NTP-TZ-DST bare minimum" for the ESP8266. It's quite simple and you can read more about on the page for the ESP8266.

The abbreviation NTP-TZ-DST bare minimum is for

  • NTP: NetWork Time Protocol
  • TZ: Time Zone
  • DST: Daylight Saving Time ("Summer time / Winter time")
  • Bare minimums: the absolute minimum that is necessary to make NTP working

The code on this page should compile fine for the ESP32 and the ESP8266 - so it has some additionals precompiler defines.

The NTP example for the ESP32/ESP8266

The following NTP sketch has some precompiler defines and is compatible for the ESP32 and ESP8266:

/*
NTP TZ DST - bare minimum
NetWork Time Protocol - Time Zone - Daylight Saving Time

Our target for this MINI sketch is:
- get the SNTP request running
- set the timezone
- (implicit) respect daylight saving time
- how to "read" time to be printed to Serial.Monitor

This example is a stripped down version of the NTP-TZ-DST (v2) from the ESP8266
and contains some #precompiler defines to make it working for
- ESP32 core 1.0.5, 1.0.6, 2.0.2  https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
- ESP8266 core 2.7.4, 3.0.2       https://werner.rothschopf.net/202011_arduino_esp8266_ntp_en.htm

by noiasca
2021-03-28 https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
*/

//#include <credentials.h>           // you could use a dedicated credentials.h file for your WIFI Password

#ifndef STASSID
#define STASSID "your-ssid"          // set your SSID
#define STAPSK "your-password"       // set your wifi password
#endif

/* Configuration of NTP */
// choose the best fitting NTP server pool for your country
#define MY_NTP_SERVER "at.pool.ntp.org"

// choose your time zone from this list
// https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
#define MY_TZ "CET-1CEST,M3.5.0/02,M10.5.0/03"

/* Necessary Includes */
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>                     // we need wifi to get internet access
#endif
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266WiFi.h> 
#endif
#include <time.h>                    // for time() ctime()

/* Globals */
time_t now;                          // this are the seconds since Epoch (1970) - UTC
tm tm;                             // the structure tm holds time information in a more convenient way *

void showTime() {
  time(&now); // read the current time
  localtime_r(&now, &tm);             // update the structure tm with the current time
  Serial.print("year:");
  Serial.print(tm.tm_year + 1900);    // years since 1900
  Serial.print("\tmonth:");
  Serial.print(tm.tm_mon + 1);        // January = 0 (!)
  Serial.print("\tday:");
  Serial.print(tm.tm_mday);           // day of month
  Serial.print("\thour:");
  Serial.print(tm.tm_hour);           // hours since midnight 0-23
  Serial.print("\tmin:");
  Serial.print(tm.tm_min);            // minutes after the hour 0-59
  Serial.print("\tsec:");
  Serial.print(tm.tm_sec);            // seconds after the minute 0-61*
  Serial.print("\twday");
  Serial.print(tm.tm_wday);           // days since Sunday 0-6
  if (tm.tm_isdst == 1)               // Daylight Saving Time flag
    Serial.print("\tDST");  
  else
    Serial.print("\tstandard");
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  Serial.println("\nNTP TZ DST - bare minimum");

  #ifdef ARDUINO_ARCH_ESP32
  // ESP32 seems to be a little more complex:
  configTime(0, 0, MY_NTP_SERVER);  // 0, 0 because we will use TZ in the next line
  setenv("TZ", MY_TZ, 1);            // Set environment variable with your time zone
  tzset();
  #else
  // ESP8266
  configTime(MY_TZ, MY_NTP_SERVER);    // --> for the ESP8266 only
  #endif

  // start network
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    delay(200);
    Serial.print ( "." );
  }
  Serial.println("\nWiFi connected");
}

void loop() {
  showTime();
  delay(1000); // dirty delay
}

The actual configuration is done using two pre-compilers defines. The first define is the NTP server to which you want to send the request. It is best to choose the server according to your location. In my case it is a NTP server pool in Austria:

#define MY_NTP_SERVER "at.pool.ntp.org" // set the best fitting NTP server (pool) for your location

To convert UTC to your specific local time, you use an existing time zone definition:

#define MY_TZ "CET-1CEST,M3.5.0/02,M10.5.0/03" // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv

You select the entry from the list linked at the end of the page. The example will work for CEST (Spain, France, Germany, Sweden, Italy, Poland to name a few). The entry contains all information for ESP32 (or the ESP8266) to calculate the local time including daylight saving time. In my example the daylight saving time changes in the third month (M3), the fifth week (5), on Sunday (0) at two o'clock (02). The winter time changes in the 10th month (M10), in the fifth week (5) on Sunday (0) at three o'clock (03).

The  ESP core comes with a time library which needs to be included:

#include <time.h>                   // for time() ctime()

The example sketch needs two variables:

now is used for the UNIX timestamp, the epoch. This variable holds the seconds since January 1st, 1970:

time_t now;                         // this are the seconds since Epoch (1970) - UTC

As humans are used to work with date/time in parts of days, months, years ... you can use the structure tm

tm tm;                              // the structure tm holds time information in a more convenient way

The structure is defined in the time.h. Later on we will see the member variables for the output.

In setup() you include 3 lines of code for the ESP32:

configTime(0, 0, MY_NTP_SERVER);   // 0, 0 because we will use TZ in the next line
setenv("TZ", MY_TZ, 1);            // Set environment variable with your time zone
tzset();

With these 3 lines you define your timezone an the NTP server.

You don't need more for NTP on an ESP32!

Next, we add some code to output the current time on the serial monitor. The output is done in 3 steps:

First you update the variable "now" and take over the current time stamp. With localtime_r you convert the time stamp into the structure tm. The structure contains the following important member variables:

  Member    Type  Meaning Range
  tm_sec    int   seconds after the minute  0-61*
  tm_min    int   minutes after the hour    0-59
  tm_hour   int   hours since midnight      0-23
  tm_mday   int   day of the month          1-31
  tm_mon    int   months since January     0-11
  tm_year   int   years since 1900
  tm_wday   int   days since Sunday         0-6
  tm_yday   int   days since January 1      0-365
  tm_isdst  int   Daylight Saving Time flag

* tm_sec is generally 0-59. The extra range is to accommodate for leap seconds in certain systems.

Example: With tm.tm_min you can access the minute time component. Just be careful with the month: The months are specified from 0 (January) to 11 (December) - so just ad 1 to get the month. The years start with 1900 - so just add 1900 to get the 4-digit year.

Change NTP Update Interval on the ESP32

To change the update interval you need following:

You have to include this .h:

#include "esp_sntp.h"

This include offers a function to modify the NTP polling interval. Put this function in setup() - best before you do the configTime(). The parameter takes the new NTP interval in milliseconds - but don't call it faster than 15 seconds.

sntp_set_sync_interval(12 * 60 * 60 * 1000UL); // 12 hours

This code was tested with ESP Core 2.0.2 and works accordingly.

Get Notifcation when NTP was called on the ESP32

You can define a callback function which will be called when the time was synched with NTP. You will need three components:

You have to include this .h:

#include "esp_sntp.h"

Define a callback function, for example to print to Serial:

void cbSyncTime(struct timeval *tv)  { // callback function to show when NTP was synchronized
  Serial.println(F("NTP time synched"));
}

in setup() you have to handover your function as callback function

sntp_set_time_sync_notification_cb(cbSyncTime);  // set a Callback function for time synchronization notification

This code was tested with ESP Core 2.0.2 and works accordingly.

Remarks for the ESP8266

There are a more additional information for the smaller microcontroller on this page: NTP for the ESP8266

Links

(*) Disclosure: Some of the links above are affiliate links, meaning, at no additional cost to you I will earn a (little) comission if you click through and make a purchase. I only recommend products I own myself and I'm convinced they are useful for other makers.

History

first upload: 2023-03-28 | Version: 2024-06-15