2. týždeň

Komunikácia prostredníctvom správ

Úvod do použítia MQTT

Ciele

  1. Vyskúšaj si MQTT bez programovania
  2. Vyskúšaj si MQTT v programe s JavaScriptom
  3. Naučiť sa používať zástupné symboly pri prijímaní správ
  4. Oboznámiť sa s prídavnými vlastnosťami MQTT správ
  5. 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:

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áč by onmousemove 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 nezahrnie kpi/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 je kpi/door/1/temp, kpi/door/3/status, kpi/door/automatic/20/status, ale nezahrnie kpi/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:

  1. doručenie správy z klienta, ktorý správu posiela na broker
  2. 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.

Zdroje

  1. Webový MQTT klient od HiveMQ
  2. Paho MQTT - projekt združujúci implementácie MQTT klientov pre rôzne jazyky vo forme knižníc
  3. Porovnanie MQTT implementácii
  4. Zoznam verejných MQTT brokerov
  5. Séria blogov HiveMQ o MQTT

Doplňujúce zdroje

  1. Ukážka Java kódu MQTT klienta - Maven
  2. Ukážka C kódu MQTT klienta odosielajúceho správy - ESP8266 čip (Arduino IDE)