# Протокол MQTT
**Внимание!**
Описание данного практикума **отличается** от того, что приведено на сайте [IT Академии](https://innovationcampus.ru/lms/mod/book/view.php?id=1332)!
## Общие сведения об MQTT
Интернет работает на сотнях различных протоколов: HTTP, FTP, SMTP, POP3, SSH и т.д. и т.д.
Какой же протокл наиболее предпочтительно использовать для Интернета вещей?
Наиболее распространенными являются HTTP RESTful, CoAP и MQTT.
Все эти протоколы относятся к **прикладному** уровню эталонной модели OSI (собственно, в концептуальной модели стека TCP/IP – тоже к прикладному уровню):
![mqtt_tcp](/assets/images/iot-academy/mqtt_tcp.png)
Каким же требованиям должен удовлетворять протокол, предназначенный для Интернета вещей?
С точки зрения безопасности было бы лучше, если бы связь устанавливалась по инициативе устройства, а не сервера.
Такая модель предотвращает возможные атаки – если злоумышленник взломает сервер, то это не окажет негативного воздействия на конечное устройство,
так как в этой модели устройство не может управляться входящим запросом.
Далее, датчики в Интернете вещей, как правило, автономны. Они работают от батареи – вот почему размер передаваемого пакета данных должен быть
как можно меньше. Чем меньше времени тратится на приём/передачу, тем меньше энергопотребление.
HTTP RESTful и CoAP являются **документно-ориентированными** протоколами, поэтому они имеют большие издержки, а протокол MQTT – нет.
MQTT или Message Queue Telemetry Transport – это легкий, компактный и открытый протокол обмена данными созданный для передачи данных на удалённых локациях,
где требуется небольшой размер кода и есть ограничения по пропускной способности канала.
Вышеперечисленные достоинства позволяют применять его в системах M2M (Машинно-Машинное взаимодействие) и IIoT (Промышленный Интернет вещей).
Основные особенности протокола MQTT:
* асинхронный протокол;
* компактные сообщения;
* работа в условиях нестабильной связи на линии передачи данных;
* поддержка нескольких уровней качества обслуживания (QoS);
* легкая интеграция новых устройств.
Протокол MQTT работает на прикладном уровне поверх TCP/IP и использует по умолчанию 1883 порт (8883 при подключении через SSL).
Сам протокол был разработан в 1999 году фирмой IBM для обмена данными между компьютерами (M2M), а если быть более точным,
то для систем транспортировки нефти требовалось передать данные от различных датчиков в SCADA-систему в режиме реального времени.
Протокол MQTT хорош для применения в ситуациях, когда пропускная способность канала ограничена (в 1999-м стоимость связи была гораздо выше,
чем сейчас, и это предопределило особенности протокола MQTT).
Обмен сообщениями в протоколе MQTT осуществляется между клиентом (client), который может быть **издателем** или **подписчиком** (publisher/subscriber) сообщений,
и **брокером** (broker) сообщений – сервером (например, Mosquitto MQTT).
Издатель отправляет **сообщения** на MQTT брокер, указывая в сообщении определенную тему – **топик** (topic).
Подписчики могут получать разные данные от множества издателей в зависимости от подписки на соответствующие топики.
Устройства MQTT используют определенные типы сообщений для взаимодействия с брокером, ниже представлены основные:
* Connect – установить соединение с брокером;
* Disconnect – разорвать соединение с брокером;
* Publish – опубликовать данные в топик на брокере;
* Subscribe – подписаться на топик на брокере;
* Unsubscribe – отписаться от топика.
Схема простого взаимодействия между подписчиком, издателем и брокером приведена на рисунке:
![mqtt_flow](/assets/images/iot-academy/mqtt_flow.png)
Что делает MQTT интересным для нас протоколом?
Если говорить языком книги Эндрю Ханта и Дэвида Томаса "Программист-прагматик", он позволяет нам соблюдать принцип *ортогональности*,
то есть строить программу как набор слабо связанных между собой сущностей.
Иными словами, каждый клиент – это независимое устройство, а система в целом образуется из множества таких устройств за счет организации связи.
![mqtt_sys](/assets/images/iot-academy/mqtt_sys.png)
Топик – это основная единица хранения информации. Топики представляют собой символы с кодировкой UTF-8.
Иерархическая структура топиков имеет формат «дерева», что упрощает их организацию и доступ к данным.
Топики состоят из одного или нескольких уровней, которые разделены между собой символом «/».
В целом, базовые требования к структуре топиков неплохо описаны The HiveMQ Team в их [блоге](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/):
1. Уровни топиков разделяются с помощью "/"
2. Именем топика может быть любая UTF-8 строка
3. Имя каждого уровня топика должно содержать минимум один символ
4. Имена чувстВиТеЛьНы к РеГисТру
5. "/" тоже может быть именем топика, но это может запутать
Обратите внимание, что структуру топиков в вашем проекте определяете только вы сами и имеете полную свободу действий. При публикации сообщении вы можете указать вообще любой топик или субтопик.
Таким образом, протокол MQTT хорошо и довольно эффективно подходит для приложений, в которых необходимо собирать данные и управлять простыми устройствами.
В качестве клиентов могут выступать любые устройства. Например, это могут быть умный датчик, компьютер или смартфон.
![mqtt](/assets/images/iot-academy/mqtt.png)
Если у вас есть датчики температуры на кухне, в спальне и пара датчиков в гостиной, то структура топиков может быть задана следующим образом:
```
home/kitchen/temperature
home/bedroom/temperature
home/living_room/temperature1
home/living_room/temperature2
```
В топик можно опубликовать (PUBLISH) любой пакет информации и он будет доставлен всем, кто подписался (SUBSCRIBE) на этот топик.
Полная информация – http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html.
Подписчик может так же получать данные сразу из нескольких топиков, для этого существуют символы подстановок (**wildcard**).
Они бывают двух типов: одноуровневые (задаются символом «**+**») и многоуровневые (задаются символом «**#**»).
Одноуровневый wildcard (символ «+») позволяет как бы «пропустить» один уровень в дереве топиков, в то время как многоуровневый wildcard (символ «#») – текущий уровень и все нижележащие.
Если устройства подпишутся на топики так, как указано на рисунке
![MQTT_Wildcard](/assets/images/iot-academy/mqtt_sub_wildcard.png "Подписка с подстановками")
то в итоге они буду принимать информацию от следующих датчиков:
| Подписчик | Топик | Сообщения от датчиков (id) |
|:----------|-------------------------:|:--------------------------:|
| 1 | home/kitchen/temperature | 1 |
| 2 | home/+/+ | 1, 2, 3, 4, 5 |
| 3 | home/+/temperature | 1, 2, 4 |
| 4 | home/living_room/# | 4, 5 |
Обратите внимание, символы подстановки можно использовать для подписки!
**Публикация сообщений всегда остуществляется в конкретный топик!**
Интересно, что изначально название протокола расшифровывалось как Message Queue Telemetry Transport.
Однако это вносило путаницу, поскольку в MQTT используется другой паттерн: "Издатель/Подписчик" (Publisher/Subscriber), а не "Очередь сообщений" (Message Queue).
Поэтому от такой расшифровки отказались, и на данный момент MQ в названии официально ничего не значит.
## MQTT брокер (Установка mosquitto)
В рамках этого практикума подробно рассмотрим протокол MQTT. Для уверенной работы с ним стоит проделать несколько упражнений.
![mosquitto_logo](/assets/images/iot-academy/mosquitto_logo.png)
Сервер Mosquitto – лишь один из возможных вариантов сервера. Можно использовать и другие: HiveMQTT, HBMQTT.
Но мы будем использовать Mosquitto, поскольку это самый популярный Open Source-сервер.
Установите себе на компьютер `mosquitto`. Это сервер, который можно запустить локально и потренироваться на нём. Установка на Ubuntu делается просто:
```bash
sudo apt install mosquitto
```
Mosquitto также существует в виде пакетов и для других дистрибутивов. Его можно поставить и в embedded-компьютере наподобие Raspberry Pi – это важно,
если компьютер функционирует как шлюз между локальной сетью Интернета вещей (LoRa или ZigBee), и собственно Интернетом (WWW).
Можно даже установить Mosquitto в Windows, просто скачав дистрибутив с [сайта](https://mosquitto.org/download/).
Тогда он появится в “Службах” Windows, и его можно будет запускать и останавливать из этого окна:
![mosquitto_win](/assets/images/iot-academy/mosquitto_win.png)
При установке под Windows не забудьте добавить путь до выполняемых файлов в переменную окружения среды `PATH` (чтобы сожно было запускать утилиты из командной строки):
![add_mosquitto_path_win](/assets/images/iot-academy/add_mosquitto_path_win.png)
Но всё же настоятельно рекомендуется работать именно в Linux.
Вам предстоит освоить базовый функционал локального MQTT-сервера, чтобы далее выполнять упражнения с ним.
## Hello World в mosquitto
### Запуск сервера
Запустите сервер через терминал:
```bash
mosquitto
```
Результат:
![mosquitto_server](/assets/images/iot-academy/mosquitto_server.png)
Если запустить не получается из-за того, что порт уже используется, выберите другой порт через ключ `-p`.
Вообще обычно по умолчанию сервер стартует на 1883 порту и запускается в режиме демона, то есть без вашего участия.
И порт может быть занят просто потому, что `mosquitto` уже стартовал в вашей системе (можете проверить это, выведя список всех процессов).
Но нам сейчас важно потренироваться, поэтому запустим на другом порту для наглядности (пусть это будет порт 1893).
```bash
mosquitto -p 1893
```
![mosquitto_server_1893](/assets/images/iot-academy/mosquitto_server_1893.png)
### Подписка на топик
В **другом** окне терминала введите команду подписки на топик:
```bash
mosquitto_sub -h "localhost" -t "mytopic" -q 1
```
или
```bash
mosquitto_sub -h "localhost" -p 1893 -t "mytopic" -q 1
```
если сам брокер запущен на порту 1893.
В качестве хоста указан `localhost` (то есть сам компьютер), а в качестве топика — вымышленный `mytopic`.
Результат: программа будет ждать сообщений из этого топика.
![mosquitto_sub_localhost](/assets/images/iot-academy/mosquitto_sub_localhost.png)
Обязательно посмотрите в предыдущее окно, где стартовал сервер: вы увидите, что там добавилась новая информация.
### Отправка сообщения
Наконец, в **третьем** окне терминала введите команду, отправляющую сообщение — `mosquitto_pub`
(при необходимости укажите номер порта через ключ `-p` аналогично тому, как это было сделано в предыдущем случае):
```bash
mosquitto_pub -h "localhost" -t "mytopic" -m "Hello World" -q 1
```
![mosquitto_pub](/assets/images/iot-academy/mosquitto_pub.png)
Параметры следующие:
- `-h` — адрес сервера (IP-адрес или доменное имя, в примере `localhost` — то есть "тот же самый компьютер");
- `-t` — название топика, в котором публикуется сообщение (в примере это `mytopic`);
- `-m` — текст сообщения (`Hello World`);
- `-q` — качество обслуживания (QoS**1**).
В результате, вы увидите в окне с подпиской текст "Hello World":
![mosquitto_sub_result](/assets/images/iot-academy/mosquitto_sub_result.png)
Возможно, вам пригодятся еще и следующие ключи для `mosquitto_pub` и `mosquitto_sub`:
- `-p` — номер порта (вместо стандартного 1883);
- `-u` — имя пользователя;
- `-P` — пароль.
### Один сервер, много клиентов
Теперь попробуем общаться по протоколу MQTT в рамках класса, между отдельными машинами.
Выберите один компьютер — он будет сервером. Узнайте его IP-адрес (если это Linux, то самый простой способ — утилита `ifconfig`).
Запустите на нём сервер MQTT командой `mosquitto`.
Все остальные могут публиковать свои сообщения командой `mosquitto_sub`.
Не заканчивайте упражнение, пока сообщение каждого из участников группы не достигнет назначения.
При желании можно посмотреть, какие сообщения приходят на сервер, командой:
```bash
mosquitto_sub -h "192.168.1.18" -t "#"
```
Вы будете получать сообщения из всех топиков (напомним, что символ `#` означает все нижестоящие).
В некоторых случаях нам потребуется подключение к брокеру с авторизацией
## Качество обслуживания (QoS)
Чтобы далее успешно работать с MQTT-сервером, нужно знать, по какой схеме происходит коммуникация. Проведите самостоятельно ряд экспериментов.
Запустите сервер `mosquitto` в отладочном режиме (при необходимости — задайте номер порта):
```bash
mosquitto -v
```
Теперь, проделывая все те же самые действия, что и в прошлом задании, вы увидите большое количество промежуточных шагов:
![mosquitto_verbose](/assets/images/iot-academy/mosquitto_verbose.png)
Кроме того, вы можете увидеть промежуточные сообщения команд `mosquitto_sub` и `mosquitto_pub`, запустив их с ключом `-d` (debug):
![mosquitto_pub_with_d_key](/assets/images/iot-academy/mosquitto_pub_with_d_key.png)
Для изображения различных протоколов удобно использовать диаграммы последовательности (Sequence Diagram) UML.
Вот, например, как выглядит жизненный цикл `mosquitto_pub` с качеством обслуживания 0, согласно вышеприведенному скриншоту:
![sequence_diagram](/assets/images/iot-academy/sequence_diagram.png)
Если подобного рода диаграммы вам неизвестны, то посмотрите короткий обучающий [ролик](https://youtu.be/XIQKt5Bs7II), посвящённый диаграммам последовательности.
## Топики в MQTT
Используя локальный MQTT-сервер, выполните следующие задания:
1. Проверьте, чувствительны ли названия топиков к регистру.
2. Можно ли послать сообщение сразу всем субтопикам определённого топика?
Предположим, что у вас есть топик `light`, в нём топики `lamp1` и `lamp2`, и вы хотите послать сигнал всем лампочкам включиться.
3. Будет ли работать подписка на топик, если в конце названия топика добавить символ «/»? А если в начале?
4. Что будет, если вызвать `mosquitto_pub` с ключом `-l`?
5. Изучите, что такое **retain**. Отправьте сообщение с ключом `-r`, но важно, что в этот момент подписчики ещё не подключены к брокеру.
Затем запустите клиент и подпишитесь на топик. Что произойдет? Что будет, если подписчик отключится от брокера, а затем подключится снова?
Что, если оставить второе сообщение с тем же ключом? (Чтобы вернуть всё к прежнему состоянию, отправьте пустое сообщение с ключом `-r`).
6. Изучите, что такое «Последняя воля и завещание» (Last Will and Testament).
Для этого при публикации не задавайте тело сообщения, но укажите параметры `--will-topic` и `--will-payload` — название топика и собственно текст «завещания».
В результате, если вы в другом окне оформите подписку на этот топик, и издатель (`mosquitto_pub`) незапланированно отключится,
то подписчик (`mosquitto_sub`) получит от сервера «завещание».
Обратите внимание, что команда `mosquitto_pub` в чистом виде отправляет сообщение и сразу отключается без аварийного завершения,
поэтому у вас не получится сделать это задание сразу. Подумайте, как смоделировать случай, когда издатель подключается на какое-то время и затем внезапно отключается.