Hlavní navigace

Tasmota a protokol MQTT: komunikácia klientov so serverom

22. 4. 2021
Doba čtení: 13 minut

Sdílet

 Autor: Ľubomír Patek
Komunikácia firmvéru Tasmota je založená na komunikačnom protokole MQTT. Dnes si tento protokol rozšírený v IoT opíšeme všeobecne so zameraním sa na tie jeho vlastnosti, ktoré sú využité v projekte Tasmota.

Lepšia znalosť protokolu vám neskôr umožní správne nastaviť komunikáciu medzi klientami a serverom a lepšie diagnostikovať problémy, ktoré často vyplývajú z nesprávneho nastavenia.

Opis protokolu som rozdelil do dvoch častí – v tej dnešnej sa budem zaoberať všeobecným opisom protokolu a opisom komunikácie klientov so serverom; v nasledujúcej konkrétnymi nastaveniami firmvéru Tasmota, MQTT servera a testovaním komunikácie so zameraním na vlastnosti, ktoré podľa diskusií na internete aj vlastných skúseností najčastejšie spôsobujú problémy.

Protokol MQTT

Protokol MQTT je otvorený sieťový protokol navrhnutý na prenos správ medzi zariadeniami s obmedzenými výpočtovými prostriedkami, s dôrazom na jednoduchosť, úspornosť a schopnosť fungovať v sieťach s malou šírkou pásma alebo s sieťach dočasným či prerušovaným spojením.

Protokol využíva na prenos spojenie TCP/IP a je založený na sieťovom modeli publish – subscribe. Aj keď bol pôvodne navrhnutý na iný účel, svojimi vlastnosťami spĺňa požiadavky na použitie v zariadeniach domácej automatizácie a preto sa v nich stal značne obľúbený.

Komunikačný model publish – subscribe

V sieťach založených na modeli publish – subscribe klienti medzi sebou nekomunikujú priamo, ale prostredníctvom sprostredkovateľa. Vďaka tomu klienti nemusia poznať topológiu siete, ani nepotrebujú vedieť navzájom o svojej existencii – stačí im informácia o tom, ako sa spojiť so sprostredkovateľom.

Poznámka: Na označenie sprostredkovateľa je zaužívaný termín message broker, špecifikácia MQTT ho označuje jednoducho slovom server. V článku budem považovať oba termíny za rovnocenné.

Komunikácia v sieti využívajúcej model publish – subscribe vyzerá tak, že odosielateľ správy (publikujúci klient) pošle správu sprostredkovateľovi (broker), ktorý následne zaistí jej doručenie všetkým klientom prihláseným na odber (odberateľom) príslušného druhu správ.

Na smerovanie správ sa používa ich zaradenie do kategórií. V MQTT sa kategórie nazývajú témy (topics). Správy zaraďujú do kategórií publikujúci klienti a klienti – odberatelia sa prihlasujú na odber správ v kategóriách, o ktoré majú záujem. Klient môže v určitej kategórií (alebo kategóriách) publikovať a zároveň byť prihlásený na odber inej kategórie (tiež viacerých).

Špecifikácia

Špecifikácia protokolu MQTT sa oproti všeobecnému opisu modelu zaoberá aj dočasným uložením správ na serveri, vytváraním trvalých spojení, spoľahlivosťou doručovania správ a doručovaním správ aj pri prerušovanom spojení.

Protokol je štandardom organizácie OASIS a v súčasnotsi sú rozšírené dve verzie: 3.1.1 a 5, ale je možné, že sa stretnete aj so staršou verziou 3.1.

Štruktúra paketu

Pred opisom samotnej komunikácie si stručne opíšeme štruktúru paketu MQTT, ktorý pozostáva z hlavičky, rozdelenej na časť s pevnou štruktúrou (fixná hlavička) a časť s variabilnou štruktúrou (variabilná hlavička) a z prenášaných dát (payload).


Štruktúra paketu MQTT

Fixná hlavička je prítomná vo všetkých typoch MQTT paketov a vždy obsahuje v prvých štyroch bitoch informáciu o type paketu, za ktorými nasledujú štyri príznakové bity (ktoré prakticky využíva len paket PUBLISH). Za prvým bajtom vždy nasleduje informácia o veľkosti zvyšnej časti paketu, na ktorej uloženie sa použije jeden až štyri bajty (podľa veľkosti paketu).

