Lab #5: Komunikácia pomocou prokolu MQTT
organizácia tém, štruktúra správ, zmena konfigurácie zariadenia a jeho ovládanie na diaľku pomocou protokolu MQTT
Video pre cvičenie
Motivácia
Komunikačný protokol MQTT patrí medzi najčastejšie a najobľúbenejšie protokoly používané v oblasti IoT. Aj preto je hlavným komunikačným protokolom v architekúre projektu Smart Department, kde sa používa na komunikáciu medzi všetkými vrstami architektúry. Všetky správy, ktoré sa pomocou protokolu MQTT posielajú, sú vo formáte JSON.
Na tomto cvičení zabezpečíme odosielanie nameraných údajov zo senzoru, ako aj príjem správ na zmenu konfigurácie zariadenia a vzdialenú správu.
Ciele
naučiť sa ovládať chytré zariadenie pomocou protokolu MQTT
naučiť sa prijímať správy cez MQTT protokol
porozumieť významu príznaku
retainpri odosielaní MQTT správ
Postup
Inštalácia
knižnice umqtt.simple
V aktuálnom firmvéri jazyka MicroPython pre mikrokontroléry
z rodiny RP2 sa nenachádza modul pre prácu s protokolom
MQTT. V tomto kroku ju pridáme nainštalovaním balíka umqtt.simple.
Task
Nainštalujte na chytré zariadenie knižnicu umqtt.simple.
Knižnica umqtt.simple
je minimalistický klient protokolu MQTT zo štandardnej knižnice
jazyka MicroPython (micropython-lib).
Je navrhnutá pre zariadenia s obmedzenými zdrojmi, takže je malá a
podporuje len to najpodstatnejšie:
- pripojenie k brokeru
- podpora šifrovanej komunikácie pomocou TLS,
- publikovanie správ s úrovňou kvality služby QoS 0 alebo 1 (QoS 2 nepodporuje),
- prihlásenie na odber tém a spracovanie prijatých správ,
- nastavenie poslednej vôle (z angl. last will).
Knižnica je blokujúca a spojenie s brokerom sama neudržiava. Pri
dlhšom spojení je preto potrebné posielať brokeru správu
PING a po výpadku sa pripojiť znova. Automatické opätovné
pripojenie rieši nadstavba umqtt.robust.
Task
Sprístupnite zdrojový kód knižnice umqtt.simple
vývojovému prostrediu PyCharm, aby rozpoznalo
jej import a ponúkalo dopĺňanie kódu.
Balík mip ukladá knižnicu na zariadenie ako preložený
súbor .mpy. Prostredie PyCharm ho nevie čítať a
navyše nevidí do súborového systému mikrokontroléra, preto import
from umqtt.simple import MQTTClient označí ako chybu.
Pomôžeme mu tak, že zdrojový kód knižnice umiestnime priamo do
projektu.
V koreňovom priečinku projektu (vedľa priečinka src/)
vytvorte priečinok stubs/ a stiahnite doň súbor simple.py
zo štandardnej knižnice jazyka MicroPython (micropython-lib).
Zachovajte pritom štruktúru balíka, teda súbor uložte ako
stubs/umqtt/simple.py.
Upozornenie
Priečinok stubs/ nesmie byť v priečinku
src/, inak by sa pri synchronizácii nahral na
zariadenie.
Potom priečinok stubs/ označte v prostredí PyCharm ako zdrojový
priečinok (pravé tlačidlo myši na priečinku → Mark Directory as →
Sources Root). Na zariadenie sa nenahrá, pretože spúšťač, ktorý ste
vytvorili v prvom cvičení, nahráva len
obsah priečinka src/.
Ak ste postupovali správne, prostredie PyCharm import
from umqtt.simple import MQTTClient rozpozná a pri písaní
kódu vám bude ponúkať metódy triedy MQTTClient.
Poznámka
Namiesto zdrojového kódu môže priečinok stubs/ obsahovať
aj stub súbory (s príponou .pyi), s ktorými ste sa
stretli už v prvom cvičení. Zo zdrojového
kódu ich je možné vygenerovať napr. nástrojom stubgen z
balíka mypy. Keďže knižnica umqtt.simple
nemá typové anotácie ani dokumentačné reťazce, vygenerovaný stub by
vývojovému prostrediu neposkytol viac informácií ako samotný zdrojový
kód.
Používateľské nastavenia pre MQTT
Chytré zariadenie bude komunikovať pomocou protokolu MQTT.
To, ku ktorému brokeru sa pripojí, aké prihlasovacie údaje použije a či
bude komunikácia šifrovaná, však závisí od toho, kde bude zariadenie
nasadené – iný broker použijete na cvičení, iný doma a iný bude mať
zákazník. Tieto údaje preto nemôžu byť súčasťou statickej konfigurácie v
module constants.py, ale musí ich byť možné zmeniť bez
zásahu do kódu.
V tomto kroku rozšírime triedu Settings o novú skupinu
nastavení pre pripojenie k MQTT brokeru.
Task
Rozšírte používateľské nastavenia o pripojenie k MQTT brokeru.
Nastavenia pripojenia budú v novej triede MQTT a v
triede Settings budú dostupné v členskej premennej
mqtt. Trieda MQTT bude vyzerať nasledovne:
from constants import DEVICE_TYPE
# ...
class MQTT(BaseModel):
broker: str = Field(default=None, type=str, optional=True)
port: int = 1883
username: str = Field(default=None, type=str, optional=True)
password: str = Field(default=None, type=str, optional=True)
tls: bool = False
topic_prefix: str = DEVICE_TYPEVýznam jednotlivých členských premenných je nasledovný:
broker– adresa MQTT brokera,port– port, na ktorom broker počúva; štandardne1883, pri šifrovanej komunikácii8883,usernameapassword– prihlasovacie údaje k brokeru,tls– príznak šifrovanej komunikácie (ak ho zapnete, nezabudnite zmeniť aj port),topic_prefix– začiatok tém, do ktorých bude zariadenie publikovať; predvolenou hodnotou je typ zariadenia z konštantyDEVICE_TYPE("thsensor"). V architektúre Smart Department má prefix tvar<department>/<room>/<device_type>(pozri dokument Komunikácia cez MQTT).
Task
Navrhnite a implementujte vhodné validátory pre overenie hodnôt v nastaveniach pripojenia k MQTT brokeru.
Zamyslite sa nad tým, ktoré hodnoty môže používateľ zadať nesprávne a čo by to spôsobilo pri pripájaní k brokeru alebo pri publikovaní správ. Môžete sa inšpirovať napríklad týmito validátormi:
check_port()– overí, či je číslo portu v rozsahu1až65535,check_host()– overí, či je adresa brokera platný doménový názov alebo IP adresa (napr. nesmie obsahovať medzery, schému akomqtt://ani číslo portu),check_topic_prefix()– overí, či prefix tém neobsahuje zástupné znaky+a#, nezačína ani nekončí znakom/a neobsahuje prázdnu úroveň (//).
Ak hodnota nie je platná, validátor vyvolá výnimku
ValueError s vhodnou správou.
Task
Vytvorte v triede Settings metódu
base_topic(), ktorá vráti základ tém zariadenia
(<base_topic>) v tvare
<topic_prefix>/<device_id>, kde
<device_id> je konštanta DEVICE_ID z
modulu identity.py.
Vlastné správy zariadenia (napr. namerané údaje alebo stav
zariadenia) sa budú posielať do tém, ktoré začínajú týmto spoločným
základom, napr. thsensor/ab123cd/data. Vďaka metóde
base_topic() sa základ tém skladá na jednom mieste. V
ďalšom texte budeme témy zapisovať skrátene pomocou základu tém, napr.
<base_topic>/data.
>>> settings.base_topic()
'thsensor/ab123cd'Poznámka
Hierarchia tém nie je náhodná. Vychádza z prístupu Unified Namespace používaného v priemysle, kde témy odrážajú štruktúru organizácie (viac v dokumente Komunikácia cez MQTT).
Task
Doplňte do súboru s nastaveniami na zariadení nastavenia pre pripojenie k MQTT brokeru, ktorý budeme používať na cvičeniach.
Broker má tieto parametre:
- adresa:
mother.fei.tuke.sk - port:
8883 - používateľské meno:
student - heslo:
dgRk9cQ8sU4fCO - šifrovaná komunikácia: áno
Namiesto zadávania jednotlivých hodnôt príkazom
settings set tentokrát upravíte priamo súbor
settings.json v priečinku DATA_FOLDER na
zariadení. Otvoriť a rozšíriť o sekciu mqtt ho môžete
priamo pomocou rozšírenia MicroPython
Tools:
{
"wifi": { ... },
"units": "metric",
"mqtt": {
"broker": "mother.fei.tuke.sk",
"port": 8883,
"username": "student",
"password": "dgRk9cQ8sU4fCO",
"tls": true,
"topic_prefix": "kpi/caprica/thsensor"
}
}Poznámka
Broker používa zoznam povolených tém (z angl. Access Control
List, ACL), preto v prefixe tém nemôžete použiť predvolenú
hodnotu "thsensor". Správy do nepovolených tém broker
zahodí bez toho, aby sa o tom zariadenie dozvedelo (viac v dokumente Komunikácia cez MQTT).
Upozornenie
Nezabudnite na to, že ak súbor s nastaveniami upravíte nesprávne,
zariadenie pri najbližšom štarte prejde do stavu
FactoryReset a všetky nastavenia sa obnovia na predvolené
hodnoty.
Upravený súbor načítajte v servisnom termináli podpríkazom
settings load. Ak súbor obsahuje chybu, podpríkaz ju ohlási
a aktuálne nastavenia nezmení – chybu tak opravíte ešte pred reštartom
zariadenia.
Ak ste postupovali správne, nastavenia sa načítajú a ich výpis bude vyzerať takto:
> settings load
Settings loaded.
> settings get mqtt
broker: "mother.fei.tuke.sk"
port: 8883
username: "student"
password: "dgRk9cQ8sU4fCO"
tls: true
topic_prefix: "kpi/caprica/thsensor"
Overte aj to, že nastavenia zostanú zachované po reštarte zariadenia.
Príkaz mqtt
V tomto kroku vytvoríme príkaz mqtt, pomocou ktorého
bude možné pracovať s MQTT brokerom priamo zo servisného
terminálu. Technik si tak bude môcť ručne overiť, či sú nastavenia
pripojenia správne. Jednotlivé činnosti príkazu implementujeme ako operácie príkazu, aby ich neskôr mohli použiť
aj stavy zariadenia.
Task
Rozšírte triedu Context o členskú premennú
mqtt, ktorá bude uchovávať klienta pre pripojenie k
MQTT brokeru.
Pripájanie k brokeru a odosielanie správ sa bude odohrávať na rôznych
miestach: v príkaze mqtt aj v stavoch zariadenia. Klient
preto musí byť uložený na mieste, ku ktorému majú prístup všetci, teda v
kontexte. Premennú vytvorte v konštruktore s hodnotou None.
Táto hodnota bude znamenať, že zariadenie nie je pripojené k
brokeru.
Task
V module commands/mqtt.py vytvorte triedu
MQTT, ktorá bude reprezentovať príkaz
mqtt.
O tejto triede bude platiť:
- bude potomkom triedy
BaseCommand, - názov príkazu bude
"mqtt", - opis príkazu bude
"Manage MQTT client.", - prvým parametrom príkazu bude názov podpríkazu
(
connect,disconnectalebopub) a ďalšie parametre dostane vybraný podpríkaz, - ak príkaz nedostane žiadny parameter alebo dostane neznámy
podpríkaz, vypíše správu
"Error: Wrong usage.".
Príkaz zaregistrujte rovnako ako ostatné príkazy.
Task
Vytvorte podpríkaz connect na pripojenie k MQTT
brokeru, ktorého činnosť implementujete ako operáciu príkazu
.connect().
Operácia .connect() bude mať túto signatúru:
def connect(self) -> None:Operácia nemá žiadne parametre a nič nevracia. Údaje potrebné na pripojenie získa z nastavení v kontexte.
Ak zariadenie nie je pripojené do WiFi siete, operácia vyvolá výnimku
NetworkError z modulu exceptions.py so správou
"Not connected to network.". Ak je zariadenie k brokeru už
pripojené (členská premenná mqtt nemá hodnotu
None), operácia vyvolá výnimku MQTTError so
správou "Already connected to broker.".
Operácia .connect() vytvorí klienta triedy
MQTTClient z knižnice umqtt.simple,
pripojí sa ním k brokeru a uloží ho do členskej premennej kontextu
mqtt. Pri vytváraní klienta použite:
- identifikátor zariadenia
DEVICE_IDako identifikátor klienta, - adresu brokera, port a prihlasovacie údaje z nastavení
mqtt, - konštantu
MQTT_KEEPALIVEako interval udržiavania spojenia, - šifrovanie podľa nastavenia
tls.
Klienta uloží do kontextu až po úspešnom pripojení. Ak sa pripojenie
nepodarí, členská premenná mqtt bude mať naďalej hodnotu
None.
Pri pripájaní môžu vzniknúť dva typy výnimiek. Ak je broker
nedostupný alebo má nesprávnu adresu, vznikne výnimka
OSError. Ak broker pripojenie odmietne (napr. pre nesprávne
meno alebo heslo), vznikne výnimka MQTTException s
návratovým kódom brokera (napr. kód 5 znamená, že klient
nie je autorizovaný). Operácia obe výnimky knižnice zachytí a namiesto
nich vyvolá výnimku MQTTError s vhodnou správou. Stavy
zariadenia tak nebudú závisieť od výnimiek použitej knižnice.
Poznámka
Šifrovanie sa v knižnici umqtt.simple
zapína parametrom ssl, hoci sa v skutočnosti používa jeho
nástupca, protokol TLS. Názov SSL sa udržal z
historických dôvodov, rovnako ako názov modulu ssl.
Parametru ssl odovzdajte objekt triedy
ssl.SSLContext (napr.
ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)). Staršia podoba
ssl=True je v knižnici ponechaná len kvôli spätnej
kompatibilite.
Podpríkaz vypíše:
"Connecting to <broker>..."na začiatku pripájania a"Connected."po úspešnom pripojení,"Error: Not connected to network.", ak zariadenie nie je pripojené do WiFi siete,"Error: Already connected to broker.", ak je zariadenie k brokeru už pripojené,"Error: <text výnimky>", ak sa pripojenie nepodarí.
Príklad použitia podpríkazu môže vyzerať nasledovne:
> mqtt connect
Error: Not connected to network.
> net connect iot-lab Secret.Password
...
> mqtt connect
Connecting to mother.fei.tuke.sk...
Connected.
> mqtt connect
Error: Already connected to broker.
Task
Vytvorte podpríkaz disconnect na odpojenie od
MQTT brokera, ktorého činnosť implementujete ako operáciu
príkazu .disconnect().
Operácia .disconnect() bude mať túto signatúru:
def disconnect(self) -> None:Operácia nemá žiadne parametre a nič nevracia.
Operácia .disconnect() sa od brokera odpojí len vtedy,
ak je zariadenie k nemu pripojené. V opačnom prípade vyvolá výnimku
MQTTError z modulu exceptions.py so správou
"Not connected to broker.". Po odpojení nastaví premennú
mqtt v kontexte na hodnotu None, a to aj
vtedy, keď odpojenie skončí chybou (napr. ak medzičasom spojenie
zaniklo), aby v kontexte nezostal neplatný klient. Chybu knižnice pri
odpájaní (výnimku OSError) operácia zachytí a namiesto nej
vyvolá výnimku MQTTError.
Podpríkaz vypíše:
"Disconnected."po odpojení,"Error: Not connected to broker.", ak zariadenie nie je pripojené k brokeru,"Error: <text výnimky>", ak sa odpojenie nepodarí.
Príklad použitia podpríkazu môže vyzerať nasledovne:
> mqtt connect
Connecting to mother.fei.tuke.sk...
Connected.
> mqtt disconnect
Disconnected.
> mqtt disconnect
Error: Not connected to broker.
Task
Vytvorte podpríkaz pub na odosielanie správ, ktorého
činnosť implementujete ako operáciu príkazu .pub().
Operácia .pub() bude mať túto signatúru:
def pub(self, topic: str, message: dict, retain: bool = False, qos: int = 0) -> None:Význam jednotlivých parametrov metódy je nasledovný:
topic– téma, do ktorej sa správa odošle,message– odosielaná správa v podobe slovníka; operácia ju pred odoslaním serializuje do formátu JSON,retain– príznakretainodosielanej správy,qos– úroveň kvality služby.
Upozornenie
Knižnica umqtt.simple
podporuje len úrovne kvality služby 0 a 1,
preto pri inej hodnote parametra qos operácia vyvolá
výnimku ValueError.
Všetky správy, ktoré zariadenie posiela, sú vo formáte JSON.
Operácia .pub() preto prijíma správu len ako slovník a
serializáciu vykoná na jednom mieste.
Ak zariadenie nie je pripojené k brokeru, operácia vyvolá výnimku
MQTTError z modulu exceptions.py so správou
"Not connected to broker.". Ak sa odoslanie nepodarí (napr.
ak medzičasom spojenie zaniklo a knižnica vyvolá výnimku
OSError), operácia vyvolá výnimku
MQTTError.
Podpríkaz pub dostane dva parametre: tému a správu vo
formáte JSON. Keďže parser rozdelí riadok podľa medzier,
správou bude celý zvyšok riadku za témou. Podpríkaz ju prevedie na
slovník a odovzdá operácii .pub(). Správy posiela vždy s
predvolenými hodnotami parametrov retain a
qos. Podpríkaz vypíše:
"Published to <téma>."po odoslaní správy,"Error: Not connected to broker.", ak zariadenie nie je pripojené k brokeru,"Error: Message is not a valid JSON object.", ak správa nie je platný JSON objekt,"Error: <text výnimky>", ak sa odoslanie nepodarí,"Error: Wrong usage.", ak podpríkaz nedostane tému a správu.
Príklad použitia podpríkazu môže vyzerať nasledovne:
> mqtt pub kpi/caprica/thsensor/ab123cd/data {"message": "Hello world!"}
Error: Not connected to broker.
> mqtt connect
Connecting to mother.fei.tuke.sk...
Connected.
> mqtt pub kpi/caprica/thsensor/ab123cd/data {"message": "Hello world!"}
Published to kpi/caprica/thsensor/ab123cd/data.
> mqtt pub kpi/caprica/thsensor/ab123cd/data Hello world!
Error: Message is not a valid JSON object.
> mqtt pub kpi/caprica/thsensor/ab123cd/data
Error: Wrong usage.
Ak ste postupovali správne, správa sa objaví v klientskej
MQTT aplikácii (napr. MQTT
Explorer), ktorá je pripojená k rovnakému brokeru a prihlásená na
odber témy <base_topic>/#.
Pripojenie k a odpojenie od MQTT brokera
Pripojenie k MQTT brokeru bude úzko súvisieť so stavom
ConnectNetwork. Najprv sa zariadenie pripojí do WiFi siete
a hneď potom sa pripojí k MQTT brokeru.
Podobne to bude platiť aj pre stav DisconnectNetwork,
ktorého súčasťou sa stane odpojenie od MQTT brokera.
Stavový diagram zariadenia sa v tomto kroku nemení. Stavy, ktorých sa krok týka, sa nachádzajú na obrázku 2.
---
# spolocna konfiguracia stavovych diagramov THSensor; vklada sa ako prvy
# riadok bloku ```mermaid, za nim nasleduje "flowchart LR" a samotny diagram
config:
flowchart:
curve: basis
nodeSpacing: 40
rankSpacing: 60
themeVariables:
fontSize: 20px
themeCSS: ".edgeLabel, .edgeLabel p, .labelBkg { background-color: var(--bs-body-bg) !important; }"
---
flowchart LR
start@{ shape: f-circ } --> init(Init)
init --> measurement(Measurement)
init -- "<code>SVC_PIN</code><br>grounded" --> terminal("Service<br>Terminal")
init -- "no/faulty<br><code>settings.json</code> /<br>long button press" --> reset("Factory<br>Reset")
measurement --> connect("Connect<br>Network")
connect --> disconnect("Disconnect<br>Network")
disconnect --> sleep(Sleep)
sleep --> sleepEnd@{ shape: fr-circ }
terminal --> terminalEnd@{ shape: fr-circ }
reset --> resetEnd@{ shape: fr-circ }
%% spolocne styly stavovych diagramov THSensor (farby podla
%% images/thsensor-state.diagram.complete.png); vklada sa na koniec diagramu
%% a stavy sa k nim priradia cez "class <stavy> <styl>"
classDef state fill:#cfe2ff,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef service fill:#cbc3a3,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef reset fill:#ffd6f5,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef config fill:#fddba7,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef error fill:#ffd7d9,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef ota fill:#c3f5c8,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef initial fill:#000,stroke:#fff,stroke-width:2px
classDef final fill:#fff,stroke:#000,stroke-width:2px
class init,measurement,connect,disconnect,sleep state
class terminal service
class reset reset
class start initial
class sleepEnd,terminalEnd,resetEnd final
Obr. 2: Stavový diagram zariadenia so stavmi
ConnectNetwork a
DisconnectNetwork
Task
Rozšírte funkcionalitu stavu ConnectNetwork o pripojenie
k MQTT brokeru.
Stav sa po úspešnom pripojení do WiFi siete pripojí k MQTT
brokeru. Na pripojenie použite operáciu .connect() príkazu
mqtt, ktorú ste vytvorili v predchádzajúcom kroku.
Ak sa pripojenie k brokeru nepodarí (operácia vyvolá výnimku
MQTTError), stav vypíše do konzoly príčinu a pokračuje
stavom DisconnectNetwork, rovnako ako pri neúspešnom
pripojení do WiFi siete. Po úspešnom pripojení pokračuje zariadenie tak
ako doteraz – stavom DisconnectNetwork. Do stavu na
odoslanie nameraných dát ho presmerujeme až v jednom z ďalších
krokov.
Task
Rozšírte funkcionalitu stavu DisconnectNetwork o
odpojenie od MQTT brokera.
Na odpojenie použite operáciu .disconnect() príkazu
mqtt. Zariadenie sa musí najprv odpojiť od MQTT
brokera a až potom od WiFi siete. Inak by sa broker o odpojení
nedozvedel a spojenie by považoval za neočakávane prerušené.
Ak zariadenie k brokeru pripojené nie je (členská premenná kontextu
mqtt má hodnotu None), nebude sa od brokera
odpájať.
Pri odpájaní od brokera môže vzniknúť chyba (výnimka
MQTTError), napríklad ak medzičasom spojenie zaniklo. V
takom prípade stav vypíše príčinu do konzoly, ale v činnosti pokračuje –
zariadenie sa musí odpojiť od WiFi siete v každom prípade.
Task
Overte správnosť svojej implementácie.
Pri overovaní vyskúšajte tieto situácie:
- Ak sú nastavenia WiFi siete aj MQTT brokera správne, tak sa
zariadenie pripojí do siete a k brokeru, potom sa odpojí najprv od
brokera a následne od siete a prejde do stavu
Sleep. - Ak sú prihlasovacie údaje k brokeru nesprávne, tak zariadenie vypíše
príčinu, prejde do stavu
DisconnectNetwork, ktorý odpojenie od brokera vynechá, a potom do stavuSleep. - Ak je broker nedostupný (napr. nesprávna adresa alebo port), tak sa zariadenie zachová rovnako ako v predchádzajúcom prípade.
- Ak sa zariadenie nepripojí do WiFi siete, tak sa o pripojenie k brokeru vôbec nepokúsi a pokračuje rovnako ako v cvičení 4.
Nesprávne hodnoty nastavte priamo v súbore settings.json
a po overení nezabudnite vrátiť správne nastavenia.
Publikovanie nameraných dát
Namerané hodnoty bude zariadenie odosielať v novom stave
PublishData. Zariadenie doň prejde zo stavu
ConnectNetwork, ak sa podarilo pripojiť do siete aj k
MQTT brokeru. Po odoslaní pokračuje stavom
DisconnectNetwork, ktorý ho odpojí od brokera aj od siete.
Ak sa pripojenie nepodarí, zariadenie stav PublishData
vynechá a rovno sa odpojí.
Stavový diagram zariadenia s novým stavom PublishData sa
nachádza na obrázku 3.
---
# spolocna konfiguracia stavovych diagramov THSensor; vklada sa ako prvy
# riadok bloku ```mermaid, za nim nasleduje "flowchart LR" a samotny diagram
config:
flowchart:
curve: basis
nodeSpacing: 40
rankSpacing: 60
themeVariables:
fontSize: 20px
themeCSS: ".edgeLabel, .edgeLabel p, .labelBkg { background-color: var(--bs-body-bg) !important; }"
---
flowchart LR
start@{ shape: f-circ } --> init(Init)
init --> measurement(Measurement)
init -- "<code>SVC_PIN</code><br>grounded" --> terminal("Service<br>Terminal")
init -- "no/faulty<br><code>settings.json</code> /<br>long button press" --> reset("Factory<br>Reset")
measurement --> connect("Connect<br>Network")
connect -- "connected" --> publish("Publish<br>Data")
connect -- "connection<br>failed" --> disconnect("Disconnect<br>Network")
publish --> disconnect
disconnect --> sleep(Sleep)
sleep --> sleepEnd@{ shape: fr-circ }
terminal --> terminalEnd@{ shape: fr-circ }
reset --> resetEnd@{ shape: fr-circ }
%% spolocne styly stavovych diagramov THSensor (farby podla
%% images/thsensor-state.diagram.complete.png); vklada sa na koniec diagramu
%% a stavy sa k nim priradia cez "class <stavy> <styl>"
classDef state fill:#cfe2ff,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef service fill:#cbc3a3,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef reset fill:#ffd6f5,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef config fill:#fddba7,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef error fill:#ffd7d9,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef ota fill:#c3f5c8,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
classDef initial fill:#000,stroke:#fff,stroke-width:2px
classDef final fill:#fff,stroke:#000,stroke-width:2px
class init,measurement,connect,publish,disconnect,sleep state
class terminal service
class reset reset
class start initial
class sleepEnd,terminalEnd,resetEnd final
Obr. 3: Stavový diagram zariadenia so stavom
PublishData
Zariadenie bude telemetriu publikovať do témy
<base_topic>/data. Štruktúra správy, význam jej
položiek, formát časových údajov aj zoznam názvov metrík a ich jednotiek
sú opísané v dokumente Komunikácia cez
MQTT, ktorý platí pre všetky zariadenia v architektúre Smart Department.
Poznámka
Štruktúra telemetrie je inšpirovaná priemyselnou špecifikáciou Sparkplug B, s ktorou však nie je kompatibilná (viac v dokumente Komunikácia cez MQTT).
V kóde správu reprezentujú dva modely z kostry projektu, ktoré sa
nachádzajú v balíku models:
Metric(modulmodels/metric.py) – jedna nameraná hodnota,Telemetry(modulmodels/telemetry.py) – telemetria zariadenia, teda všetky metriky odoslané naraz v jednej správe.
Poznámka
Modely Metric a Telemetry sú súčasťou
kostry projektu, preto ich nevytvárajte ani neupravujte. Vďaka tomu majú
správy zo všetkých zariadení rovnakú štruktúru a ostatné časti systému
ich dokážu spracovať.
Task
Vytvorte stav PublishData, ktorý odošle namerané hodnoty
ako telemetriu zariadenia do témy
<base_topic>/data.
Stav vytvorte v module publish_data.py balíka
states. Z nameraných hodnôt vytvorte objekty triedy
Metric, vložte ich do objektu triedy Telemetry
a ten preveďte na slovník metódou .model_dump(). Na samotné
odoslanie použite operáciu .pub() príkazu mqtt
a tému zložte pomocou metódy base_topic().
Namerané hodnoty vznikajú v stave Measurement, ale
odosielajú sa až v stave PublishData. Zabezpečte preto, aby
sa k nim stav PublishData dostal, napr. prostredníctvom
kontextu.
Ak sa odoslanie nepodarí, vypíšte do konzoly príčinu. Zariadenie bude
v oboch prípadoch pokračovať stavom DisconnectNetwork.
Task
Upravte stav ConnectNetwork tak, aby po úspešnom
pripojení do siete a k MQTT brokeru pokračoval stavom
PublishData.
Ak sa pripojenie do siete alebo k brokeru nepodarí, zariadenie bude
naďalej pokračovať stavom DisconnectNetwork.
Task
Overte, či zariadenie odosiela telemetriu.
Pomocou klientskej MQTT aplikácie (napr. MQTT Explorer) sa pripojte k
rovnakému brokeru ako zariadenie a prihláste sa na odber témy
<base_topic>/data. Potom nechajte zariadenie prejsť
meraním a pripojením do siete.
Ak ste postupovali správne, po každom meraní sa v téme objaví nová
správa so štruktúrou podľa dokumentu Komunikácia cez
MQTT. Overte aj to, že zariadenie pri nedostupnom brokeri stav
PublishData vynechá a pokračuje stavom
DisconnectNetwork.
Publikovanie stavu zariadenia
Zariadenie sa po každom meraní pripojí k brokeru len na krátky čas a
potom sa uspí. Ostatné časti systému by však mali vedieť, či je
zariadenie dostupné, a to aj vtedy, keď sa na odber prihlásia až v čase,
keď zariadenie spí. Preto bude zariadenie zverejňovať svoj stav do témy
<base_topic>/status s príznakom retain.
Broker si tak poslednú správu o stave zapamätá a doručí ju každému
novému odberateľovi.
Zariadenie bude rozlišovať tieto stavy:
online– zariadenie je pripojené k brokeru,sleeping– zariadenie sa korektne odpojilo a spí.
Štruktúra správy a význam jej položiek sú opísané v dokumente Komunikácia cez MQTT.
Task
Vytvorte v module models/status.py model
Status, ktorý bude reprezentovať správu o stave
zariadenia.
Model bude potomkom triedy BaseModel a jeho členské
premenné budú zodpovedať položkám správy podľa dokumentu Komunikácia cez
MQTT. Inšpirovať sa môžete modelmi Metric a
Telemetry z kostry projektu. V tomto kroku bude čas
dt povinnou položkou správy.
Hodnoty oboch položiek overte validátormi:
- čas
dtoverte rovnako ako v modelochMetricaTelemetry– funkciouiso8601()z modulumodels/validators.py, - stav
statusmôže mať len jednu z hodnôt"online","sleeping"alebo"offline".
Model si môžete vyskúšať napríklad v REPL režime:
>>> from models.status import Status
>>> Status(dt='2026-10-31T07:05:00Z', status='online').model_dump()
{'dt': '2026-10-31T07:05:00Z', 'status': 'online'}
>>> Status(dt='2026-10-31T07:05:00Z', status='running')
Traceback (most recent call last):
...
ValueError: ...Poznámka
Overenie, či je hodnota jednou z povolených hodnôt, budete potrebovať
aj v ďalších modeloch. Zvážte preto, či ho neimplementovať ako
samostatnú opakovane použiteľnú kontrolu v module
models/validators.py.
Task
Upravte stav ConnectNetwork tak, aby po pripojení k
brokeru zverejnil stav zariadenia online.
Stav zariadenia zverejnite hneď po úspešnom pripojení k brokeru, ešte
pred prechodom do stavu PublishData. Správu vytvorte ako
objekt triedy Status s aktuálnym časom a stavom
"online" a odošlite ju do témy
<base_topic>/status. Pri odoslaní nastavte parametre
operácie podľa dokumentu Komunikácia cez
MQTT.
Task
Upravte stav DisconnectNetwork tak, aby pred odpojením
od brokera zverejnil stav zariadenia sleeping.
Stav zariadenia zverejnite tesne pred odpojením od brokera, ak je
zariadenie k brokeru pripojené. Správu vytvorte ako objekt triedy
Status s aktuálnym časom a stavom "sleeping" a
odošlite ju do témy <base_topic>/status. Pri odoslaní
nastavte parametre operácie podľa dokumentu Komunikácia cez
MQTT.
Task
Overte, či zariadenie zverejňuje svoj stav.
Pomocou klientskej MQTT aplikácie (napr. MQTT Explorer) sa prihláste na
odber témy <base_topic>/status a nechajte zariadenie
prejsť meraním a pripojením do siete. Ak ste postupovali správne, v téme
sa objaví stav online a pred uspaním zariadenia stav
sleeping.
Odpojte klientsku aplikáciu od brokera a znova sa pripojte, kým
zariadenie spí. Vďaka príznaku retain by ste mali ihneď
dostať posledný známy stav sleeping.
Posledná vôľa
Ak sa spojenie so zariadením neočakávane preruší, napr. pri výpadku
napájania, v téme zostane stav online, hoci zariadenie už
nie je dostupné. Pre takéto situácie poskytuje protokol MQTT
tzv. poslednú vôľu – správu, ktorú klient odovzdá brokeru už
pri pripájaní a broker ju zverejní namiesto neho, ak spojenie s ním
neočakávane zanikne.
Zariadenie bude mať ako poslednú vôľu nastavený stav
offline, ktorý sa zverejní do rovnakej témy ako ostatné
stavy zariadenia.
Task
Upravte model Status tak, aby bolo možné vytvoriť správu
o stave zariadenia bez času.
Keďže k prerušeniu spojenia môže dôjsť kedykoľvek, pri nastavovaní
poslednej vôle nevieme povedať, kedy to bude. Preto položka
dt v modeli Status bude voliteľná. Správa bez
času bude mať v položke dt hodnotu None.
Upozornenie
Validátor sa volá pri každom priradení hodnoty. Ak položke
dt priradíte hodnotu None (napr.
Status(dt=None, status='offline')), musí ju váš validátor
vedieť spracovať.
Task
Rozšírte operáciu .connect() príkazu mqtt o
nastavenie poslednej vôle so stavom zariadenia offline.
Poslednú vôľu nastavte metódou .set_last_will() nad
objektom klienta, a to ešte pred
pripojením k brokeru. Správu vytvorte ako objekt triedy
Status so stavom "offline" bez času. Poslednú
vôľu nastavte pre tému <base_topic>/status s
parametrami podľa dokumentu Komunikácia cez
MQTT.
Upozornenie
Metóda .set_last_will() na rozdiel od operácie
.pub() neprijíma slovník. Správu preto musíte do formátu
JSON serializovať sami.
Task
Overte, či broker po neočakávanom prerušení spojenia zverejní poslednú vôľu zariadenia.
Pri bežnej činnosti je zariadenie pripojené k brokeru len krátko,
preto na overenie použite servisný terminál, v ktorom zostane spojenie
otvorené. V servisnom termináli pripojte zariadenie k brokeru
podpríkazom mqtt connect a potom zariadenie odpojte od
napájania.
Broker zverejní stav offline až vtedy, keď zistí, že sa
klient odmlčal, teda približne po 1,5-násobku intervalu
MQTT_KEEPALIVE. Preto chvíľu počkajte.
Overte aj to, že ak sa zariadenie od brokera odpojí korektne
podpríkazom mqtt disconnect, broker poslednú vôľu
nezverejní.
Správanie overte sledovaním témy
<base_topic>/status pomocou klientskej MQTT
aplikácie, ako je napr. MQTT
Explorer.
Pred odchodom z cvičenia…
Upozornenie
Ak máte dosku RPi Pico 2 WH požičanú od cvičiaceho, pred odchodom z cvičenia prosím:
- Uložte si kód, ktorý ste na cvičení vytvorili.
- Zmažte obsah Flash pamäte mikrokontroléra nahratím firmvéru flash_nuke.uf2. Pozor! Mikrokontrolér neodpájajte od počítača, kým sa vo vašom počítači znova neobjaví ako USB zariadenie.
- Nahrajte na mikrokontrolér najnovší firmvér.
Ďalšie úlohy
Zverejnite po pripojení k brokeru aj informácie o zariadení do témy
<base_topic>/infopodľa dokumentu Komunikácia cez MQTT.Presuňte do modulu
models/validators.pyvalidátory, ktoré je možné opakovane používať.Modul
models/validators.pyje jediný súbor kostry, ktorý môžete meniť. Pri kopírovaní novej verzie kostry ho preto neprepíšte.
Ďalšie zdroje
Smart Department: Komunikácia cez MQTT - organizácia tém, typy správ a ich formát
MicroPython Lib: umqtt.simple - zdrojové kódy, ukážkové príklady a dokumentácia
MicroPython Chapters: Inštalácia balíkov
MicroPython Chapters: Práca s protokolom MQTT
Zoznam zmien
2026-11-05
- Krok „Publikovanie nameraných dát“: položka
unitsmetriky obsahuje kód jednotky podľa štandardu SenML (napr."Cel"pre teplotu a"%RH"pre relatívnu vlhkosť), nie značku jednotky ("°C","%"). Teplotu zariadenie odosiela vždy v stupňoch Celzia bez ohľadu na nastavenieunits(pozri dokument Komunikácia cez MQTT).
2026-11-01
- Krok „Používateľské nastavenia pre MQTT“: nastavenie
topic_prefixmá hodnotu"kpi/caprica/thsensor", pretože broker prepúšťa len povolené témy. - Kroky „Používateľské nastavenia pre MQTT“ a „Príkaz
mqtt“: identifikátor zariadenia je konštantaDEVICE_IDz moduluidentity.py, nie nastaveniedevice_id(pozri cvičenie 4). - Krok „Príkaz
mqtt“: ukážky podpríkazupubpoužívajú povolenú témukpi/caprica/thsensor/ab123cd/data.
2026-10-31
- Krok „Publikovanie stavu zariadenia a posledná vôľa“ rozdelený na
kroky „Publikovanie stavu zariadenia“ (stavy
onlineasleeping, modelStatus) a „Posledná vôľa“ (stavoffline); nová téma<topic_prefix>/<device_id>/status. - Ďalšie úlohy: dobrovoľná úloha na zverejnenie informácií o zariadení
(
info). - Ďalšie úlohy: dobrovoľná úloha na presun validátorov do modulu
models/validators.py.
2026-10-30
- Krok „Publikovanie nameraných dát“: položka
unitsmetriky obsahuje značku jednotky (napr."°C"), nie sústavu jednotiek ("metric").
2026-10-29
- Krok „Príkaz
mqtt“: operácia.disconnect()nastaví členskú premennú kontextumqttna hodnotuNoneaj vtedy, keď odpojenie skončí chybou. - Krok „Príkaz
mqtt“: operácia.disconnect()vyvolá výnimkuMQTTError, ak zariadenie nie je pripojené k brokeru. - Krok „Príkaz
mqtt“: podpríkazconnecthlási chýbajúce pripojenie do WiFi siete správou"Error: Not connected to network."a podpríkazdisconnectchýbajúce pripojenie k brokeru správou"Error: Not connected to broker.". - Krok „Príkaz
mqtt“: operácia.connect()vyvolá výnimkuNetworkError, ak zariadenie nie je pripojené do WiFi siete. - Krok „Príkaz
mqtt“: podpríkazpubhlási chýbajúce pripojenie k brokeru správou"Error: Not connected to broker.". - Krok „Príkaz
mqtt“: operácia.pub()vyvolá výnimkuMQTTError, ak zariadenie nie je pripojené k brokeru. - Krok „Príkaz
mqtt“: operácia.connect()vyvolá výnimkuMQTTError, ak je zariadenie k brokeru už pripojené; podpríkazconnectvtedy vypíše"Error: Already connected to broker.". - Krok „Príkaz
mqtt“: operácie.connect(),.disconnect()a.pub()zachytia výnimky knižnice (OSError,MQTTException) a namiesto nich vyvolajú výnimkuMQTTError; podpríkazpubvypíše"Error: <text výnimky>", ak sa odoslanie nepodarí. - Krok „Pripojenie k a odpojenie od MQTT brokera“: stavy
ConnectNetworkaDisconnectNetworkspracúvajú výnimkuMQTTError. - Krok „Pripojenie k a odpojenie od MQTT brokera“: nová úloha na overenie implementácie.
2026-10-28
- Krok „Príkaz
mqtt“: nová úloha na rozšírenie triedyContexto členskú premennúmqtt. - Krok „Príkaz
mqtt“: operácia.connect()uloží klienta do kontextu až po úspešnom pripojení. - Krok „Pripojenie k a odpojenie od MQTT brokera“: doplnený
stavový diagram a opis úloh pre stavy
ConnectNetworkaDisconnectNetwork.