3.4 Hodiny reálneho času
Pri práci s mikrokontrolérmi je často potrebné poznať reálny dátum a čas (a pokiaľ možno, veľmi presný). Táto potreba je síce v koncepte internetu vecí často potlačená - predovšetkým v režime fog alebo cloud computing - no rozhodne nie je na škodu. Dostupnosť a plynutie času riešia hodiny reálneho času (RTC - Real Time Clock).
Interné hodiny síce môžu byť implementované priamo v MCU (nie je to samozrejmosť), no aby fungovali aj po odpojení napájania, musela by byť prítomná batéria. Plnohodnotné riešenie RTC teda predstavuje samostatný obvod s malou batériou - externé hodiny. Takéto riešenie poznáme aj zo základných dosiek počítačov. Vývojové dosky mikrokontrolérov nezvyknú mať externé hodiny, medzi výnimky patrí zariadenie M5StickC Plus.
Interné hodiny RTC
Mikrokontroléry ESP32 majú k dispozícii interné hodiny, ktoré si čas udržia aj pri reštarte zariadenia, no po odstavení napájania sa čas stratí a vráti sa na polnoc 1. januára roku 2000. Preto je potrebné interné hodiny nastaviť po každom spustení zariadenia:
- a) manuálne (čo je len zriedka realizovateľné),
- b) z externého modulu RTC (ak je prítomný),
- c) z časového NTP servera (ak je dostupná sieť).
Náš firmvér pre M5Stick pri každom štarte nastaví interné hodiny z externého modulu RTC, pokiaľ má realistický dátum.
Interné hodiny sú v MicroPython dostupné cez objekt RTC z modulu machine. Konštruktor tohoto objektu neprijíma žiadne parametre. Hoci dokumentácia uvádza viaceré objektové funkcie, implementovaná je len funkcia datetime(), ktorá nastaví dátum a čas - jej jediným parametrom je tupla v tvare (rok, mesiac, deň, None, hodina, minúta, sekunda, mikrosekunda). Štvrtým parametrom je v skutočnosti deň týždňa, ale pri nastavovaní hodín nemá zmysel a možno ho vynechať uvedením None.
Nastavenie interných hodín teda môžeme realizovať príkazom:
- RTC().datetime(({rok}, {mesiac}, {deň}, None, {hodina}, {minúta}, {sekunda}, 0))
Pre získanie času z interných hodín máme k dispozícii viaceré funkcie z modulu time:
- time(): vráti aktuálny čas v podobe uplynutých sekúnd od polnoci 1. januára 2000;
- time_ns(): vráti aktuálny čas v podobe uplynutých nanosekúnd od polnoci 1. januára 2000;
- gmtime(): vráti aktuálny čas v podobe tuply v tvare (rok, mesiac, deň, hodina, minúta, sekunda, deň týždňa, deň roka) - je v neutrálnej časovej zóne UTC (GMT);
- localtime(): funguje rovnako ako gmtime(), ale mala by vrátiť čas v miestnej časovej zóne - časové zóny však nie sú v MicroPython implementované.
from time import gmtime
def Cas(c):
return "{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(c[0], c[1], c[2], c[3], c[4], c[5])
print("Interné hodiny:", Cas(gmtime()))
Časové zóny
Časové zóny a letný čas môžu značne komplikovať výpočty s časovými údajmi. Najlepším riešením je nastaviť interné hodiny na neutrálny čas UTC, s týmto časom aj pracovať a časovú zónu (spolu so spracovaním letného času) riešiť až pri vypisovaní času.
Ukážka vlastnej funkcie localtime(), ktorá vráti čas v našej časovej zóne CET (GMT+1), s prihliadnutím na letný čas CEST (GMT+2):
def localtime(s = None):
from time import time, mktime, gmtime
if s is None: s = time()
rok = gmtime(s)[0]
letny = list(gmtime(mktime((rok, 3, 31, 2, 0, 0, None, None))))
letny[2] -= (letny[6] + 1) % 7
zimny = list(gmtime(mktime((rok, 10, 31, 2, 0, 0, None, None))))
zimny[2] -= (zimny[6] + 1) % 7
letny[6:8] = zimny[6:8] = (None, None)
s += 3600 # UTC+1
if (mktime(letny) < s < mktime(zimny)):
s += 3600
return gmtime(s)
print("Interné hodiny miestnej časovej zóny:", Cas(localtime()))
Externé hodiny RTC
Zariadenie M5StickC Plus je vybavené externým modulom RTC BM8563, teda externými hodinami, ktoré bežia aj po vypnutí zariadenia, až do vybitia batérie. Keďže sa jedná o klon RTC PCF8563, môžeme využiť knižnicu preň. Hodiny sú pripojené cez zbernicu I²C. V našom firmvéri je v module M5Stick už všetko nachystané, objekt hodín je dostupný pod menom M5Stick.rtc.
Objekt poskytuje funkcie pre externé hodiny:
- datetime(): vráti aktuálny čas v podobe tuply v tvare (rok, mesiac, deň, hodina, minúta, sekunda, deň týždňa) - je vhodné nastaviť ho interným hodinám (viď vyššie);
- datetime({čas}): nastaví čas, zadaný v podobe tuply v tvare (rok, mesiac, deň, hodina, minúta, sekunda, deň týždňa) - deň týždňa je v rozsahu 0 (pondelok) až 6 (nedeľa).
Aj čas externých hodín by mal byť v neutrálnej časovej zóne UTC.
# zobrazenie externých hodín
import M5Stick
cas_ext = M5Stick.rtc.datetime()
print("Externé hodiny:", Cas(cas_ext))
# nastavenie interných hodín z externých hodín:
from machine import RTC
print("Nastavujem interné hodiny…")
RTC().datetime(cas_ext[:3] + (None,) + cas_ext[3:])
print("Interné hodiny:", Cas(gmtime()))
# zobrazenie času v miestnej časovej zóne (treba funkciu localtime)
print("Interné hodiny miestne:", Cas(localtime()))
