ESP32 (17) – SNTP

luca 11/05/2017 2

In embedded applications it’s often necessary a clock, as accurate as possible. For example, imagine a device that has to activate an output at a given time or a logger that has to store values (temperature…) at precise intervals.

A solution widely adopted is the use of an RTC (Real Time Clock) chip. The problem becomes how you configure the RTC chip with the current time and how you keep it synchronized. Some time ago, I published a project (RTCSetup) to configure an RTC connected to an Arduino using the time of your PC; today I’ll show you how to use an Internet time service with the esp32 chip.

SNTP

SNTP (Simple Network Time Protocol) is a protocol designed to synchronize the clock of devices connected to the Internet. The latest version of the protocol (SNTPv4) is defined in the RFC 4330. The word “simple” in its name is because of SNTP is a reduced and simplified version of the NTP (Network Time Protocol) protocol; protocol which, to be able to guarantee high accuracy, is quite complex.

The basic working principle of SNTP is as follows:

- the client device connects to the server using the UDP protocol on port 123

- the server answers with different fields, among which is the timestamp value, 64bit long, where:

  • the first 32 bits are the number of seconds from 01/01/1990
  • the remaining 32 bits are the fraction of seconds

sntp-00

esp-idf

In a previous post I’ve already introduced the lwip library, chosen by the esp-idf framework to implement network communcation. The library includes an app (sntp.c) which implements a SNTP client.

Let’s see how to use it in your program (the full source of the test program is available in my Github repository).

First you have to include its header file:

#include "apps/sntp/sntp.h"

Then configure the SNTP client in pool mode, to query the servers every n seconds:

sntp_setoperatingmode(SNTP_OPMODE_POLL);

Alternatively, you can configure the client in listen only mode (SNTP_OPMODE_LISTENONLY). In this configuration, the client will listen for broadcast updates, without actively query any servers.

sntp_opts.h

Some configuration settings of the client, including the update delay (in milliseconds), can be changed in the sntp_opts.h file:

sntp-01

Now you have to tell the SNTP client which server it has to use. A common choice is to use the cluster of servers from pool.ntp.org. Alternatively, because of I live in Italy, I normally use the SNTP server offered by the Istituto Nazionale di Ricerca Meterologica (ntp1.inrim.it) that has an atomic clock as its time source.

In the example provided with this post, the server name can be configured via menuconfig:

sntp_setservername(0, CONFIG_SNTP_SERVER);

After having configured the client, you can start it with:

sntp_init();

the client will be executed in parallel with the main program.

To get the actual time from the system clock, use the time() method which updates a time_t variable:

time_t now;
time(&now);

The time_t variable usually represents the time as the number of seconds from a date called Epoch (01/01/1970). To split the variable into the different time values (year, month, day…) you can use the localtime_r method which updates a tm struct:

struct tm timeinfo;
localtime_r(&now, &timeinfo);

The fields of the tm struct are defined in the time.h file:

sntp-02

We can verify if the first sync with the SNTP server was perfomed with success for example checking the year field:

while(timeinfo.tm_year < (2016 - 1900)) {
  printf("Time not set, waiting...\n");
  vTaskDelay(5000 / portTICK_PERIOD_MS);
  time(&now);
  localtime_r(&now, &timeinfo);
}
Many date and time functions have two versions, one “normal” and one restartable (es. localtime and localtime_r). In a multithread environment like the FreeRTOS operating system is always suggested to use the functions with the _r suffix because they can be executed in parallel (reentrancy) as they don’t use common buffers.

If the struct tm is available, you can then use the strftime() method to format a string with the pattern you prefer. Many placeholders are available: they are described in the manpage of the method. Here are some examples:

char buffer[100];
// es. 08/05/2017 15:10:34
strftime(buffer, sizeof(buffer), "%d/%m/%Y %H:%M:%S", &timeinfo);
// es. Monday, 08 May 2017 
strftime(buffer, sizeof(buffer), "%A, %d %B %Y", &timeinfo);
// es. Today is day 128 of year 2017 
strftime(buffer, sizeof(buffer), "Today is day %j %Y", &timeinfo);

Time zone

SNTP servers keep the time with the UTC (Coordinated universal time) time zone. You can change the time zone modifying the TZ environmental value with the method:

int setenv(const char *name, const char *value, int overwrite);

name is the name of the variable and value the value for it. If overwrite is > 0 the method updates the variable if already exists.

Once the value for the TZ variable is set, you can initialize the timezone conversion routine with the tzset() command.

The TZ variable can have two different kind of values, depending on whether you want to handle daylight saving time or not.

In the simplest form (no daylight saving time) the format is std offset, where std is the name for the time zone (3 or more characters long) and offset specifies the time value you must add to the local time to get a UTC value. For example Italy is in the CET (Central European Time) timezone, which is one hour ahead from UTC (therefore offset -1). The command to use in Italy is:

setenv("TZ", "CET-1", 1);

If you want to handle the daylight saving time, you can use the format std offset dst [offset],start[/time],end[/time]std and the first offset have the same meaning as above. dst and the second offset specify the name and offset for the corresponding daylight saving time zone (if the second offset is omitted, the default is 1 hour ahead of standard time). Finally, start and end are the day and time when daylight saving time starts and ends.

For Italy, in the daylight saving time we use the CEST (Central European Summer Time) time zone, active (direttiva 2008/84/CE del Parlamento Europeo) from 02:00 of the last Sunday of March to 03:00 of the last Sunday of October.

The command is therefore:

setenv("TZ", "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", 1);

M3.5.0 specify March (3), last week (5), Sunday (0).

On the Internet you can find websites that lists the TZ values for different countries.

In conclusion, if you want to convert the time to a given timezone the steps are:

  • configure the TZ  variable (setenv())
  • inizialize the timezone conversion routine (tzset())
  • update the tm struct (localtime_r())

In the example program:

// print the actual time in Italy
printf("Actual time in Italy:\n");
localtime_r(&now, &timeinfo);
strftime(buffer, sizeof(buffer), "%d/%m/%Y %H:%M:%S", &timeinfo);
printf("%s\n", buffer);

sntp-03

2 Comments »

  1. Daniel 01/09/2017 at 14:32 - Reply

    Hi Luca,

    I’m new to the ESP32 and I’ve used the example SNTP code to synchronise the local time on an ESP32 with the time on a Raspberry Pi, which I’ve set up as an NTP server (this is because I need to synchronise multiple ESPs’ clocks offline). In order to test if the ESP clock remains in sync with the Pi, I’ve changed SNTP_UPDATE_DELAY to its minimum of 15 seconds, and manually adjusted the time on the Pi (i.e. added 30 mins to the current time), however I see no change in the local time on the ESP, even after waiting a while. After rebooting the ESP it syncs with the new time. Do you know how I could get it to re-synchronise on its own? Is there a particular function I can run to do this after the SNTP client has initially been set up?

    Thanks!

    • luca 07/09/2017 at 13:37 - Reply

      Hi Daniel… it should work without any explicit call to the sntp “module”… you may run a tcpdump on the Raspberry to “see” if the esp32 chip correctly request the time every 15 seconds, otherwise it might be a bug of the framework

Leave A Response »

Click here to cancel reply.

This website uses cookies to ensure you get the best experience on our website maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi