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