Co je MQTT

V poslední době se objevilo množství nejrůznějších malých elektronických modulů a začalo být populární je propojovat mezi sebou nebo „do toho klaudu“. Mluví se o IoT (Internet of Things = internet věcí) nebo komunikaci M2M (Machine-To-Machine = „mašina mašině“) a v pozadí řady řešení je protokol MQTT, o kterém je tento úvodní článek. Zjistíte, že to je velmi jednoduché a snadno použitelné a doufáme, že vás bude motivovat k jeho použití – například se systémem Továrny na stole nebo stavebnicí Totem, které MQTT mohou dobře využít.

Již před desítkami let začalo být potřeba propojovat mezi sebou některá vzdálená zařízení a přenášet na dálku nějaká data; typicky kvůli kontrole a řízení procesů. Ještě před nástupem internetu tak byly propojovány i na velké vzdálenosti různá zařízení, například řídící centrum se vzdálenými měřícími stanicemi (např. pro dálkové vedení elektřiny, plynu, ropy nebo sběr meteorologických dat). Spojení mohlo být například pomocí dálkových kabelových spojů nebo rádiové, což vyžadovalo nákladné technologie i provoz. S příchodem internetu a mobilních telefonních sítí se přímo nabízelo využít tyto komunikační možnosti i pro takovéto datové přenosy, což je značně zjednodušilo, zlevnilo a nakonec i zpřístupnilo širokému okruhu uživatelů včetně hobbistů a amatérů. Pro vlastní přenos dat byly (a jsou) vyžívány různé protokoly1, jeden z nich se však v IoT velmi výrazně rozšířil pro své základní vlastnosti – jednoduchost, nenáročnost a snadnost nasazení – a je to právě MQTT z titulku tohoto článku.

MQTT (Message Queuing Telemetry Transport, český překlad je přenos vzdálených měření pomocí front zpráv) byl navržen původně v rámci průmyslového projektu sběru dat z ropovodu, později se stal uznávaným a používaným standardem pro obecný přenos dat tohoto typu. MQTT jako protokol určuje postupy, jak se data přenášejí, význam dat samozřejmě pak záleží na použití. Pro spojení zdroje a cíle dat využívá TCP/IP, tedy běžnou počítačovou síť, dnes dostupnou v podstatě všude. MQTT je natolik jednoduchý, že navíc k potřebě TCP/IP už nevyžaduje od koncových zařízení žádný větší výkon, je tedy možné jej využít i u těch nejjednodušších modulů: připojit se k počítačové síti je dnes možné i s velmi malými a levnými zařízeními za pár korun jako je Arduino připojené do lokální sítě pomocí „Ethernet shieldu“ nebo ESP8266/ESP32 připojené přes WiFi, a jejich výkon je i pro MQTT naprosto dostatečný. Ten, kdo je dost silný na TCP/IP, je dostatečně silný i na použití MQTT.

Základní principy

Veškerá přenášená data odněkud pocházejí a někam se posílají. Zdroj dat je označován jako producent, cíl dat jako konzument. Přiřazení dat od producenta ke správnému konzumentu (nebo více konzumentům) zařídí tzv. broker2. Základní myšlenka je, že producenta dat nezajímá, kdo je bude zpracovávat, a konzumenta nezajímá, odkud data pocházejí. Důležité je jen, aby oba věděli, jaká data chtějí posílat nebo dostávat, a broker zajistí, že se data dostanou na správné místo. Takovému označení se říká téma (anglicky topic). Například teplotní čidlo bude pouze posílat data o teplotě v pokoji („je tu 26°C“). Řídící jednotka klimatizace se přihlásí k odběru zpráv o teplotě v konkrétní místnosti a podle svého nastavení bude větrat a chladit. Když čidlo nastavíme, aby produkovalo data do tématu „teplota v pracovně“, tak klimatizaci nastavíme, aby se přihlásila k odběru zpráv také z tohoto tématu a podle hlášené teploty zapínala chlazení. Když pak přidáme další klimatizační jednotku, tak opět pouze řekneme, jaká data budou sloužit k jejímu řízení, a stejně tak když přidáme topení. A teploměr nemusí zajímat, že tam máme klimatizací a topení několik, vždy bude posílat data o teplotě jen jednou, brokeru, který zajistí přeposlání všem přihlášeným příjemcům.

Jde to i rozšířit: budeme mít řídící počítač, který si nechá posílat teplotu z různých míst (uvnitř, na chodbě, venku) a bude řídit třeba celou klimatizační strojovnu a ústřední topení a bude zapínat jednotlivé klapky, průduchy a ventilátory, ale vše spolu pořád může komunikovat jednoduše pomocí MQTT.

