ESP8266 - NTP klient a letný čas

Zápisník experimentátora

Hierarchy: ESP8266

Urobil som zopár experimentov s NTP serverom a ESP8266. Stiahnutie času a konverzia do UTC je dobre popísaná priamo v demo príkladoch. Konverzia času do časovej zóny a používanie letného času nie je skoro nikde dokumentované. V tomto príklade sa naučíme takúto konverziu urobiť. Využijeme na to knižnice, ktoré pre nás pripravili šikovní programátori.

Použité súčiastky

Ja som využil NodeMCU (v. 0.9), ale rovnaké výsledky by ste dosiahli s ľubovolnou doskou, na ktorej je ESP8266.

Arduino IDE

Celé zapojenie som testoval v IDE 1.8.2.

Použité knižnice

Celý trik prevodu NTP času do lokálneho času je ukrytý vo vhodnom použití knižníc.

  • Arduino Time Library (link) - Tu nájdete základné funkcie na prácu s časom.
  • Arduino Timezone Library (link) - Tu nájdete funkcie na prevod medzi UTC časom a časom v konkrétnej časovej zóne. Knižnica podporuje aj zimný a letný čas.

Program

Program vypisuje v pravidelných intervaloch aktuálny čas v niekoľkých časových zónach. Priklad výpisu je v ukážke.

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á časť programu je kompletne prevzatá zo vzorového príkladu k ESP8266. Zmenené je iba nastavenie hesla. Nie je priamo v programe, ale uložené bezpečne mimo neho. Podrobnosti nájdete v blogu o vkladaní makier do programu v Arduine.

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

Nasledujú časové zóny. Autor knižnice Arduino Timezone Library navrhol pohodlný spôsob na definíciu ľubovoľnej časovej zóny. Potrebujete len poznať pravidlo prechodu času z letného na zimný a naopak a o zvyšok sa postarajú triedy 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);

Teraz väčšinu kódu preskočím a sústredím sa iba na prevod samotného času, ktorý sme získali z NTP servera. Jeho funkcia sa dá uhádnuť zo samotného zdrojového kódu. Najprv sa prevedie čas zo základu v roku 1900 na základ v roku 1970. A potom sa vyžiada konverzia do konkrétnej časovej zóny. Výsledok sa vypíše funkciou printTime. Vidíte, že to nie je nič zložité.

Pokiaľ píšete program, ktorý má reagovať na lokálny čas, je pre vás výhodné čas skladovať v UTC (napríklad v integrovanom obvode DS1307) a len vtedy, keď je to potrebné, si ho pretransformovať, porovnať s definovaným časom akcie a vykonať akciu.

    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 sa nachádza na serveri GitHub.


20.07.2017


Menu