Fyzikální kabinet FyzKAB

ESP32: Získání času a data ze NTP serveru

princip NTP
zdroj obrázku: https://microcontrollerslab.com/getting-current-date-time-esp32-arduino-ide-ntp-server/

Čas od času se setkáme s projektem, ve kterém je určení přesného času klíčovým momentem. Představte si například relé, které musí být sepnuto v určitý daný čas, nebo zápis dat v přesných časových intervalech. Jako první nápad nás jistě napadne, použití RTC čip (Real Time Clock). Bohužel, tyto čipy i přes jejich konstrukci nejsou úplně přesné, takže se stejně musí jednou za čas synchronizovat. V případě potřeby opravdu přesného času by tedy mohlo být řešením použití NTP protokolu (Network Time Protocol). Pokud má naše vývojová deska s modulem ESP32 přístup k internetu, můžete získat přesný čas a datum zdarma a velmi rychle, aniž bychom potřebovali jakýkoliv další hardware.

Co je to NTP?

NTP znamená Network Time Protocol a je to standardní internetový protokol (IP) pro synchronizaci hodin počítače s přesnou časovou referencí. Protokol lze použít k synchronizaci všech síťových zařízení na hodnotu koordinovaného světového času (UTC *) a to vše během několika milisekund (50 milisekund na veřejném internetu a méně než 5 milisekund v prostředí LAN). NTP nastavuje hodiny počítačů na UTC, pochopitelně klient používá určitý offset místního časového pásma nebo časový posun letního času. Tímto způsobem se mohou klienti synchronizovat se servery bez ohledu na rozdíly v umístění a časovém pásmu.

Architektura NTP

NTP používá hierarchickou architekturu. Každá úroveň v hierarchii je známá jako vrstva. Úplně nahoře je vysoce přesná časomíra, jako jsou atomové hodiny, GPS nebo rádiové hodiny. Tato vrstva je známá jako hardwarové hodiny „Stratum 0“. Servery „Stratum 1“ mají přímé připojení k hardwarovým hodinám „Stratum 0“, a proto mají nejpřesnější čas. Každá nižší vrstva v hierarchii se synchronizuje od výše uvedené vrstvy a funguje jako server pro počítače s nižšími vrstvami.

NTP architektura
zdroj obrázku: https://en.wikipedia.org/wiki/Network_Time_Protocol

Jak NTP funguje?

NTP může fungovat několika způsoby. Nejběžnější konfigurací je provoz v režimu klient-server. Základní pracovní princip je následující:

  1. Klientské zařízení, jako je ESP32, se připojuje k serveru pomocí protokolu UDP (User Datagram Protocol) na portu 123.
  2. Klient poté odešle paket požadavků na server NTP.
  3. V reakci na tento požadavek server NTP odešle paket časového razítka.
  4. Paket časového razítka obsahuje více informací, jako je časové razítko, přesnost, zpoždění nebo časové pásmo systému UNIX.
  5. Klient pak může analyzovat aktuální hodnoty data a času.

princip NTP
zdroj obrázku: https://laptrinhx.com/esp32-17-sntp-3394712906/

Dost teorie, pojďme do praxe!

Získání data a času ze serveru NTP

Následující program nám představí příklad, ve kterém uvidíme, jak lze získat datum a čas ze serveru NTP. Než se však pustíme do nahrání kódu do modulu ESP32, je třeba ještě nastavit několik věcí.

Proměnné ssid a password je třeba nastavit na hodnoty potřebné pro připojení k Wi-Fi sítě:

const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";

Dále je třeba nastavit proměnné offsetu místního času a letního času (nastavuje se v sekundách), např. pro UTC –5,00 je třeba dosadit do proměnné gmtOffset_sec hodnotu –5 * 60 * 60 = –18000 nebo v případě UTC +1,00 dosadíme 1 * 60 * 60 = 3600.

const long gmtOffset_sec = 3600;

Obdobně nastavíme do proměnné daylightOffset_sec časový offset odpovídající letnímu času (opět nastavíme v sekundách) – pro letní čas hodnota 3600, pro zimní čas hodnota 0.

const int daylightOffset_sec = 3600;

Jakmile nastavíme tyto hodnoty, můžeme přeložit a nahrát do modulu ESP32 následující program:

/* ukázka nacteni casu z NTP serveru */

#include <WiFi.h>   // vlozeni knihovny pro wifi
#include "time.h"   // vlozeni knihovny pro spravu casu

const char* ssid = "YOUR_SSID";   // nazev wifi site, ke které se chcete pripojit
const char* password = "YOUR_PASS";   // heslo pro pripojeni k wifi siti

const char* ntpServer = "pool.ntp.org";   // adresa zvoleného NTP serveru
const long gmtOffset_sec = 3600;   // casovy offset pro lokalni cas
const int daylightOffset_sec = 3600;   // casovu offset pro letni cas

/* -------------- */

void printLocalTime() {   // vlastní funkce pro tisk aktualniho data a čas
   struct tm timeinfo;   // casova struktura pro ulozeni data a casu od NTP serveru
   if( !getLocalTime(&timeinfo) ) {   // prenos pozadavku na server NTP a analyza casoveho razitka
     Serial.println("Nepodarilo se ziskat cas!");
     return;
   }
   Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");   // tisk data a casu v zadatem formatu
}

// funkce SETUP se spusti jednou pri stisknuti tlacitka reset nebo pri zapnuti desky.
void setup() {
   Serial.begin(115200);   // prenosova rychlost serioveho vystupu

   Serial.printf("Connecting to %s ", ssid);   // pripojeni k Wi-Fi siti
   WiFi.begin(ssid, password);
   while (WiFi.status() != WL_CONNECTED) {   // nez se pripoji, tiskni tecky
     delay(500);
     Serial.print(".");
   }
   Serial.println(" CONNECTED");

   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);   // inicializujeme klienta NTP 
   printLocalTime();

   WiFi.disconnect(true);   // odpojeni od Wi-Fi, uz neni potreba.
   WiFi.mode(WIFI_OFF);   // vypnuti Wi-Fi
}