V základu je MQTT z uživatelského pohledu velmi jednoduchý a snadno zvládnutelný. Navíc volitelně umožňuje i zajištění kvality na třech úrovních jistoty přenosu a zabezpečení různých úrovní komunikace, takže je možné jej použít jak pro nejjednodušší systémy, tak i pro skutečně náročná řešení. Koncové prvky (producent a konzument) mohou být velmi malé, například běžné malé mikrokontrolery nebo Arduino, broker je k dispozici pro různé platformy (linux, MacOS, Windows) a je možné ho provozovat i na malých počítačích jako je třeba RaspberryPi (a naopak existují cloudová řešení pro mnohořádově silnější provoz a rozsáhlé využití). A pro vlastní implementaci je k dispozici i řada knihoven a příkladů.

Samozřejmě můžeme vytvořit i velmi složité řešení, MQTT však pořád může být naprosto dostačující.

Co je potřeba vědět

Prvky, které se účastní komunikace:

  • Producent (producer) – zdroj dat
  • Konzument (consumer) – cíl dat
  • Klient (client) – producent nebo konzument
  • Broker (broker) – ten, kdo propojuje producenty s konzumenty. Někdy se označuje i jako server.

Další názvosloví:

  • Téma (topic) – označení „proudu“ dat, která se předávají, Například „teplota v pokoji“, „tlak u ventilu“ apod. Název tématu je řetězec kódovaný v UTF-83 s výjimkou speciálních znaků: / lomítko, + plus, # dvojitý křížek a $ dolar (souhrnně označované jako zástupné znaky, wildcards, jejichž význam popíšeme příště).
  • Zpráva (message) – jedna přenášená jednotka dat. Obsahuje hlavičku (header) a data (payload, „náklad“). Hlavička je potřebná pro protokol (např. typ zprávy, téma, velikost zprávy atd.), data jsou to, co chceme předávat. Může to být naprosto cokoli, text nebo čísla nebo binární data; význam zná producent a konzumenti tohoto tématu, broker jim nerozumí a pouze data předává. To také umožňuje data libovolně strukturovat nebo formátovat pro přenos4. Data mohou být také naprosto libovolně kódována podle toho, jak to autoři producenta a konzumenta udělají5.
  • Publikování (publish) – odeslání zprávy do daného tématu. Každý producent může produkovat zprávy do libovolného počtu témat.
  • Přihlášení k odběru (subscribe) – konzument se přihlásí k odběru zpráv, přičemž brokeru sdělí, o jaké téma má zájem. Každý konzument se může přihlásit k libovolnému počtu témat. Může se přihlásit i k tématům, která sám jako producent odesílá (a pak je bude od brokera také normálně dostávat jako každý jiný konzument přihlášený k tomuto tématu).

Protokol MQTT je definován ještě mnohem podrobněji, pro jednoduché použití toto ale stačí (detailům se budeme věnovat v některém budoucím článku).

Praktický postup komunikace

Pro komunikaci potřebujeme tři prvky: producenta, konzumenta a brokera.

Implementací MQTT pro producenta a konzumenta je celá řada, pro snad všechna běžně používaná prostředí, ať už to jsou Windows, linux, MacOS nebo na nižším konci spektra Arduino, mbed nebo samotné mikrokontrolery. Je dostupné jako knihovna pro řadu programovacích jazyků – C, C++, Java, Lua, Perl, PHP, Python a microPython a další.

V nejzákladnějším režimu potřebujeme pro poslání dat následující:

  1. Spojit se s brokerem.
  2. Publikovat zprávu do tématu.

Pro příjem dat:

  1. Spojit se s brokerem.
  2. Přihlásit se k odběru tématu.

(samozřejmě toto je využití jen těch nejzákladnějších možností, MQTT toho umí mnohem víc)

Pro broker existuje také celá řada možností, v hobby komunitě (i menších komerčních využitích) je velmi oblíbený broker Eclipse Mosquitto™, který je open source a je k dispozici pro Windows, MacOS i linux/BSD (Debian, tedy i pro RaspberryPi, Ubuntu, a obecně všechny se snap systémem).

Posílání a příjem s Arduinem

Pro Arduino existuje více různých knihoven, které „umějí MQTT“. Následující příklady jsou s použitím knihovny PubSubClient:

/* producent.ino */
/* Zakladni priklad na posilani zprav pres MQTT */
#include <Arduino.h>

// pro Ethernet shield
#include <SPI.h>
#include <Ethernet.h>

// pro MQTT
#include <PubSubClient.h>

// Nastaveni pripojeni k siti - upravte si podle sveho:
const byte mojeMac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // moje MAC adresa (napsana na shieldu nebo vymyslena)
const byte mojeIp[] = {172, 16, 0, 100}; // moje vlastni IP adresa
const byte brokerIp[] = {172, 16, 0, 2}; // adresa MQTT brokeru
const uint16_t brokerPort = 1883; // port MQTT brokeru pro nesifrovany provoz (obvykle netreba menit)

EthernetClient sit; // objekt, pomoci ktereho pristupujeme k siti 
PubSubClient klient(sit); // MQTT klient

void setup()
{
  // pripojeni k siti:
  Ethernet.begin(mojeMac, mojeIp);
  delay(1500);   // pauzicka, ktera je obvykle potreba

  // lokalni vypis
  Serial.begin(115200);

  // nastaveni MQTT:
  klient.setServer(brokerIp, brokerPort); // kam se pripojit 
}

void loop()
{
  while (!klient.connected()) { // tocime se dokola, dokud se nepripojime
    if (klient.connect("pokusnyProducent")) { // zkusime se pripojit, jmenujeme se "pokusnyProducent". A kdyz to vyjde, tak:
      klient.publish("nějaké téma","123 zkouška rozhlasu, jak mě slyšíte?"); // publikujeme do tematu danou zpravu
      Serial.println("Poslali jsme zprávu.");
    }
    else { // kdyz pripojeni nevyjde, tak:
      delay(5000); // chvili pockam.
    }
  }
  klient.loop(); // tohle se musi opakovane a casto volat, aby MQTT klient spravne fungoval.
}
/* konzument.ino */
/* Zakladni priklad na prijimani zprav pres MQTT */
#include <Arduino.h>

// pro Ethernet shield
#include <SPI.h>
#include <Ethernet.h>

// pro MQTT
#include <PubSubClient.h>

// Nastaveni pripojeni k siti - upravte si podle sveho:
const byte mojeMac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // moje MAC adresa (napsana na shieldu nebo vymyslena)
const byte mojeIp[] = {172, 16, 0, 101}; // moje vlastni IP adresa
const byte brokerIp[] = {172, 16, 0, 2}; // adresa MQTT brokeru
const uint16_t brokerPort = 1883; // port MQTT brokeru pro nesifrovany provoz (obvykle netreba menit)

EthernetClient sit; // objekt, pomoci ktereho pristupujeme k siti 
PubSubClient klient(sit); // MQTT klient

// funkce, kterou nam MQTT klient zavola, az nam neco prijde
void callback(char* tema, byte* data, unsigned int delka) {
  Serial.print("Něco přišlo! Téma: [");
  Serial.print(tema);
  Serial.print("], data: \"");
  for (unsigned int i=0;i<delka;i++) {
    Serial.print((char)data[i]);
  }
  Serial.println("\"");
}

void setup()
{
  // pripojeni k siti:
  Ethernet.begin(mojeMac, mojeIp);
  delay(1500);   // pauzicka, ktera je obvykle potreba

  // lokalni vypis
  Serial.begin(115200);

  // nastaveni MQTT:
  klient.setServer(brokerIp, brokerPort); // kam se pripojit 
  klient.setCallback(callback); // co se ma zavolat, az neco prijde
}

void loop()
{
  while (!klient.connected()) { // tocime se dokola, dokud se nepripojime
    if (klient.connect("pokusnyKonzument")) { // zkusime se pripojit, jmenujeme se "pokusnyKonzument". A kdyz to vyjde, tak:
      klient.subscribe("nějaké téma"); // si zazadame o posilani zprav z daneho tematu.
      Serial.println("Přihlásili jsme se k odběru tématu.");
    }
    else { // kdyz pripojeni nevyjde, tak:
      delay(5000); // chvili pockam.
    }
  }
  klient.loop(); // tohle se musi opakovane a casto volat, aby MQTT klient spravne fungoval.
}

A na závěr nesouvisející poznámka k M2M: Jmenovala se tak také populární Norská dívčí skupina ale to s komunikací strojů opravdu vůbec nesouvisí…


Poznámky:

  1. Například protokoly AMQP, CoAP, DDS, LwM2M, MQTT, XMPP.
  2. Český překlad je zprostředkovatel, ale v této branži se i v češtině používá původní anglické označení broker, vyslovované anglicky [broukr]. Občas se používá i označení server.
  3. Díky UTF-8 tedy můžeme použít českou diakritiku stejně jako azbuku nebo japonské znaky.
  4. Často se používá prostý text, ale také formáty jako JSON, BSON nebo XML pro snazší strojové zpracování.
  5. Pro MQTT to není podstatné ani zajímavé, prostě předá data, ať to je, co chce.