Every once in a while you’ll come across an idea where keeping time a prime concern. For example, imagine a relay that has to be activated at a certain time or a data logger that has to store values at precise intervals.
The first thing that comes in your mind is to use an RTC (Real Time Clock) chip. But these chips are not perfectly accurate so, you need to do manual adjustments over and over again to keep them synchronized.
The solution here is to use Network Time Protocol (NTP). If your ESP32 project has access to the Internet, you can get date and time (with a precision within a few milliseconds of UTC) for FREE. You don’t need any additional hardware.
What is an NTP?
An NTP stands for Network Time Protocol. It’s a standard Internet Protocol (IP) for synchronizing the computer clocks to some reference over a network.
The protocol can be used to synchronize all networked devices to Coordinated Universal Time (UTC) within a few milliseconds ( 50 milliseconds over the public Internet and under 5 milliseconds in a LAN environment).
Coordinated Universal Time (UTC) is a world-wide time standard, closely related to GMT (Greenwich Mean Time). UTC does not vary, it is the same world wide.
NTP sets the clocks of computers to UTC, any local time zone offset or day light saving time offset is applied by the client. In this manner clients can synchronize to servers regardless of location and time zone differences.
NTP Architecture
NTP uses a hierarchical architecture. Each level in the hierarchy is known as a stratum.
At the very top are high-precision timekeeping devices, such as atomic clocks, GPS or radio clocks, known as stratum 0 hardware clocks.
Stratum 1 servers have a direct connection to a stratum 0 hardware clock and therefore have the most accurate time.
Each stratum in the hierarchy synchronizes to the stratum above and act as servers for lower stratum computers.
How NTP Works?
NTP can operate in a number of ways. The most common configuration is to operate in client-server mode. The basic working principle is as follows:
- The client device such as ESP32 connects to the server using the User Datagram Protocol (UDP) on port 123.
- A client then transmits a request packet to a NTP server.
- In response to this request the NTP server sends a time stamp packet.
- A time stamp packet contains multiple information like UNIX timestamp, accuracy, delay or timezone.
- A client can then parse out current date & time values.
Preparing the Arduino IDE
Enough of the theory, Let’s Go Practical!
But before venturing further into this tutorial, you should have the ESP32 add-on installed in your Arduino IDE. Follow below tutorial to prepare your Arduino IDE to work with the ESP32, if you haven’t already.
Getting Date and Time from NTP Server
The following sketch will give you complete understanding on how to get date and time from the NTP Server.
Before you head for uploading the sketch, you need to make some changes to make it work for you.
- You need to modify the following two variables with your network credentials, so that ESP32 can establish a connection with existing network.
const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASS";
- You need to adjust the UTC offset for your timezone in milliseconds. Refer the list of UTC time offsets. Here are some examples for different timezones:
- For UTC -5.00 : -5 * 60 * 60 : -18000
- For UTC +1.00 : 1 * 60 * 60 : 3600
- For UTC +0.00 : 0 * 60 * 60 : 0
const long gmtOffset_sec = 3600;
- Change the Daylight offset in milliseconds. If your country observes Daylight saving time set it to 3600. Otherwise, set it to 0.
const int daylightOffset_sec = 3600;
Once you are done, go ahead and try the sketch out.
#include <WiFi.h>
#include "time.h"
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;
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");
}
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();
}
After uploading the sketch, press the EN button on your ESP32, and you should get the date and time every second as shown below.
Code Explanation
Let’s take a quick look at the code to see how it works. First, we include the libraries needed for this project.
- WiFi.h library provides ESP32 specific WiFi methods we are calling to connect to network.
- time.h is the ESP32 native time library which does graceful NTP server synchronization.
#include <WiFi.h>
#include "time.h"
Next, we set up a few constants like SSID, WiFi password, UTC Offset & Daylight offset that you are already aware of.
Along with that we need to specify the address of the NTP Server we wish to use. pool.ntp.org is an open NTP project great for things like this.
const char* ntpServer = "pool.ntp.org";
The pool.ntp.org automatically picks time servers which are geographically close for you. But if you want to choose explicitly, use one of the sub-zones of pool.ntp.org.
Area | HostName |
Worldwide | pool.ntp.org |
Asia | asia.pool.ntp.org |
Europe | europe.pool.ntp.org |
North America | north-america.pool.ntp.org |
Oceania | oceania.pool.ntp.org |
South America | south-america.pool.ntp.org |
In setup section, we first initialize serial communication with PC and join the WiFi network using WiFi.begin()
function.
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");
Once ESP32 is connected to the network, we initialize the NTP client using configTime()
function to get date and time from an NTP server.
//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Now we can simply call the printLocalTime()
custom function whenever we want to print current date & time.
getLocalTime()
function is used to transmit a request packet to a NTP server and parse the received time stamp packet into to a readable format. It takes time structure as a parameter.
You can access the date & time information by accessing members of this time structure.
%A | returns day of week |
%B | returns month of year |
%d | returns day of month |
%Y | returns year |
%H | returns hour |
%M | returns minutes |
%S | returns seconds |
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");
}