Vo variabilnej hlavičke sú nastavené parametre prenosu pre niektoré typy paketov a jej štruktúra závisí od typu paketu.

Niektoré typy paketov môžu obsahovať aj tretiu časť – prenášané dáta. V tejto časti sa môžu prenášať niektoré ďalšie riadiace dáta (napr. identifikátor klienta, názov témy apod.). Samotné dáta aplikácie v nej prenáša len paket typu PUBLISH a je preň voliteľná – je možné publikovať aj prázdnu správu.

Opis komunikácie

Poďme sa teraz bližšie pozrieť na to, ako vyzerá komunikácia pomocou protokolu MQTT a na obsah niektorých typov paketov.


Vytvorenie a ukončenie spojenia MQTT

MQTT server po spustení čaká na spojenie na vyhradenom porte TCP (1883, alebo 8883 pre spojenie TLS/SSL). Po vytvorení sieťového spojenia môže začať komunikácia MQTT, ktorú budeme nazývať reláciou (session) a vyzerá nasledovne:

Reláciu začína klient tým, že doručí serveru požiadavku na spojenie v pakete CONNECT a server ju následne potvrdí pomocou paketu CONNACK. Teraz, v prípade akceptovaného spojenia môže nastať výmena správ – publikovanie, prihlásenie sa na odber, odhlásenie sa z odberu a udržiavanie spojenia pomocou paketov PING.

Výmena správ pokračuje až do ukončenia spojenia, ktoré môže nastať buď korektným spôsobom – rozpojením spojenia TCP po doručení paketu DISCONNECT serveru, alebo rozpojením z iných dôvodov, ktorému budeme hovoriť neočakávané alebo nečisté rozpojenie.

Pozrime sa teraz na to, aké informácie prenášajú tie pakety, ktoré sú dôležité pre založenie celej komunikácie a pakety, v ktorých sa prenášajú správy.

Paket CONNECT

Prvý paket, ktorým klient žiada o začatie relácie je paket CONNECT. Tento paket nastavuje parametre spojenia, ktoré sú dôležité pre celú nasledujúcu komunikáciu – sú nimi identifikátor klienta (Client ID), príznak Clean session umožňujúci vyžívať perzistentné spojenia (v Tasmota sa nepoužívajú), údaj o maximálnom čase bez spojenia Keep alive a voliteľne závet (obsahujúci položky Will topic, Will message, Will QoS, Will retain) a prihlasovacie údaje, ktorými sa klient preukazuje serveru v prípade, že to server vyžaduje. Nasledujúci obrázok znázorňuje, ako sú jednotlivé parametre v pakete uložené:


Paket Connect

Keep alive

Hodnota Keep alive je maximálny čas (v sekundách), ktorý môže uplynúť medzi okamžikom ukončenia prenosu jedného paketu a okamžikom začiatku prenosu nasledujúceho. Tento interval nastavuje klient a je jeho zodpovednosťou dodržiavať ho. Ak klient nemá čo poslať, spojenie udržiava pomocou paketu PINGREQ, na ktorý mu server odpovie paketom PINGRESP. Pri prekročení tohoto intervalu server preruší sieťové spojenie. Za nadviazanie nového spojenia zodpovedá klient.

Will

Protokol disponuje aj mechanizmom na informovanie ostatných klientov o prerušení spojenia. Ak je v pakete CONNECT nastavený príznak Will, server v prípade prerušenia spojenia publikuje v mene klienta špeciálnu správu, ktorú nazýva závet (Will).

Poznámka: Závet sa nelíši od ostatných MQTT správ svojou štruktúrou, ani spôsobom publikovania – rozdiel je len v tom, že musí byť definovaný hneď na začiatku spojenia (pred začatím výmeny správ) a ostatným klientom bude doručený len v prípade neočakávaného rozpojenia relácie (bez doručenia paketu DISCONNECT serveru).

Paket CONNACK

Server potvrdí klientovi prijatie paketu CONNECT doručením paketu CONNACK, v ktorom buď oznámi akceptovanie žiadosti o vytvorenie relácie, alebo ju odmietne a oznámi dôvod odmietnutia.

