Wysyłanie odczytów z czujnika do brokera MQTT to kilka linijek kodu. Trudniej sprawić, żeby cały dom pełen czujników, przełączników i skryptów komunikował się przez MQTT bez nieaktualnych wartości, zgubionych poleceń i urządzeń, które wyglądają na dostępne, choć dawno nie działają. Decydują o tym głównie cztery mechanizmy: struktura tematów, poziom jakości usługi (QoS), wiadomości zachowywane (ang. retained) i ostatnia wola (ang. Last Will). Poniżej omówienie każdego z nich na podstawie specyfikacji MQTT 3.1.1 i podręcznika Mosquitto oraz sposób na sprawdzenie ich w praktyce za pomocą mosquitto_pub i mosquitto_sub.
Tematy: hierarchia, którą projektujesz sam
Temat MQTT to ciąg znaków podzielony na poziomy ukośnikiem /, na przykład home/livingroom/temperature. W podręczniku Mosquitto podkreślono, że tematu nie trzeba wcześniej tworzyć: wystarczy w nim opublikować wiadomość. Ta swoboda jest też głównym zagrożeniem, bo nic nie stoi na przeszkodzie, żeby jeden skrypt pisał do home/LivingRoom/temp, a drugi czytał z home/livingroom/temperature. W nazwach tematów rozróżniana jest wielkość liter, więc to dwa różne tematy.
Kilka zasad, które oszczędzą kłopotów:
- Idź od ogółu do szczegółu:
home/<pokój>/<urządzenie>/<wartość>albo<miejsce>/<urządzenie>/<wartość>. Dzięki temu symbole wieloznaczne mają sens. - Używaj małych liter i nie używaj spacji. To eliminuje pomyłki z wielkością liter i kłopoty z cudzysłowami w poleceniach powłoki.
- Nie zaczynaj tematów od
/. Ukośnik na początku tworzy pusty pierwszy poziom. W podręczniku Mosquitto zaznaczono, że puste poziomy są dozwolone, ale prowadzą do „nieco nieoczywistego zachowania”. - Nie używaj tematów zaczynających się od
$. W specyfikacji są one zarezerwowane dla brokera.$SYS/to popularny przedrostek statystyk brokera, a subskrypcja#nie odbiera niczego z tematów zaczynających się od$. - Oddziel stan od poleceń. Przekaźnik może na przykład publikować stan w temacie
home/garage/door/state, a polecenia odbierać whome/garage/door/set. W Zigbee2MQTT polecenia trafiają do tematów z końcówką/set.
Symbole wieloznaczne
W subskrypcjach można używać dwóch symboli wieloznacznych:
+pasuje do dokładnie jednego poziomu:home/+/temperatureodbierahome/kitchen/temperatureihome/bedroom/temperature.#pasuje do wszystkich pozostałych poziomów i musi być ostatnim znakiem:home/#odbiera wszystko podhome.
Symbole wieloznaczne działają tylko w subskrypcjach. Publikuje się zawsze do konkretnego tematu.
QoS: jak bardzo zadbać o dostarczenie
MQTT ma trzy poziomy QoS. W podręczniku Mosquitto opisano je tak:
- QoS 0: wiadomość jest dostarczana najwyżej raz, bez potwierdzenia.
- QoS 1: wiadomość jest dostarczana co najmniej raz i wymaga potwierdzenia. Możliwe są duplikaty.
- QoS 2: wiadomość jest dostarczana dokładnie raz dzięki czteroetapowej wymianie komunikatów.
Wyższe poziomy są bardziej niezawodne, ale zwiększają opóźnienie i ruch w sieci. Ważny szczegół: QoS dotyczy osobno każdego odcinka. Nadawca wybiera QoS w kierunku brokera, a każdy subskrybent wybiera maksymalny QoS, z jakim chce odbierać. Jeśli czujnik publikuje z QoS 2, a panel subskrybuje z QoS 0, panel dostaje wiadomość z QoS 0.
W praktyce:
- Częste odczyty z czujników: QoS 0. Jeśli jeden odczyt zginie, za chwilę przyjdzie następny.
- Polecenia i zdarzenia, których nie wolno zgubić (otwórz zawór, ktoś nacisnął dzwonek): QoS 1, a po stronie odbiorcy obsługa duplikatów. Dwukrotne wysłanie „ON” do przekaźnika nie szkodzi, dwukrotne przełączenie już tak. Lepiej więc wysyłać polecenia bezwzględne, takie jak
ONiOFF, zamiastTOGGLE. - QoS 2 rzadko jest potrzebny w automatyce domowej, a niektóre małe biblioteki klienckie go nie obsługują, więc sprawdź to, zanim zaczniesz na nim polegać.
QoS nie pomoże, gdy klient jest odłączony, chyba że korzysta z trwałej sesji. Według podręcznika Mosquitto klient, który łączy się z wyłączoną flagą „clean session”, zachowuje subskrypcje po rozłączeniu, a wiadomości z QoS 1 i 2 czekają na niego do ponownego połączenia. Wiele klientów domyślnie łączy się z czystą sesją; w mosquitto_sub trzeba na przykład dodać opcję -c, żeby to zmienić.
Wiadomości zachowywane: ostatnia znana wartość
Wiadomość zachowywana jest przechowywana przez brokera także po dostarczeniu. Gdy nowy klient zasubskrybuje pasujący temat, od razu ją otrzyma. W podręczniku Mosquitto nazwano to mechanizmem „ostatniej dobrej wartości”: bez niego panel uruchomiony przed chwilą musiałby czekać, aż czujnik raportujący co 15 minut znowu coś opublikuje.
Zachowuj wiadomości o stanie: temperaturę, położenie przełącznika, informację o dostępności urządzenia. Nie zachowuj zdarzeń ani poleceń. Zachowane ON w temacie poleceń trafi do urządzenia przy każdym ponownym połączeniu i może coś włączyć długo po tym, jak ktokolwiek o to prosił.
Aby usunąć wiadomość zachowywaną, opublikuj w tym samym temacie pustą wiadomość z flagą zachowania. Opcja -n w Mosquitto wysyła wiadomość o zerowej długości:
mosquitto_pub -h BROKER -t 'home/garage/door/set' -r -n
Przy przeglądaniu ruchu na zapracowanym brokerze mosquitto_sub -R ukrywa wiadomości zachowywane (nieświeże), a --retained-only pokazuje tylko je.
Ostatnia wola: informacja, że urządzenie zniknęło
Przy łączeniu klient może przekazać brokerowi ostatnią wolę: temat, treść, QoS i flagę zachowania. Broker publikuje ją, jeśli połączenie zakończy się bez prawidłowego pakietu DISCONNECT. W specyfikacji MQTT 3.1.1 wymieniono takie sytuacje jak awaria sieci, brak komunikacji klienta w czasie podtrzymania (ang. keep-alive) albo zamknięcie połączenia bez wysłania DISCONNECT. Jeśli klient rozłączy się prawidłowo, ostatnia wola przepada.
Ważny jest tu czas podtrzymania. Według specyfikacji broker musi zamknąć połączenie, jeśli przez półtora okresu podtrzymania nie dostanie od klienta żadnego pakietu. Przy czasie podtrzymania 60 sekund broker uzna więc odłączone urządzenie za niedostępne po około 90 sekundach.
Popularny wzorzec informowania o dostępności urządzenia wygląda tak:
- Przy łączeniu ustaw ostatnią wolę: temat
home/garage/door/status, treśćoffline, z flagą zachowania. - Zaraz po połączeniu opublikuj w tym samym temacie
online, również z flagą zachowania. - Przed planowanym wyłączeniem opublikuj
offlinesamodzielnie, bo prawidłowe rozłączenie nie uruchamia ostatniej woli.
Każdy subskrybent, także uruchomiony później, zobaczy wtedy aktualny stan dzięki fladze zachowania. Temat zigbee2mqtt/bridge/state w Zigbee2MQTT działa podobnie: według dokumentacji przy uruchomieniu publikowane jest zachowywane online, a tuż przed zatrzymaniem mostu offline.
Sprawdź to narzędziami Mosquitto
Otwórz terminal i obserwuj wszystko pod home:
mosquitto_sub -h BROKER -u UŻYTKOWNIK -P 'HASŁO' -t 'home/#' -v
Opcja -v wyświetla temat razem z treścią. W drugim terminalu zasymuluj urządzenie z ostatnią wolą:
mosquitto_sub -h BROKER -u UŻYTKOWNIK -P 'HASŁO' -t 'home/test/set' -k 10 \
--will-topic 'home/test/status' --will-payload 'offline' --will-retain
Opublikuj jego stan dostępności:
mosquitto_pub -h BROKER -u UŻYTKOWNIK -P 'HASŁO' -t 'home/test/status' -m 'online' -r
Teraz zakończ symulowane urządzenie bez pakietu DISCONNECT, na przykład poleceniem kill -9 na jego procesie. System operacyjny zamyka połączenie, broker wykrywa zamknięcie bez DISCONNECT, a w pierwszym terminalu pojawia się home/test/status offline. Po ponownej subskrypcji z trzeciego terminala od razu dostaniesz offline, bo ostatnia wola była zachowywana.
Czas podtrzymania ustawiony opcją -k 10 ma znaczenie, gdy połączenie znika po cichu, na przykład gdy urządzenie traci zasilanie albo Wi-Fi i nic nie zamyka połączenia. Broker czeka wtedy do półtora okresu podtrzymania, tutaj około 15 sekund, zanim opublikuje ostatnią wolę.
Jeśli zatrzymasz urządzenie skrótem Ctrl+C, mosquitto_sub rozłączy się prawidłowo i broker nie wyśle ostatniej woli. Tak ma być i właśnie dlatego potrzebny jest krok 3 powyżej.
Krótka lista kontrolna
- Tematy: małe litery, bez
/na początku, bez$, od ogółu do szczegółu, stan oddzielony od poleceń. - QoS 0 dla częstych odczytów, QoS 1 dla poleceń i ważnych zdarzeń, odbiorcy odporni na duplikaty.
- Zachowuj stan i dostępność, nigdy polecenia ani jednorazowe zdarzenia.
- Każde stale działające urządzenie ustawia zachowywaną ostatnią wolę
offlinei po połączeniu publikuje zachowywaneonline. - Dobierz czas podtrzymania do tego, jak szybko musisz się dowiedzieć o zniknięciu urządzenia.