Ciele
- Vyskúšaj si MQTT bez programovania
- Vyskúšaj si MQTT v programe s JavaScriptom
- Naučiť sa používať zástupné symboly pri prijímaní správ
- Oboznámiť sa s prídavnými vlastnosťami MQTT správ
- Vyskúšať si spracovať údaje zo senzorov OpenLabu
MQTT (úvodne informácie)
MQTT je protokol pre komunikáciu prostredníctvom správ založený na spôsobe komunikácie Klient<->Server s komunikáčným vzorom publikovania a odoberánia (publish/subscribe) správ. Hlavné charakteristiky MQTT protokolu sú:
- nízke nároky na systemové prostriedky,
- otvorenosť,
- jednoduchosť,
- a dizajn s ohľadom na jednoduchú implementáciu klienta.
Vďaka týmto charakteristikam je tento protokol ideálny pre použitie v rôznych situáciach ako sú rôzne obmedzené prostredia pre komunikáciu medzi strojmi (Machine to Machine - M2M) a alebo aj v prostredí Internetú vecí (IoT), kde sú nízke nároky na systemové prostriedky a nízky dátový tok veľmi dôležité.
Výhodou MQTT je aj to, že je to primárne binárny protokol s minimálnym množstvom prídavných informácii v každom pakete, čo je výhoda aj oproti známemu protokolu HTTP, ktorý má omnoho viac informácii v hlavičkách každej správy.
Postup
Krok 1: Prvotná práca s MQTT
Na začiatok si poďme vysvetliť základné pojmy v MQTT s ktorými sa môžeš stretnúť:
- broker - server, ktorý zabezpečuje vzájomnú komunikáciu medzi klientami a uchováva informácie o tom, aké správy ktorý klient očakáva, na odber akých správ sa prihlásil
- klient - aplikácia alebo IoT zariadenie, ktoré implementuje MQTT protokol a bude posielať alebo prijímať správy
- topic - oblast, textová adresa na ktorú sa posielajú správy, štandardne vo formáte cesty (path), ktorej jednotlivé podčasti sú oddelené lomítkom (
/
) (napr.kpi/door/1/temp
) - správa - štandardne binárna správa odoslana z klienta na špecifikovaný topic
- subscription - prihlasenie na odber správ z definovanej oblasti
Je teda jasné, že pre správnu komunikáciu prostredníctvom MQTT je potrebné aby sme mali dostupný nejaký broker. Existujú viaceré implementácie MQTT brokera podľa otvoreného štandardu:
- Mosquitto (open source implementácie)
- HiveMQ (komerčná implementácia)
- iné (pozri porovnanie)
Niektoré spoločnosti ponúkajú aj rôzne verejne dostupné nasadené broker servery, ktoré sú spísané aj v tomto zozname.
Ty si vyskúšaš verejný broker, ktorý poskytuje HiveMQ, keďže je aktuálne najrýchlejší a dostupný bez prihlásenia alebo používateľského účtu aj keď v neplatenej verzii nepodporuje zabezpečenú komunikáciu.
Úloha 1.1
Choď na stránku verejného MQTT klienta od HiveMQ a po pripojení sa na predvyplnený broker sa prihlás na odber správ z topicu test/kpi/iotclass
(QoS zatiaľ nechaj na 0). Mal by si tam dostať úvodnú správu, ktorú som ti tam poslal.
Úloha 1.2
Skús na rovnaký topic test/kpi/iotclass
poslať akúkoľvek textovú správu. Mal by si ju zároveň prijať a rovnako by si mal prijať aj správy od ostatných svojích spolužiakov.
Teraz si si vyskúšal ako je možné jednoducho posielať a prijímať správy cez MQTT broker. Môžeš si skúsiť poslať aj viacero správ a aj na iné topicy. Reálne si môžeš vymyslieť akýkoľvek názov topicu, no neodporúča sa tvoriť príliš dlhé názvy, nakoľko názov musí putovať v hlavičke každej správy a tým ju zväčšuje.
Krok 2: Použitie klienta MQTT vo vlastnom programe
Vytvorenie klienta MQTT by nemalo byť veľmi zložité, avšak aj to je istým spôsobom práca naviac. Našťastie existujú už viaceré implementácie MQTT klienta v podobe knižníc pre rôzne jazyky. Najväčší projekt v tomto ohľade je aktuálne Paho MQTT od Eclipse, ktorý poskytuje implementácie v jazykoch:
- C,
- C++,
- Java,
- JavaScript,
- Python,
- Go,
- Rust,
- C# (.NET)
Samozrejme existujú aj implementácie MQTT klienta určené pre testovanie správ a to pre jednotlivé operačné systémy, stačí len hľadať pre daný operačný systém (Windows, Android, iOS).
My si kvôli jednoduchosti vyskúšame vytvoriť klienta v JavaScripte.
Úloha 2.1
Nájdi si na stránke projektu Paho MQTT informáciu o tom ako použiť klienta pre Javascript. Skús sa dostať až na stránku so zdrojovými kódmi (GitHub) tohto projektu nakoľko tam nájdeš informácie o zmenách vo verzii 1.1.0, ktorú budeme ďalej používať.
Teraz si vytvoríš stránku, ktorá bude simulovať IoT senzor na základe pohybov kurzora po stránke. Výsledok má byť to, že stránka bude posielať pri každom pohybe kurzora správu o aktuálnej polohe kurzora.
Úloha 2.2
Vytvor si základnú kostru HTML súboru u seba na počítači:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Moje prvé MQTT</title>
<style>
body {
margin:0;
}
.fullscreen {
position:fixed;
height:100%;
width:100%;
}
</style>
</head>
<body onmousemove="moved(event)">
<div class="fullscreen"></div>
<!-- Javascript inline -->
<script type="text/javascript">
function moved(e) {
var data = {
x:e.clientX,
y:e.clientY
}
console.log(data);
}
</script>
</body>
</html>
Poznámka
Používame CSS na doštýlovanie stránky:
- v
body
sa odstrania zbytočné odsadenia (margin), ktoré sú štandardne v prehliadači - triedu (class)
fullscreen
používame za účelom roztiahnutia elementu<div>
na celú zobrazenú obrazovku, lebo ináč byonmousemove
nereagovalo na pohyb kurzora v každom mieste
Táto HTML kostra zabezpečí to, že do konzoly tvojho prehliadača (štandardne zobrazená stlačením klávesy F12) sa budú zapisovať údaje o súradniciach X a Y polohy kurzora. Vyskúšaj si či a ako to funguje!!!
Teraz ideme tieto správy z webovej stránky aj reálne posielať na MQTT. Preto budeme potrebovať si pridať Paho MQTT implementáciu klienta a ju aj použiť.
Úloha 2.3
Pridaj do hlavičky <head>
odkaz na Paho MQTT Javascript implementáciu vo verzii 1.1.0
dostupnú na neoficiálnom CDN:
<script src="https://cdn.jsdelivr.net/npm/paho-mqtt@1.1.0/paho-mqtt.min.js" type="text/javascript"></script>
Poznámka
Verzia 1.1.0
nie je ešte úplne finálna, avšak podporuje automatické znovunadviazanie spojenia s MQTT brokerom v prípade pádu spojenia a taktiež zmenila názvy balíkov.
Ideme inicializovať kód MQTT klienta v Javascripte.
Úloha 2.4
Do Javascriptu stránky pridaj inicializovanie MQTT klienta:
var clientId = "example_clientID_" + new Date().getTime();
var mqttClient = new Paho.Client("broker.hivemq.com", 8000, "/mqtt", clientId);
mqttClient.onConnectionLost = onConnectionLost;
mqttClient.onMessageArrived = onMessageArrived;
mqttClient.connect({onSuccess: onConnect, reconnect: true});
function onConnect() {
console.log("onConnect");
}
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
console.log("onConnectionLost:" + responseObject.errorMessage);
}
}
function onMessageArrived(message) {
console.log("onMessageArrived for topic '" +message.destinationName +"': " + message.payloadString);
}
Poznámka
clientId
slúži na jednoznačné identifikovanie klienta, na jeden broker nemôžu byť naraz pripojení dvaja klienti s rovnakým ID! Preto ho nastavujeme nejakou textovou hodnotou a pre istotu aj aktuálnym časom, môžete si textovú hodnotu zmeniť a použiť tam aj svoje meno.
Funkcie on...
nie su povinné, ale umožňujú nám programovo reagovať na rôzne udalosti a neskôr ich budeme používať, zatiaľ je v ich tele len výpis informácie do konzoly. Na pripojenie používame verejný broker broker.hivemq.com
s portom 8000
a štandardnou cestou /mqtt
No a teraz konečne sa posnažíme odoslať MQTT správu.
Úloha 2.5
Zabezpeč odoslanie správy o aktuálnej polohe kurzoru do topicu kpi/test/tvojepriezvisko/cursor
(napr. kpi/test/lakatos/cursor
), pričom format správy bude v podobe serializovaného JSON objektu data
.
Poznámka
Metódu na odoslanie správy si vyhľadaj v dokumentácii k Javascript Paho MQTT klient. Pričom ako hodnotu parametra qos
nastav zatiaľ 0
a ako hodnotu parametra retained
nastav false
.
Poznámka
Serializáciu JSON objektu vieš vykonať pomocou štandardnej metódy dostupnej v Javascripte JSON.stringify(data)
.
Teraz si to over prihlásenim na odber správ z tvojho topicu cez webového klienta, ktorého si používal na začiatku cvičenia aby si zistil, že či tie správy reálne chodia.
Ak si si to dobre overil, tak poďme si vyskúšať ako sa vieš prihlásiť na odber správ aj v svojom programe.
Úloha 2.6
Prihlás sa na odber správ v programe a zobraz hodnotu poslednej z prijatých správ v jednoduchom HTML elemente.
Do elementu <div class="fullscreen">
vlož nový element, ktorý bude zobrazovať prijaté dáta.
<h2>Dáta: <span id="tempData">?</span></h2>
Do funkcie onConnect
pridaj volanie pre prihlásenie sa na odber správ z tvojho topicu (kpi/test/tvojepriezvisko/cursor
).
Poznámka
Metódu na prihlásenie sa na odber správ (subscribe) z určeného topicu si znova nájdi v dokumentácii.
A do funkcie, ktorá sa stará o spracovanie prijatých správ onMessageArrived
pridaj spracovanie prijatej správy pomocou zobrazenia obsahu v elemente s id tempData
:
document.getElementById('tempData').innerHTML = message.payloadString;
Over si správnu funkcionalitu spustením web stránky.
A teraz pre lepší prehľad si poďme polohu kurzora vizualizovať použitím HTML Canvas elementu.
Úloha 2.7
Prv pridaj <canvas>
element do html stránky hneď za aktuálny jediný <div>
element.
<canvas id="canvas" class="fullscreen"></canvas>
Do Javascriptu pridajme kód pre inicializáciu a následne aj pomocnú funkciu pre kreslenie:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//change pixel size of canvas to real size of window (must be called after canvas element is drawn)
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function drawCursor(x,y,name) {
//First clear canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//init variables
var diameter = 10;
var fontSize = 16;
//Draw circle
ctx.fillStyle = "#000000";
ctx.beginPath();
ctx.arc(x,y,diameter,0,2*Math.PI);
ctx.fill();
if (name) {
//Draw text on top of circle
ctx.font = fontSize+"px Arial";
ctx.fillText(name,x-diameter,y-fontSize);
}
}
Teraz spojazdni kreslenie kurzora z prijatej správy použitím dodanej funkcie drawCursor
. Pričom ako parameter name
môžeš použiť svoje meno alebo ani nemusíš použiť nič.
Poznámka
Deserializáciu JSON objektu prijatého v MQTT správe vieš vykonať pomocou štandardnej metódy dostupnej v Javascripte JSON.parse(inputString)
. Potom už pristúpiš k jednotlivým hodnotám ako k členským premenným daného objektu.
Teraz si môžeš overiť správny príjem správ aj od ktoréhokoľvek spolužiaka, stačí že zadáš jeho topic do subscribe metódy. Vyskúšaj si to!!!
Poznámka
Ak tvoj spolužiak má iný rozmer okna prehliadača, môže sa stať, že sa kurzor nebude vykresľovať u teba ak poloha jeho kurzoru presiahne tvoje rozlíšenie.
Krok 3: Príjem správ z viacerých topicov
Už nám funguje príjem správ z jedného zvoleného topicu. Avšak, čo by bolo potrebné aby sme dokázali prijímať správy z viac ako jedného topicu?
Samozrejme môžeme sa MQTT klientom prihlásiť na odber správ aj od viacerých topicov.
Úloha 3.1
Pridaj ďalší riadok s metódou subscribe
pre topic svojho spolužiaka, tak aby si bol prihlásený na odber z dvoch topicov. Hýbte svojimi kurzormi a sledujte, čo sa bude zobrazovať.
Predpokladám, že zobrazenie nebolo veľmi dobré, grafické znázornenie kurzora mohlo skákať na rôzne polohy.
Úloha 3.2
Pridaj do svojho kódu zapamätanie si polohu každého kurzoru a následne pri vykresľovaní prejdite všetkými kurzormi. Vytvorte globálnu premennú:
var cursors = {};
Následne pri prijatí správy použite informáciu o topicu message.destinationName
ako kľúč na uloženie polohy:
cursors[message.destinationName]=deserializedDataFromMessage;
Pri vykresľovaní prechádzajte všetkými uloženými kurzormi:
for(var i in cursors) {
var currentCursor = cursors[i];
...
//vykreslenie currentCursor
...
}
Upozornenie
Pred uvedený cyklus for
je potrebné vyňať z funkcie drawCursor
časť, ktorá sa stará o vyčistenie obrazovky (clearRect
) ináč sa bude obrazovka pred vykreslením každého kurzora vymazávať a neuvidíte naraz všetky kurzory.
Poznámka
Hodnotu i
v tele cyklu môžete použiť aj ako hodnotu pre parameter name
vo funkcii drawCursor
pre zobrazenie mena. Prípadne môžete získať len hodnotu v skutočného mena z topicu použitím regulárneho výrazu, rozbytia reťazca na podreťazce alebo iného spracovania textu.
Vyskúšaj si, či to správne funguje.
Je to celé pekné, nie? No čo by to znamenalo v kóde, ak by sme chceli získať informáciu o kurzora od každého spolužiaka?
Zástupné symboly
Práve na to máme v prípade prihlasovania sa na odber (subscribe) správ v MQTT možnosť použiť zástupné symboly (wildcard).
Existujú dva typy zástupných symbolov:
Jednoúrovňový "+
" - slúži na náhradu akéhokoľvek mena jednej časti cesty topicu
- môže byť uvedený ako náhrada medzi akýmikoľvek lomítkami
- nahrádza len 1 úroveň (len medzi dvomi lomítkami)
- napr.:
kpi/door/+/status
(zahrnie napr.kpi/door/10/status
,kpi/door/automatic/status
, ale nezahrniekpi/door/automatic/20/status
)
Viacúrovňový "#
" - slúži na náhradu viacerých mien časti topicu
- musí byť uvedený len na konci topicu a pred ním musí byť lomítko "
/
" - napr.:
kpi/door/#
(zahrnie čokoľvek ako jekpi/door/1/temp
,kpi/door/3/status
,kpi/door/automatic/20/status
, ale nezahrniekpi/lamp/3/status
)
Úloha 3.3
Keď už vieme o zástupných symboloch, upravte svoj kód tak aby zobrazoval informácie o polohe kurzoru od každého vášho spolužiaka aj od Vás. Použite ale len jedno volanie subscribe
.
Upozornenie
Do jedného topicu môže posielať správu viacero klientov, avšak to nie je odporúčané, ak daný topic má slúžiť ako zdroj údajov senzoru. V takomto prípade by každé zariadenie (senzor) malo mať vlastný topic. Avšak ak daný topic slúži na odosielanie príkazov pre iné IoT zariadenie (napr. príkaz pre zapnutie/vypnutie skenovania) môže do jedného topicu posielať správy aj viacero klientov.
Krok 4: Prídavné vlastností správ
Každá MQTT správa má špecifikované 4 vlastnosti:
- topic - adresa na ktorú bola správa zaslana
- telo - samotný obsah správy
- QoS - Quality of Service - definovanie v akom režime bude správa doručovaná
- uchovanie (retention) - informácia o tom, že či má byť správa uchovaná na brokeri pre daný topic
QoS
Quality of Service (QoS) definuje pre spojenie a správu to, že akým najvyšším spôsobom bude doručovaná. Táto vlastnosť môže nadobudať číselné hodnoty od 0 do 2.
Význam QoS hodnôt:
0
- správa doručena najviac 1x ale nemusí byť ani raz (At most once) - doručenie bez potvrdenia (1x komunikácia, len 1 smerom)1
- zaručené doručenie správy aspoň 1x (At least once) - doručenie s 1x potvrdením (2x komunikácia - 1x oboma smermi)2
- zaručené doručenie správy presne 1x (Exactly once) - doručenie s 2x potvrdením pre účely odstránenia možnosti doručenia duplikátu (4x komunikácia - 2x oboma smermi)
V MQTT sa nachádzajú 2 spôsoby definovania QoS pre doručovanie správy:
- doručenie správy z klienta, ktorý správu posiela na broker
- doručenie správy z brokera na klienta, ktorý je prihlásený na odber
Klient, ktorý správu odosiela určí QoS pre túto správu a dané QoS platí pre doručenie správy na broker. Následne každý klient, ktorý sa prihlásuje na odber správ z topicu si môže určiť očakávanú QoS.
Úloha 4.1
Vyskúšaj si nastaviť pri prihlasovaní na odber správy vyššie QoS a rovnako aj pri odosielaní správy vyššie QoS a sleduj o koľko sa spomalí/oneskorí komunikácia.
Retention
Ak ma správa nastavenú vlastnosť "retention
" na true
, broker má povinnosť uchovať poslednú takúto správu pre daný topic. Následne pre každého klienta, ktorý sa prihlási na odber správ z daného topicu dostane poslednú správu s vlastnosťou retention
nastavenou na true
.
Teda táto vlastnosť umožňuje uchovať pre každý topic maximálne jednu správu priamo na brokeri. Je to rozdiel oproti bežnému spôsobu fungovania MQTT, kde bežne každá správa príde len ak je v rovnakom čase klient prihlásený na odber ako bola aj správa odosielaná.
Úloha 4.2
Vyskúšaj si nastaviť pri odosielaní správy vlastnosť retention
na true
a následne sa prihlás na odber správ z daného topicu a uvidíš, či dostaneš správu aj v čase, keď si ju aktuálne neposlal(a).
Týmto spôsobom je možné riešiť uchovávanie napríklad stavu senzoru aj bez toho aby senzor stále posielal správu o svojom stave. Stačí ak pošle svoj stav len 1x s retention
a ktokoľvek sa neskôr prihlási na odber získa aktuálnu informáciu o stave daného senzoru.
Krok 5: Spracovanie údajov zo senzorov OpenLabu
Aj samotný OpenLab a jeho senzory fungujú taktiež na princípe MQTT správ.
Používa sa tam vlastný broker openlab.kpi.fei.tuke.sk
(port 80
, cesta /mqtt
pre WebSocket; pre TCP sa používa port 1883
), ktorý je dostupný len zo siete TUKE.
Senzory publikujú svoje informácie každú sekundu do topicov s názvami v tvare openlab/sensorkits/MACadresaSenzoru/druhUdaju
(napr. openlab/sensorkits/B8:27:EB:2F:7B:7D/atmp
pre jeden zo senzorov s hodnotou nameranej teploty)
Zoznam možných údajov z každého senzoru:
Druh údaju v topic časti | Význam |
---|---|
atmp |
teplota (štandardná) |
gtmp |
teplota (vnútra čipu) |
light |
svetlo (jas) |
pres |
tlak |
humi |
vlhkosť vzduchu |
vol |
hlasitosť zvuku |
json |
všetký údaje v JSON formáte |
Zoznam senzorov a ich umiestnení:
MAC senzoru | Umiestnenie |
---|---|
B8:27:EB:B2:23:2A |
pri malom paneli 2x2 v bočnej chodbe |
B8:27:EB:F8:82:2C |
za malým panelom 2x2 smerom k Aurore |
B8:27:EB:78:8F:4D |
pri vertikálnych TV |
B8:27:EB:DA:2E:65 |
pri paneli 3x3 bližšie pri oknách |
B8:27:EB:DC:E8:38 |
pri projektore nad schodami |
B8:27:EB:2F:7B:7D |
pri automatických dverách do A bloku |
Úloha 5.1
Vytvorte alebo upravte existujúce riešenie tak aby ste zobrazovali informácie o stave teploty všetkých dostupných senzorov v z priestoru OpenLab.
Komunikačné protokoly používané v MQTT
- TCP
- TCP+SSL
- WS (WebSocket)
- WSS (WebSocket Secure) - WebSocket s SSL
Bežne sa pre komunikáciu v MQTT používa protokol TCP, prípadne pre zabezpečenie komunikácie môže byť využitá nadstavba SSL. Túto štandardnú komunikáciu zvyknú využívať klienti vo všetkých bežných jazykoch (Java, C, Python, C#) okrem webového klienta. Štandardne používaným portom pre TCP je 1883
.
Pre komunikáciu vo web prehliadači s využitím Javascript implementácie je potrebné komunikáciu MQTT zaobaliť do WebSocketu. WebSocket môže byť taktiež zabezpečený pomocou bezpečnej SSL komunikácie WSS - WebSocket cez HTTPS.
Pre lepší prehľad odporúčam si pozrieť jednoduche použitie MQTT v Jave.