ESP32: Čas z NTP serveru v MicroPythonu
Když jsme se v minulém článku o MicroPythonu na modulu ESP32 zabývali možnostmi připojení k Wi-Fi síti, možná Vás trochu zamrzelo, že jsme článek nedoplnili nějakým více „užitečným“ příkladem. Ukázali jsme si jaký je rozdíl mezi režimy AP a STA, ale vždy naše ukázky končily jen nějakým stavovým hlášením ve výstupu. Abychom případné zájemce o hrátky s modulem ESP32 v MicroPythonu neodradili, zkusíme si dnes ukázat jedno (snad) roztomilé využití připojení modulu ESP32 k Wi-Fi.
Naším dnešním úkolem bude načíst do modulu ESP32 přesný čas z některého z NTP serverů. Této problematice jsme se věnovali dříve v článku: ESP32: Získání času a data ze NTP serveru, kde jsme ale program vytvářeli v prostředí Arduino IDE. Dnes máme stejné zadání, ale výsledný program budeme vytvářet v jazyce MicroPython (opět použijeme vývojové prostředí Thonny).
Na samý úvod si opět neodpustíme drobné vysvětlení pro ty, kteří se nechtějí pročítat zmíněným dřívejším článkem o NTP serveru.
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. Laicky řečeno NTP server je server, který nám po vhodném dotazu vrátí přesný čas. A máme jasno!
Získání data a času ze serveru NTP
Abychom mohli odeslat dotaz na některý z NTP serverů, musíme se pochopitelně s modulem ESP32 nejdříve přihlásit k nějaké Wi-Fi, která nám poskytne přístup k internetu. Problematiku připojení k Wi-Fi jsme řešili minule, takže se můžeme inspirovat v článku ESP32 – jak na Wi-Fi v MicroPython? a rovnou napsat program pro připojení k zadané Wi-Fi síti.
Připojení ESP32 k Wi-Fi
Asi tušíme, že v tomto případě bude muset modul ESP32 fungovat v režimu stanice (STA). Využijeme knihovny network
a rovnou asi i tušíme, že se nám hodí knihovna time
(zatím ji využijeme pro příkaz sleep
). Kód programu je obdobou kódu z minulého článku, nebudeme ho zde znova podrobně vysvětlovat. Pro lepší orientaci je tedy alespoň doplněn komentáři.
# pripojeni knihoven
import network
import time
# ***** Definice přístupu k síti WLAN *****
ssid = 'nazev wi-fi site'
pwd = 'heslo wi-fi site'
# pripojeni k Wi-Fi
mynet = network.WLAN(network.STA_IF) # nastaveni rezimu STA
mynet.active(True) # zapnuti sitoveho pripojeni
if not mynet.isconnected(): # neni-li ESP32 pripojen, zacne se pripojovat
mynet.connect(ssid, pwd) # autorizace pristupu
print("Pripojuji se k Wi-Fi")
n = 0
while (not mynet.isconnected()) and (n < 10): # cekaci smycka
n += 1 # timeout
print(".", end="")
time.sleep(1)
STAconf = mynet.ifconfig() # nacteni informaci pripojeni
print("\nSTA-IP:\t\t", STAconf[0], "\nSTA-NETMASK:\t", STAconf[1], \
"\nSTA-GATEWAY:\t", STAconf[2], "\nSTA-DNS:\t", STAconf[3] , sep="")
print("---")
time.sleep(3) # pripojeni je vyrizeno, poctej 3s
Abychom úplně jen nepřevzali program z minulého článku, dovolili jsme si do něj přidat několik úprav. První změna je v čekací smyčce, která běží, když probíhá připojení modulu ESP32 k zadané Wi-Fi síti. Kromě testování stavu připojení ještě testujeme proměnnou n
, která se postupně zvětšuje. Tato proměnná nám slouží jako jakýsi časovač. V čekací smyčce máme vteřinovou pauzu (sleep(1)
), takže proměnná n
dosáhne testované hodnoty asi za 10 vteřin. Čekací podmínka tedy buď skončí úspěšným připojením, nebo uplynutím 10 sekund. Kdyby se v čekací smyčce čekalo pouze na úspěšné připojení, došlo by při opakovaném neúspěšném pokusu o připojení k nekonečnému zacyklení programu. To byla kupříkladu slabina našeho programu v minulém článku. Tak alespoň v tomto jsme předešlý kód trochu povýšili! Zároveň si ale všimněme, že dále není nikterak testováno, jak čekací smyčka skončila, takže program poběží dále i v případě, že připojení bylo neúspěšné a uplynul čas 10 sekund. Zvídaný a pozorný čtenář jistě dokáže
kód upravit!
Druhou úpravou kódu oproti minulému je načtení a vypsání všech informací o připojení (nejen IP adresy modulu, ale i masky sítě, síťové brány a DNS). V programu tedy vidíme, že příklaz mynet.ifconfig()
ve skutečnosti vrací všechny tyto údaje jako pole. (My jsme si minule vybírali jen položku [0]
, která obsahovala IP adresu).
Možná za zmínku stojí i příkaz print
, který vypisuje síťové údaje. Zaprvé jsou v něm použity formátovací znaky \n
a \t
. Je možné, že jejich použití znáte kupříkladu z jazyka PHP. Pro ty, kteří to vidí poprvé, uveďme, že to jsou symboly pro odřádkování (\n
) a pro tabulátor (\t
). Používání těchto pomocných symbolů nám pomůže formátovat výstup k trochu snesitelnější podobě.
Také si všimněte zalomení řádku příkazu print
, který je relativně dlouhý. Řádku jsme zalomili pomocí symbolu zpětného lomítka. Protože programovací jazyk Python předpokládá příkazy na každé řádce, musíme mu v případě potřeby zalomení dlouhého řádku dát znamení, že na další řádce pokračuje zbytek předchozí řádky. Jinak by ten zbytek zalomené řádky považoval za nový příkaz a nastala by chyba. Budme si tedy pamatovat, že zkrácení příliš dlouhé řádky můžeme udělat jen pokud na konec zalomené řádky přidáme zpětné lomítko \
.
A u zmíněného příkazu print
ještě chvíli zůstaneme. Někoho může zarazit použitý parametr sep
. Minule jsme se seznámili s parametrem end
(je zde také v kódu použit). Teď se tedy podíváme na parametr sep
. Argument parametru sep
určuje oddělující znaky mezi jednotlivými prvky výstupu. Normálně je nastavena mezera. Takže pokud vytiskneme dva prvky, např. řetězce print("Ahoj","svete")
, bude na výstupu napsáno „Ahoj svete“. S parametrem sep="-"
to bude „Ahoj-svete“ a při sep=""
pochopitelně „Ahojsvete“. Takže ve výše uvedeném programu u výstupu síťových informací zakazujeme standardní oddělení vytištěných textů a proměnných mezerou.
A pak, že jsme „jen“ převrazli kód programu z minula! 😉
Načtení informací z NTP
Pokud jsme modulem ESP32 již připojeni k Wi-Fi síti, která nám otevírá možnost komunikace se světem, začneme řešit, jak zaslat dotaz na NTP server. Výhodou je, že v tomto směru náš požadavek není nikterak výjimečný, takže s velkou pravděpodobností jej jistě řešil někdo před námi. A máme v tomto směru skutečně štěstí, neboť pro přístup k NTP serveru dokonce v MicroPythonu existuje knihovna! Knihovna se jmenuje ntptime
a my si ji tedy připojíme do programu.
A když dnes tak trochu zkoušíme, co vše si od nás MicroPyhton nechá líbit, použijeme k tomu trochu netradiční zápis:
import ntptime as ntp
Tento příkaz importuje knihovnu ntptime
a přiřadí jí alias ntp
. Po zadání tohoto importu se tedy můžeme v programu odkazovat na knihovnu ntptime
jako na ntp
.
- Důležitá informace:
- Ne všechny knihovny používají formát příkazu importu:
import [library] as [name]
. Doporučené příkazy importu najdete v dokumentaci ke každé knihovně.
Jistě důležitou informací, kterou bude třeba zadat, je URL adresa nějakého NTP serveru. I když knihovna ntptime
se umí vypořádat i se situací, kdy adresu NTP nezadáme – pak si sama dosadí defaultní hodnotu pool.ntp.org
. My si vybereme český NTP server a jeho adresu zadáme následujícím způsobem:
ntp.host = 'ntp.nic.cz'
Pokud bychom zatoužili po jiném NTP serveru, stačí se poohlédnout po internetu. Adres NTP serverů se dá „vygooglit“ docela dost. Zde uvádíme některé evropské. Je ale otázkou, zda má význam se dotazovat na přesný čas někde přes půl zeměkoule.
Adresa NTP serveru | Země umístění |
---|---|
cz.pool.ntp.org | Česká republika |
de.pool.ntp.org | Německo |
li.pool.ntp.org | Lichtenštejnsko |
hr.pool.ntp.org | Chorvatsko |
no.pool.ntp.org | Norsko |
tr.pool.ntp.org | Turecko |
Načtení údajů z NTP
Načtení času z NTP serveru provedeme příkazem ntp.time()
, musíme si však uvědomit dvě základní věci:
- Formát časové značky vrácené při volání serveru NTP je čas v sekundách od 1. 1. 2000, potřebujeme takový čas převést do nějakého rozumnějšího tvaru.
- Navrácený čas je v podobě UTC (koordinovaného světového času), tedy musíme zohlednit naše časové pásmo.
Problém časového pásma vyřešíme proměnnou timeZone
, která bude udávat naše aktuální časové pásmo (pro ČR to bude hodnota +1). Hodnotu časového pásma pak vynásobíme hodnotou 3600 (počet vteřin v jedné hodině) a přičteme ji k hodnotě navrácené z funkce ntp.time()
. Tím budeme mít správnou hodnotu časové značky, pak ji převedeme pomocí funkce localtime
, která je obsažena v knihovně time
, na formát data a času. Knihovnu time
již máme k programu připojenou, neboť jsme z ní využívali funkci sleep
.
Nezbývá než zmínit, do jaké podoby převede funkce localtime
zadanou časovou značku. Výstupem bude pole, jehož první položka (programátor rozumí, že nultá! 😉) je aktuální rok, následuje měsíc a den. Aha… A nechtěli jsme náhodou aktuální čas? I k tomu se dostaneme! Hodiny aktuálního času najdeme na 4. místě (tj. index pole 3), následující položka pole jsou minuty a pochopitelně pak i vteřiny. Navrácené pole má ještě další dvě položky a to aktuální číslo dne v týdnu (pozor, pondělí není číslo 1!) a pořadové číslo dne v daném roce. Všechny tyto údaje jsou názorně vypsány v následujícím programu. A jelikož jsme si o pár odstavců dříve ukázali, jak lze výstup trochu vizuálně naformátovat, opět použijeme zástupné symboly za tabulátor a pochopitelně i parametr sep
a end
. 😊
„Vyzobnutí“ funkcí z knihovny
A když jsme se dnes tak nějak „pythonovsky rozdováděli“, tak zkusíme ještě jednu vychytávku. Z poměrně velké a robustní knihovny time
využíváme jen dvě funkce, a to sleep
a localtime
. Tak proč si k programu připojovat celou tuto knihovnu. Vytáhneme si tedy z této knihovny jen potřebné funkce:
from time import sleep, localtime
Ošetření výjimky
A aby tohoto dnes opravdu nebylo málo, ještě se podíváme na jednu programátorsky zajímavou strukturu. To je tzv. ošetření výjimky pomocí struktury try
/except
. Jak to funguje? Příkazy v bloku uvozeném příkazem try
se normálně provádějí, ale když nastane nějaký problém (výjimka), Python přeskočí zbytek bloku try
a provede všechno v bloku except
. Naopak, pokud výjimka nenastane, přeskočí se celý blok except
.
Toto řešení se nám docela hodí, protože při pokusu o dotaz na NTP server se může ledacos pokazit. Vložíme tedy tento blok příkazů do bloku try
a v bloku except
připravíme hlášku o problému. Je to sice trochu alibistický přístup, ale v tomto jednoduchém příkladu asi lze říci: „Hele, a proč ne?“
Definitivní kód programu pro načtení času z NTP serveru by pak mohl vypadat následujícím způsobem:
# pripojeni knihoven
import ntptime as ntp # knihovna ntptime s aliasem ntp
import network # knihovna pro praci s Wi-Fi
from time import sleep, localtime # něco malo z knihovny time
timeZone=1 # nastaveni casovehp pasma v CR
# ***** Definice přístupu k síti WLAN *****
ssid = 'nazev wi-fi site'
pwd = 'heslo wi-fi site'
ntp.host = 'ntp.nic.cz' # prepsani adresy NTP serveru [default: pool.ntp.org]
# pripojeni k Wi-Fi
mynet = network.WLAN(network.STA_IF) # nastaveni rezimu STA
mynet.active(True) # zapnuti sitoveho pripojeni
if not mynet.isconnected(): # neni-li ESP32 pripojen, zacne se pripojovat
mynet.connect(ssid, pwd) # autorizace pristupu
print("Pripojuji se k Wi-Fi")
n = 0
while (not mynet.isconnected()) and (n < 10): # cekaci smycka
n += 1 # timeout
print(".", end="")
sleep(1)
STAconf = mynet.ifconfig() # nacteni informaci pripojeni
print("\nSTA-IP:\t\t", STAconf[0], "\nSTA-NETMASK:\t", STAconf[1], \
"\nSTA-GATEWAY:\t", STAconf[2], "\nSTA-DNS:\t", STAconf[3] , sep="")
print("---")
sleep(3) # pripojeni je vyrizeno, poctej 3s
while True: # nekonecna smycka dotazu na NTP
try: # osetreni
vyjimky
tag = localtime(ntp.time() + timeZone*3600) # nacteni casu, zohledni casove pasmo a sup do pole
print("NTP-Time:") # vypis ziskanych udaju
print("Datum:\t", end="") # DATUM
print(tag[2], tag[1], tag[0], sep=". ") # den. mesic. rok
print("Cas:\t", end="") # CAS
print(tag[3], tag[4], tag[5], sep=":") # hod:min:sec
print("Den v tydnu:", tag[6], end="") # den v tydnu
print(", den v roce:",
tag[7], "\n") # den v roce
except: # co delat, když nastane chyba
print("Chyba prenosu")
sleep(1) # za vteřinu se zeptáme
znova
Použití výjimky nám mimo jiného trochu zachránilo problém, na který jsme si „naběhli“ při nevhodně zvolené struktuře programu při samotném přihlášení k Wi-Fi. Jak jsme viděli dříve, pokud by se připojení nezdařilo do nastavených deseti sekund, nemělo by smysl se dále dožadovat informací z internetového NTP severu. Náš program ale i v tomto případě pokračuje a začne se pokoušet o načtení dat z NTP serveru. Následujcí obrázek ukazuje, jak to dopadne, když kupříkladu zapomeneme v programu zadat údaje ssid
a psw
své Wi-Fi sítě.