Po vytvorení spojenia môže klient pokračovať publikovaním správy (pomocou paketu PUBLISH), alebo prihlásením sa na odber správ (pomocou paketu SUBSCRIBE).

Podobne, ako sa paketom CONNECT nastavujú parametre platné pre celú reláciu, sa neskôr nastavujú ďalšie parametre komunikácie – samostatne pre publikovanie a samostatne pre odber správ. Predtým, ako sa na niektoré z nich pozrieme bližšie, sa pokúsim objasniť koncept spoľahlivosti doručovania, ktorý je spoločný pre publikovanie aj doručovanie a tiež pre už spomenuté publikovanie závetov.

Spoľahlivosť doručovania správ – QoS

Priebeh nasledujúcej výmeny paketov závisí od požadovanej spoľahlivosti doručenia prenášanej správy. Protokol MQTT ponúka tri úrovne spoľahlivosti doručovania správ (QoS, quality of services):

  • pri úrovni QoS = 0 sa vykoná len jeden pokus o doručenie bez spätného potvrdzovania (doručenie správy nie je zaručené, ale nestane sa, že by bola doručená viackrát)
  • pri úrovni QoS = 1 sa vykoná najmenej jeden pokus o doručenie a jej doručenie bude potvrdené (môže sa stať, že správa bude doručená viackrát)
  • pri úrovni QoS = 2 bude správa doručená presne len raz (správa bude doručená, jej doručenie potvrdené a nemôže sa stať, že bude doručená viackrát)

Príznak QoS nastavuje publikujúci klient v pakete PUBLISH. Server pri prijímaní paketu použije takú úroveň QoS, akú si klient nastavil (predpokladajme, že server pozná všetky úrovne). Server potom takúto správu doručí (stále pomocou paketu PUBLISH) klientom prihláseným na odber príslušnej témy najviac s takou QoS, akú si každý z nich nastavil v pakete SUBSCRIBE. Je teda možné, že správa bude odberateľovi doručená s nižšou úrovňou, aká bola nastavená publikujúcim klientom.

Paket PUBLISH

Prostredníctvom paketu PUBLISH doručuje klient serveru správu aplikácie a server ju potom prostredníctvom neho distribuuje klientom prihláseným na odber príslušnej témy.


Paket Publish

Prijatie paketu PUBLISH sa potvrdzuje v závislosti od úrovne QoS nasledovne:

  • pri QoS 0 sa nepotvrdzuje
  • pri QoS 1 sa potvrdzuje paketom PUBACK
  • pri QoS 2 sa potvrdzuje výmenou paketov PUBREC (publish received), PUBREL (publish release) a PUBCOMP (publish complete)

Knižnica PubSubClient použitá v Tasmota dokáže pri publikovaní správ využiť len úroveň QoS 0, pri prihlásení sa na odber aj QoS 1.

Podržanie správy na serveri

Pri bežnom doručovaní server prijatú správu zmaže hneď po jej rozoslaní klientom aktuálne prihláseným na odber príslušnej témy. Ak sa potom na odber v rovnakej téme prihlási nový klient, až do času publikovania novej správy ostane bez dát.

Protokol preto ponúka možnosť uloženia poslednej správy na serveri. Ak je v pakete PUBLISH nastavený príznak retain, server správu nevymaže hneď po jej publikovaní, ale uloží si ju až do momentu publikovania novej správy v príslušnej téme. V prípade, ak sa na odber správ prihlási nový klient v čase, keď už bola správa publikovaná a nová ešte nie je dostupná, server mu doručí túto uloženú správu.

Dobrým príkladom na použitie príznaku retain sú už spomenuté závety – je vhodné, aby informácia o odchode klienta zo siete bola dostupná nielen klientom pripojeným v čase jej publikovania, ale aj tým, ktorí sa pripoja neskôr.

Paket SUBSCRIBE

Paketom SUBSCRIBE sa klient prihlasuje na odber tém. Ku každej téme priradí aj maximálnu hodnotu úrovne QoS, s akou mu server môže doručovať správy.


Paket Subscribe

Prijatie a spracovanie paketu SUBSCRIBE potvrdí server paketom SUBACK, ktorý obsahuje buď návratový kód (v prípade viacerých tém zoznam kódov) potvrdzujúci úroveň QoS, s ktorou bude správy doručovať, alebo kód indikujúci chybu.

