ESP8266 - NTP Client and Daylight Saving Time

Zápisník experimentátora

Hierarchy: ESP8266

I did a few experiments with the NTP server and ESP8266. Downloading time and converting to UTC is well described directly in demo examples. Conversion of time to the time zone and use of daylight saving time is hardly documented. In this example we will learn how to do this conversion. We will use the libraries that have been prepared for us by skilled programmers.

Used parts

I have taken advantage of NodeMCU (v. 0.9), but the same results would be achieved with any board on which ESP8266 is.

Arduino IDE

I tested the code in IDE 1.8.2.

Used libraries

The whole trick of transferring NTP time to local time is concealed in the appropriate use of libraries.

  • Arduino Time Library (link) - Here you will find basic time management functions.
  • Arduino Timezone Library (link) - Here's how to convert UTC time and time to a particular time zone. The library also supports winter and summer time.

Program (sketch)

The program prints the current time in several time zones at regular intervals. The example of the listing is in the preview.

sending NTP packet...
packet received, length=48
Seconds since Jan 1 1900 = 3709508909
Unix time = 1500520109
03:08:29 Thu 20 Jul 2017 UTC Universal Coordinated Time
05:08:29 Thu 20 Jul 2017 CEST Bratislava
23:08:29 Wed 19 Jul 2017 EDT New York
13:08:29 Thu 20 Jul 2017 AEST Sydney

The introductory part of the program is completely taken from the exemplary example of ESP8266. Only the password setting is changed. It is not directly in the program, but stored safely outside of it. For more details, see the article Inserting the macro definition into the Arduino program.

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <Timezone.h>

// Safe stored password
// http://www.arduinoslovakia.eu/blog/2017/6/vlozenie-definicie-makra-do-programu-v-arduine?lang=en
#if defined(_SSID)
const char* ssid = _SSID;
const char* pass = _PWD;
#else
char ssid[] = "*************";  //  your network SSID (name)
char pass[] = "********";       // your network password
#endif

unsigned int localPort = 2390;      // local port to listen for UDP packets

IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;

Next time zones. The author of the Arduino Timezone Library has proposed a convenient way to define any time zone. You only need to know the time-change rule from summer to winter and vice versa, and the rest will make classes TimeChangeRule and Timezone.

//Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};     //Central European Summer Time
TimeChangeRule CET = {"CET", Last, Sun, Oct, 3, 60};       //Central European Standard Time
Timezone CE(CEST, CET);

//Australia Eastern Time Zone (Sydney, Melbourne)
TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660};    //UTC + 11 hours
TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600};    //UTC + 10 hours
Timezone ausET(aEDT, aEST);

//US Eastern Time Zone (New York, Detroit)
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};  //Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};   //Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);

Now I will skip most of the code and focus only on converting the time we've got from the NTP server. Its function can be guessed from the source code itself. First, time is converted from base in 1900 to base in 1970, and then conversion is required to a specific time zone. The result is printed with printTime. You see, this is nothing complicated.

If you write a program to respond to local time, it's a good time to store time in a UTC (for example, in the​ DS1307), and only if necessary convert it, compare it to a defined action time, and take action.

    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);

    TimeChangeRule *tcr;
    time_t utc;
    utc = epoch;

    printTime(utc, "UTC", "Universal Coordinated Time");
    printTime(CE.toLocal(utc, &tcr), tcr -> abbrev, "Bratislava");
    printTime(usET.toLocal(utc, &tcr), tcr -> abbrev, "New York");
    printTime(ausET.toLocal(utc, &tcr), tcr -> abbrev, "Sydney");

Source code

The source code is located on the GitHub server.


23.07.2017


Menu