ESP8266 - NTP klient a letní čas

Zápisník experimentátora

Hierarchy: ESP8266

Udělal jsem pár experimentů s NTP serverem a ESP8266. Stažení času a konverze do UTC je dobře popsána přímo v demo příkladech. Konverze času do časové zóny a používání letního času není skoro nikde dokumentováno. V tomto příkladu se naučíme takovou konverzi udělat. Využijeme k tomu knihovny, které pro nás připravili šikovní programátoři.

Použité součástky

Já jsem využil NodeMCU (v. 0.9), ale stejné výsledky byste dosáhli s libovolnou deskou, na níž je ESP8266.

Arduino IDE

Celé zapojení jsem testoval v IDE 1.8.2.

Použité knižnice

Celý trik převodu NTP času do lokálního času je ukryt ve vhodném použití knihoven.

  • Arduino Time Library (link) - Zde najdete základní funkce pro práci s časem.
  • Arduino Timezone Library (link) - Zde najdete funkce pro převod mezi UTC časem a časem v konkrétní časové zóně. Knihovna podporuje i zimní i letní čas.

Program

Program vypisuje v pravidelných intervalech aktuální čas v několika časových zónách. Příklad výpisu je v ukázce.

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

Úvodní část programu je kompletně převzata ze vzorového příkladu k ESP8266. Změněné je pouze nastavení hesla. Není přímo v programu, ale uložené bezpečně mimo něj. Podrobnosti naleznete v blogu o vkládání maker do programu v Arduinu.

#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;

Následují časové zóny. Autor knihovny Arduino Timezone Library navrhl pohodlný způsob na definici libovolné časové zóny. Potřebujete jen znát pravidlo přechodu času z letního na zimní a naopak a o zbytek se postarají třídy TimeChangeRule a 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);

Nyní většinu kódu přeskočím a soustředím se pouze na převod samotného času, který jsme získali z NTP serveru. Jeho funkce se dá uhodnout ze samotného zdrojového kódu. Nejprve se provede čas ze základu v roce 1900 na základ v roce 1970. A pak se vyžádá konverze do konkrétní časové zóny. Výsledek se vypíše funkcí printTime. Vidíte, že to není nic složitého.

Pokud píšete program, který má reagovat na lokální čas, je pro vás výhodné čas skladovat v UTC (například v integrovaném obvodu​ DS1307) a jen tehdy, když je to potřeba, si ho přetransformovat, porovnat s definovaným časem akce a provést akci.

    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");

Zdrojový kód

Zdrojový kód se nachází na serveru GitHub.


23.07.2017


Menu