Paket UNSUBSCRIBE

Dáta doručované paketom UNSUBSCRIBE obsahujú zoznam tém, z odberu ktorých sa chce klient odhlásiť. Server odhlásené témy odstráni a odhlásenie potvrdí prázdnym paketom UNSUBACK. Aj po odhlásení sa z odberu musí server dokončiť neukončené doručovanie paketov s úrovňou QoS 1 a 2.

Témy v MQTT

Na témy sa možno pozerať ako na už spomenuté kategórie, do ktorých sa zaraďujú správy; niekomu možno pomôže predstava komunikačných kanálov, do ktorých klienti správy publikujú a z ktorých ich odoberajú.

Z pohľadu protokolu je názov témy len textový reťazec, ktorý si server uloží pri prihlásení sa klienta na odber správ. Po prijatí správy publikovanej niektorým klientom server porovná reťazec publikovanej správy s uloženými reťazcami odoberajúcich klientov a správu doručí tým z nich, ktorých reťazce sa zhodujú s reťazcom publikovanej správy.

Názov témy môže byť štrukturovaný do viacerých úrovní pomocou separátora úrovní – znaku “/” a je vyhodnocovaný ako celok vrátane separátorov, preto názvy tém

/prízemie/kúpeľňa
prízemie/kúpeľňa
prízemie/kúpeľňa/

budú považované za tri rôzne názvy tém. Použitie separátora na začiatku a na konci je možné, ale nie je odporúčané, pretože takéto použitie môže pri nedostatočnom zvážení či nepozornosti spôsobovať problémy.

Reťazec označujúci názov témy musí obsahovať minimálne jeden znak kódovaný pomocou UTF-8, rozlišuje sa v ňom medzi malými a veľkými písmenami a môže obsahovať aj medzery, ale tým je z podobných dôvodov ako pri použití separátora lepšie sa vyhnúť. Maximálna veľkosť reťazca je obmedzená špecifikáciou na 65 kB (ďalej ju môže obmedziť aplikácia). Viac podrobností nájdete v špecifikácii.

Filtre tém

V článku som doteraz hovoril len o možnosti prihlásiť sa na odber tém pomocou ich úplných názvov. Protokol však umožňuje používať aj filtre tém, ktoré umožňujú prihlásiť odber viacerých tém naraz prostredníctvom zástupných znakov (žolíkov).

Žolíky sú dva a možno ich použiť len na zastúpenie celej úrovne (nie jednotlivých znakov):

  • znak + zastupuje jednu úroveň
  • znak # zastupuje všetky úrovne napravo od neho

Špecifikácia používa termín filter témy len v súvislosti s odberom, inak používa termín názov témy. Znamená to, že zástupné znaky je možné používať len pri prihlasovaní sa na odber; publikovať treba každú tému individuálne.

Príklady:

  • filter kuchyňa/# zastupuje všetky úrovne v kuchyni
  • filter # zastupuje úplne všetky témy na serveri
  • filter prízemie/+/teplota prihlási klienta na odber tém prízemie/kúpeľňa/teplota, prízemie/kuchyňa/teplota atď.
  • filter /# umožňuje vyhľadať neodporúčané použitie separátora na začiatku názvu témy

Špeciálne témy

Názvy tém začínajúce znakom $ sú určené na špeciálne použitie a klienti ich môžu len čítať. Téma $SYS je zaužívaná ako prefix tém obsahujúcich správy špecifické pre server.

Závet

Zhrňme si informácie, ktoré sme už o závete spomenuli: je to správa, ktorú v prípade, že klient pri nadväzovaní relácie vyplnil príslušné polia v pakete CONNECT, server uloží, aby ju mohol v prípade neočakávaného (nečistého) ukončenia relácie publikovať v jeho mene.

Podmienkou uloženia závetu na serveri je nastavenie príznaku závetu (príznak Will), definovanie názvu témy závetu (Will Topic) a správy závetu (Will Message), ktoré si server asociuje so spojením. Rovnako ako pre iné správy, aj pre závet je možné nastaviť príznaky Will QoS a Will Retain.

Poznámka: Špecifikácia používa termín Will. V literatúre a v programoch nájdete v bežnej reči viac zaužívaný termín Last Will and Testament (LWT).

