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.
Já jsem využil NodeMCU (v. 0.9), ale stejné výsledky byste dosáhli s libovolnou deskou, na níž je ESP8266.

Celé zapojení jsem testoval v IDE 1.8.2.
Celý trik převodu NTP času do lokálního času je ukryt ve vhodném použití knihoven.
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 se nachází na serveru GitHub.
23.07.2017