// funkce LOOP bezi stale dokola.
void loop() {
   delay(1000);
   printLocalTime();
}


Po nahrání programu do modulu ESP32 a stisknutí tlačítko EN (nebo též označen RST) by se každou sekundu měl zobrazovat datum a čas (jak je uvedeno níže).

ESP32 čte datum a čas z výstupu serveru NTP

Poznámky ke kódu:

  • Pro adresu NTP serveru lze nastavit různé hodnoty. Hodnota pool.ntp.org je globální celosvětová hodnota, která automaticky vybírá časové servery, které jsou vám geograficky blízké. Pokud si ale chcete vybrat explicitně, použijte jednu z dílčích zón:

Umístění: Adresa:
Celosvětově pool.ntp.org
Asie asia.pool.ntp.org
Evropa europe.pool.ntp.org
Severní Amerika north-america.pool.ntp.org
Oceánie oceania.pool.ntp.org
Jižní Amerika south-america.pool.ntp.org

  • Funkce getLocalTime() se používá k přenosu paketu požadavku na server NTP a analýze přijatého paketu časového razítka do čitelného formátu. Jako parametr vyžaduje odkaz na proměnnou časové struktury. K informacím o datu a čase můžeme přistupovat prostřednictvím položek této časové struktury – viz následující tabulka:

identifikár význam
%A vrací den v týdnu (celý název)
%a vrací den v týdnu (zkrácený název)
%B vrací měsíc v roce (celý název)
%b vrací měsíc v roce (zkrácený název)
%d vrací číslo den v měsíci
%Y vrací rok
%H vrací hodiny (24 hodinový formát)
%I vrací hodiny (12 hodinový formát)
%M vrací minuty
%S vrací sekundy

Kupříkladu příkaz Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); zobrazí aktuální čas ve formátu „aktuální den v týdnu“, následuje čárka a mezera, pak se zobrazí název měsíce, následuje den a rok (opět oddělené mezerami) a na závěr je čas (hodiny, minuty, sekundy) vše oddělené dvojtečkami.

Použití knihovny NTPClient

Protože načítání času z NTP serveru je poměrně běžný úkol, byla vytvořena speciální knihovna. Knihovnu NTP klienta lze buď nainstalovat přes Správce knihoven v prostředí Arduino IDE nebo přímo ze stránek vývojáře – https://github.com/arduino-libraries/NTPClient

#include <NTPClient.h>
#include <WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "YOUR_SSID";   // nazev wifi site, ke které se chcete pripojit
const char* password = "YOUR_PASS";   // heslo pro pripojeni k wifi siti

WiFiUDP ntpUDP;

// vychozi nastaveni je 'pool.ntp.org' s intervalem aktualizace 60 sekund a bez offsetu
NTPClient timeClient(ntpUDP);
// lze zadat adresu NTP serveru, offset (sec)a interval aktualizace (ms).
// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

// funkce SETUP se spusti jednou pri stisknuti tlacitka reset nebo pri zapnuti desky.
void setup() {
   Serial.begin(115200);
   WiFi.begin(ssid, password);
   while (WiFi.status() != WL_CONNECTED) {   // nez se pripoji, tiskni tecky
     delay(500);
     Serial.print(".");
   }
   Serial.println(" CONNECTED");

   timeClient.begin();   // start NTP klienta }

// funkce LOOP bezi stale dokola.
void loop() {
   timeClient.update();
   Serial.println(timeClient.getFormattedTime());
   delay(1000);
}


Po nahrání kódu do vývojové desky s modulem ESP32 můžeme na Serial Monitoru vidět výpis aktuálního UTC času. Výpis se aktualizuje každou sekundu, každou minutu by mělo dojít k synchronizaci času s NTP serverem přes Wi-Fi připojení. Pokud bychom chtěli změnit jak interval synchronizace, tak především časový offset lokálního času vůči UTC, je tato možnost ukázána v kódu (zakomentováná inicializace NTP klienta).

ESP32 čte datum a čas z výstupu serveru NTP

Všimněme si, že použití knihovny NTPClient má několik výhod. Zaprvé to je poměrně jednoduchá implementace NTP klenta, včetně jeho nastavení. Druhou výhodou je možnost nastavení intervalu pravidelné synchronizace. Naopak určitou nevýhodou knihovny NTPClient je absence některých funkcí, například funkce pro zobrazení měsíce a roku (knihovna obsahuje jen funkce getDay(), getHours(), getMinutes(), getSeconds(), popř. getFormattedTime()). Tuto slabinu lze ale vyřešit pomocí funkce getEpochTime(), která vrací unixovou epochu, což jsou sekundy uplynulé od 00:00:00 UTC 1. ledna 1970, a z této podoby je získání potřebných informací již řešitelné.

Obecně lze asi říci, že pokud chceme vytvořit aplikaci, kdy dojde k jednorázové Wi-Fi synchornizaci (například při probuzení obvodu, načtení dat, jejich odeslání po síti a opětovná hibernace) lze použít první postup. V případě trvalého a dlouhodobého ho připojení, kdy dochází ke sběru dat (například sběr dat v meteostanici) je asi lepší využít postup druhý s knihovny NTPClient a pravidelnou synchronizací.

 


*)  Koordinovaný světový čas UTC (Coordinated Universal Time) je celosvětový časový standard, který úzce souvisí s GMT (greenwichský střední čas). UTC se nemění, je stejný na celém světě.

Reklama:
UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!