Informovanie o dostupnosti zariadenia

Ak je pre aplikáciu dôležitejšie poznať stav zariadenia (jeho dostupnosť na sieti), ako informáciu o tom, či bolo spojenie ukončené korektne alebo nie, je možné doplniť mechanizmus závetov tak, že okrem uloženia správy závetu (napr. “offline”) na serveri (ktorá bude publikovaná len v prípade nečistého rozpojenia relácie), publikuje klient hneď po nadviazaní spojenia – teraz už vo vlastnej réžii – v téme totožnej s témou závetov aj informáciu o príchode klienta na sieť (napr. správu “online”) a prípadne aj ďalšiu správu “offline” pri jeho korektnom odchode zo siete. Získame tak úplnú informáciu o dostupnosti zariadenia na sieti.

Vyššie opísaný spôsob využíva napríklad aj Tasmota a Home Assistant. Home Assistant nazýva tému opisnejšie – “availability topic”, Tasmota ostáva pri názve LWT.

Podobne ako závety, aj správy o dostupnosti je vhodné publikovať s nastaveným príznakom retain, aby bola informácia dostupná aj pre klientov, ktorí neboli v čase ich publikovania dostupní.

Poznámka: Správe posielanej pri štarte zariadenia sa v bežnej reči hovorí aj birth message a obom niekedy birth and LWT.

Vlastnosti protokolu MQTT nevyužité v Tasmota

V krátkosti sa ešte pozrime na vlastnosti protokolu, ktoré Tasmota nevyužíva, alebo ich využíva len čiastočne. Pri opise QoS sme už spomenuli, že Tasmota vie publikovať správy len s úrovňou QoS 0, odoberať aj s úrovňou QoS 1 a úroveň QoS 2 nevyužíva.

Trvalé relácie

Bežná relácia trvá len toľko, koľko trvá sieťové spojenie (TCP) a po jej ukončení server zmaže všetky odbery klienta (zoznam filtrov tém, na odber ktorých je prihlásený a ďalšie informácie asociované s reláciou). Protokol ponúka aj možnosť vytvárania trvalých relácií, ktoré prežijú aj prerušenie sieťového spojenia – server v takom prípade všetky informácie súvisiace s odbermi klienta uloží a po nasledujúcom nadviazaní spojenia pokračuje v ich používaní. Takejto relácii sa hovorí aj trvalá (durable, persistent) a nastavuje sa príznakom clean session v pakete CONNECT.

Klienti obvykle využívajú len jeden z týchto dvoch typov spojenia a neprepínajú medzi nimi. Vo firmvéri Tasmota je príznak clean session trvalo nastavený na 1 (spojenia nie sú trvalé) a zmeniť sa to dá len pri kompilácii.

Trvalé relácie sú vhodné napríklad pre klientov, ktorí sú väčšinu času offline a majú tak obmedzené prostriedky (výpočtové, energetické, sieťové pripojenie), že by ich nadmerne zaťažovalo aj samotné založenie novej relácie pred každým prenosom.

MQTT a bezpečnosť

Protokol MQTT sa sústreďuje na samotný prenos správ a bezpečnosť prenosu ponecháva na prenosový protokol – na šifrovanie TCP spojenia je možné použiť protokol TLS/SSL. Samotný protokol umožňuje serveru vyžadovať prihlasovanie klientov menom a heslom (aj per client), ktoré sa však v prípade nešifrovaného sieťového spojenia prenášajú nešifrované.

Root_skoleni


Verím, že aj keď dnešný diel nebol veľmi zábavný, v budúcnosti vám pomôže pri riešení problémov a ušetrí čas pri prehľadávaní diskusií na internete – mnohé z problémov sa v nich opakujú a ich častou príčinou je neznalosť protokolu a jeho implementácie vo firmvéri Tasmota.

Nasledujúci diel by už mal byť pre používateľa firmvéru Tasmota o niečo zaujímavejší – povieme si niečo o skladaní tém z jednotlivých úrovní, o nastaveniach súvisiacich s protokolom MQTT a prakticky si vyskúšame komunikáciu.

Odkazy

Autor článku

Priaznivec open source, ktorý rád skúma, ako veci fungujú.