Jak vidíme na obrázku, protože v programu nebyly dobře vyplněny přístupové údaje (modrý rámeček), při připojování proběhlo deset sekund (deset teček v červeném rámečku) a modul ESP32 se nepřipojil k Wi-Fi. Údaje připojení (IP adresa, maska sítě…) jsou načteny jako samé nuly. Následující pokusy o kontakt s NTP serverem logicky selhávají. Nastává tedy výjimka, v programu se spouští blok except
, který vypisuje hlášku „Chyba prenosu“. A jelikož program běží dál, tak každou vteřinu se neúspěšný dotaz na NTP server opakuje. Asi vidíme, že použité ošetření výjimky nebylo špatné, ale pro rozumnější chování programu by asi bylo lepší celkovou strukturu programu malinko předělat.
To lze opět pojmou jako určitý dobrovolný domácí úkol pro pilného čtenáře.
Pokud vše proběhne, jak požadujeme, což by bylo žádoucí, program načte potřebné údaje a zobrazí je na výstupu. Tuto situaci zachycuje následující obrázek.

Ve výstupním okně vidíme jak kompletní síťové informace připojeného modulu ESP32, tak (a to především) načtené a formátovaně vypsané časové údaje z NTP serveru.
Závěr
V dnešním článku jsme se sice zabývali jen jedním konkrétním užitím modulu ESP32, který se připojil k Wi-Fi síti, ale i při tomto relativně jednoduchém příkladu jsme si opět ukázali několik dalších vlastností programovacího jazyka (Micro)Python. Znalost několika zde prezentovaných rádoby vychytávek není pro začátečníky ani tak potřebná pro aktivní psaní programů, jako to spíše oceníme v okamžiku studia programů jiných autorů. Není nic rušivějšího než neustálé zbytečné hledání rozdílných forem zápisu programu nebo jeho různých zkrácených podob.
Pochopitelně doufáme, že zde uvedený příklad posloužil i jako onen praktický příklad užití modulu ESP32 připojeného k internetu. I když je pravda, že hlavní část té síťové práce za nás dnes obstarala knihovna ntptime
. Ale i tak to s tím programováním (nejen) v MicroPythonu někdy bývá…