Getting current time from NTP Servers


Advertisements

In IoT devices, the timestamp becomes an important attribute of the packet exchanged between the device and the server. Therefore, it is necessary to have the correct time on your device at all times. One way is to use an RTC (Real Time Clock) interfaced with your ESP32. You can even use ESP32's internal RTC. Once given a reference time, it can correctly output future timestamps. But how will you get the reference time? One way is to hardcode the current time while programming the ESP32. But that is not a neat method. Secondly, the RTC is prone to drift and it is a good idea to keep providing it with reference timestamps regularly. In this chapter, we will see how to get the current time from NTP Servers, feed it to ESP32's internal RTC once, and print future timestamps.

A brief about NTP

NTP stands for Network Time Protocol. It is a protocol for clock synchronization between computer systems. In layperson terms, there is a server sitting somewhere which maintains time accurately. Whenever a client requests the current time from the NTP server, it sends back time accurate up to 100s of milliseconds. You can read more about NTP here. For ESP32, there is an in−built time library that handles all the communication with the NTP servers. Let's explore the use of that library in the code walkthrough below.

Code Walkthrough

We will use an in−built example for this walkthrough. It can be found in File −> Examples −> ESP32 −> Time −> SimpleTime. It can also be found on GitHub.

We begin with the inclusion of the WiFi and the time libraries.

#include <WiFi.h>
#include "time.h"

Next, we define some global variables. Replace the WiFi SSID and password with the corresponding values for your WiFi. Next, we have defined the URL for the NTP Server. The gmtOffset_sec refers to the offset in seconds of your timezone from the GMT or the closely related UTC. For instance, in India, where the timezone is 5 hours and 30 mins ahead of the UTC, the gmtOffset_sec will be (5+0.5)*3600 = 19800.

The daylightOffset_sec is relevant for countries that have daylight savings. It can simply be set to 0 in other countries.

const char* ssid       = "YOUR_SSID";
const char* password   = "YOUR_PASS";

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

Next, you can see a function printLocalTime(). It simply fetches the local time from the internal RTC and prints it to serial.

void printLocalTime()
{
   struct tm timeinfo;
   if(!getLocalTime(&timeinfo)){
      Serial.println("Failed to obtain time");
      return;
   }
   Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

You might be having three questions here −

  • Where is the struct tm defined?
  • Where is the getLocalTime() function defined?
  • What are the %A, %B, etc. formatters?

The struct tm is defined in the time.h file that we have included at the top. In fact, the time library is not an ESP32 specific library. It is an AVR library that is compatible to ESP32. You can find the source code at here. If you look at the time.h file, you will see the struct tm.

struct tm {
   int8_t   tm_sec; /**< seconds after the minute - [ 0 to 59 ] */
   int8_t   tm_min; /**< minutes after the hour - [ 0 to 59 ] */
   int8_t   tm_hour; /**< hours since midnight - [ 0 to 23 ] */
   int8_t   tm_mday; /**< day of the month - [ 1 to 31 ] */
   int8_t   tm_wday; /**< days since Sunday - [ 0 to 6 ] */
   int8_t   tm_mon; /**< months since January - [ 0 to 11 ] */
   int16_t  tm_year; /**< years since 1900 */
   int16_t  tm_yday; /**< days since January 1 - [ 0 to 365 ] */
   int16_t  tm_isdst; /**< Daylight Saving Time flag */
};

Now, the getLocalTime function is ESP32 specific. It is defined in the esp32−hal−time.c file. It is a part of the Arduino core for ESP32 and doesn't need a separate include in Arduino. You can see the source code here.

Now, the meaning of the formatters is given below −

/*
   %a Abbreviated weekday name
   %A Full weekday name
   %b Abbreviated month name
   %B Full month name
   %c Date and time representation for your locale
   %d Day of month as a decimal number (01−31)
   %H Hour in 24-hour format (00−23)
   %I Hour in 12-hour format (01−12)
   %j Day of year as decimal number (001−366)
   %m Month as decimal number (01−12)
   %M Minute as decimal number (00−59)
   %p Current locale's A.M./P.M. indicator for 12−hour clock
   %S Second as decimal number (00−59)
   %U Week of year as decimal number,  Sunday as first day of week (00−51)
   %w Weekday as decimal number (0−6; Sunday is 0)
   %W Week of year as decimal number, Monday as first day of week (00−51)
   %x Date representation for current locale
   %X Time representation for current locale
   %y Year without century, as decimal number (00−99)
   %Y Year with century, as decimal number
   %z %Z Time-zone name or abbreviation, (no characters if time zone is unknown)
   %% Percent sign
   You can include text literals (such as spaces and colons) to make a neater display or for padding between adjoining columns.
   You can suppress the display of leading zeroes  by using the "#" character  (%#d, %#H, %#I, %#j, %#m, %#M, %#S, %#U, %#w, %#W, %#y, %#Y)
*/

Thus, with our formatting scheme of %A, %B %d %Y %H:%M:%S, we can expect the output to be similar to the following: Sunday, November 15 2020 14:51:30.

Now, coming to the setup and the loop. In the setup, we initialize Serial, connect to the internet using our WiFi, and configure the internal RTC of ESP32 using the configTime() function. As you can see, that function takes in three arguments, the gmtOffset, the daylightOffset and the ntpServer. It will fetch the time from ntpServer in UTC, apply the gmtOffset and the daylightOffset locally, and return the output time. This function, like getLocalTime, is defined in the esp32-hal-time.c file. As you can see from the file, TCP/IP protocol is used for fetching time from the NTP server.

Once we've obtained the time from the NTP server and fed it to the internal RTC of the ESP32, we no longer need WiFi. Thus, We disconnect the WiFi and keep printing time in the loop every second. You can see on the serial monitor that the time gets incremented by one second in every print. This is because the internal RTC of ESP32 maintains the time once it got the reference.

void setup()
{
   Serial.begin(115200);
  
   //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");
  
   //init and get the time
   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
   printLocalTime();

   //disconnect WiFi as it's no longer needed
   WiFi.disconnect(true);
   WiFi.mode(WIFI_OFF);
}
void loop() {
  delay(1000);
  printLocalTime();
}

The Serial Monitor output will look like −

ESP32 NTP Sketch Output

That's it. You've learned how to get the correct time from the NTP servers and configure your ESP32's internal RTC. Now, in whatever packets you send to the server, you can add the timestamp.

References

Advertisements