• Post comments:0 Comments
  • Reading time:7 mins read

Syncing date/time with NTP server is an universal application for any IoT projects.

ESP8266/ESP32 as the most popular IoT solutions, provide built-in WIFI function. It is fairly easy to sync date/time with NTP Server.

Especially when it comes to work with clarkwise touch screen, it has built-in RTC (real time clock) module. But you got to sync it and adjust to your time zone.

There are some tutorials online, but unfortunately most of them don’t involve timezone and daylight saving time setup. Only get UTC without any offset.

In this turorial, we will solve all the pain points, and don’t worry, it’s still easy

Bullet points:

What is NTP?

According to the defination on Wikipedia:

The Network Time Protocol (NTP) is a networking protocol for clock synchronization between computer systems over packet-switched, variable-latency data networks.

In the shorter answer, it is a way to get accurate time from the Internet and correct local clock time.

Subscribe to Our Newsletter

Sign up to receive updates/coupons/newsletters.

We don’t spam! Read our privacy policy for more info.

Install the NTP Client Library

The NTP Client is sort of the official Arduino library. To install it, open Libraries Manager. Sketch > Include Library > Manage Libraries.

Browse to Library Manager

Inside the Library Manager, we type NTPClient in the search box, and then click the install button.

Arduino IDE Library Manager Search for NTPClient
Arduino IDE Library Manager Search for NTPClient

Baisc - Obtain the time

Remember to replace the SSID and PASSWORD. We highlighted them already.

				
					#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

const char *ssid     = "<SSID>";
const char *password = "<PASSWORD>";

WiFiUDP ntpUDP;

// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP);

// You can specify the time server pool and the offset, (in seconds)
// additionally you can specify the update interval (in milliseconds).
// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

void setup(){
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }

  timeClient.begin();
}

void loop() {
  timeClient.update();

  Serial.println(timeClient.getFormattedTime());

  delay(1000);
}
				
			

Now in the Serial Monitor, the formatted time is printed every second.

You may notice some problems: 

  1. There is not date
  2. It is UTC time, and not with the correct timezone.

Now let’s make it into a real deal.

Obtain the Unix Epoch Time and format it into date and time.

Now let’s make a little change.

Instead of the formatted time, we get the Unix Epoch Time.

About what is Unix Epoch Time, please refer to Wikipedia.

And then we turn the Unix Epoch Time into time structure. By using gmtime function.

Let’s see the code now. We highlighted the changed part.

				
					#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

const char *ssid     = "SSID";
const char *password = "PASSWORD";

long getOffset();

WiFiUDP ntpUDP;

// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP);

// You can specify the time server pool and the offset, (in seconds)
// additionally you can specify the update interval (in milliseconds).
// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

void setup(){
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }

  timeClient.begin();
  timeClient.setTimeOffset(getOffset());
}

void loop() {
  timeClient.update();

  time_t epochTime = time_t(timeClient.getEpochTime());
  struct tm *ptm;
  ptm = gmtime ( &epochTime );

  Serial.print(String(ptm->tm_year+1900)); //years since 1900
  Serial.print("-");
  Serial.print(ptm->tm_mon+1); //months since January
  Serial.print("-");
  Serial.print(ptm->tm_mday); //day of the month
  Serial.print(" ");
  Serial.print(ptm->tm_hour); //hours since midnight
  Serial.print(":");
  Serial.print(ptm->tm_min); //minutes after the hour
  Serial.print(":");
  Serial.println(ptm->tm_sec); //seconds after the minute

  //Serial.println(timeClient.getFormattedTime());

  delay(1000);
}

long getOffset(){
  WiFiClient client;
  HTTPClient http;

  long t_offset=0;

  Serial.print("[HTTP] begin...\n");
  if (http.begin(client, "http://ip-api.com/line/?fields=offset")) {  // HTTP
    Serial.print("[HTTP] GET...\n");
    int httpCode = http.GET();
    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = http.getString();
        t_offset = atol(payload.c_str());
      }
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
    return t_offset;
  } else {
    Serial.printf("[HTTP} Unable to connect\n");
  }
}
				
			

Great, now we get the date and time from NTP Server, but still not in the correct time zone.

Of course we can set up a timezone offset or even a daylight saving time offset, but it is 2021 already and we need it to be done automatically.

We get the offset base on our public IP address. Thanks for the free service from ip-api.com.

We used ip-api service in another tutorial – How to parse JSON on Arduino/ESP8266/ESP in 2021

Get UTC Daylight Saving Time offset based on public IP Address

ip-api.com provides a lot of information in different format. You can find their documentation here.

It is easy to get the offset, just send a HTTP Get request to 

http://ip-api.com/line/?fields=offset

It will return the offset in seconds, which is exactly what we need!

Now let’s add this part to our code, and setup the offset. The added part is highlighted.

				
					#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

const char *ssid     = "<SSID>";
const char *password = "<PASSWORD>";

WiFiUDP ntpUDP;

// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", getOffset());

// You can specify the time server pool and the offset, (in seconds)
// additionally you can specify the update interval (in milliseconds).
// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

void setup(){
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }

  timeClient.begin();
}

void loop() {
  timeClient.update();

  unsigned long epochTime = timeClient.getEpochTime();
  struct tm *ptm;
  ptm = gmtime ( &epochTime );

  Serial.print(String(ptm->tm_year+1900)); //years since 1900
  Serial.print("-");
  Serial.print(ptm->tm_mon+1); //months since January
  Serial.print("-");
  Serial.print(ptm->tm_mday); //day of the month
  Serial.print(" ");
  Serial.print(ptm->tm_hour); //hours since midnight
  Serial.print(":");
  Serial.print(ptm->tm_min); //minutes after the hour
  Serial.print(":");
  Serial.println(ptm->tm_sec); //seconds after the minute

  //Serial.println(timeClient.getFormattedTime());

  delay(1000);
}

long getOffset(){
  WiFiClient client;
  HTTPClient http;

  long offset=0;

  Serial.print("[HTTP] begin...\n");
  if (http.begin(client, "http://ip-api.com/line/?fields=offset")) {  // HTTP
    Serial.print("[HTTP] GET...\n");
    int httpCode = http.GET();
    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = http.getString();
        offset = long(payload);
      }
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
    return offset;
  } else {
    Serial.printf("[HTTP} Unable to connect\n");
  }
}
				
			

That’s it!

Now you have everything done. If you like it, please share it to your friends!

close

Updates

Tips

Coupons

We don’t spam! Read our privacy policy for more info.

Leave a Reply