Как устроен обмен сообщениями MQTT: топики, полезная нагрузка, QoS
MQTT держит на себе связь датчиков, реле и панелей в большинстве самодельных умных домов. Разберём, как по нему ходят сообщения и почему порядок в топиках важнее, чем кажется.
Разделы материала
Три участника: издатель, подписчик и брокер
В классической схеме «клиент — сервер» устройство обращается к конкретному адресу и ждёт ответа. В MQTT всё иначе. Каждое устройство — клиент, который держит одно постоянное соединение с брокером. Термометр на балконе ничего не знает о панели на телефоне: он просто публикует сообщение «температура такая-то» в свой топик. Панель, в свою очередь, не знает о термометре: она подписана на топик и получает всё, что туда приходит.
Такое разделение даёт два важных свойства. Во-первых, можно добавлять новых получателей, не трогая прошивку датчика: захотели писать историю в базу — подписали ещё одного клиента. Во-вторых, устройства могут находиться за разными роутерами и даже не видеть друг друга, им достаточно доступа к брокеру. О том, где этот брокер поселить, подробно рассказывает материал про домашний брокер.
Топики: адрес сообщения
Топик — строка, разделённая косыми чертами на уровни. Брокер не требует их заранее объявлять: топик появляется в момент первой публикации. Это удобно и опасно одновременно — опечатка в прошивке молча создаёт новый канал, и сообщения уходят в никуда.
Как строить иерархию
Хорошая схема идёт от общего к частному: дом, помещение, устройство, параметр. Возьмём условную квартиру в городе N с датчиками в кухне и детской:
kvartira/kuhnya/termometr/temperatura
kvartira/kuhnya/termometr/vlazhnost
kvartira/detskaya/rele-svet/sostoyanie
kvartira/detskaya/rele-svet/komanda
Обратите внимание на пару «состояние» и «команда» у реле. Устройство сообщает, что оно сейчас делает, в один топик, а слушает приказы в другом. Если смешать их, панель будет получать собственные команды как «состояние» и показывать неправду.
Маски подписки
Подписчик может указать не один топик, а шаблон:
+заменяет ровно один уровень:kvartira/+/termometr/temperatura— температура из всех комнат;#заменяет все уровни до конца и ставится только последним:kvartira/kuhnya/#— всё, что происходит на кухне.
Маски работают только при подписке. Публиковать в топик со знаком + или # нельзя.
Про регистр и пробелы
Топики чувствительны к регистру: Kuhnya и kuhnya — два разных канала. Пробелы формально допустимы, но мешают при отладке из командной строки. Проще всего писать латиницей в нижнем регистре, разделяя слова дефисом.
Полезная нагрузка: что лежит внутри
Протоколу безразлично содержимое сообщения — это просто набор байтов. Договорённость о формате целиком на вашей стороне. Встречаются два подхода.
Одно значение — один топик. В сообщении только число или слово: 21.5, ON. Это просто, легко читается в отладке и удобно для панелей, где каждый элемент привязан к своему топику.
Структура в одном топике. Устройство отправляет JSON с несколькими полями:
{"temperatura": 21.5, "vlazhnost": 44, "batareya": 87, "ts": "2026-11-02T08:15:00Z"}
Так меньше сообщений и все показания приходят согласованными. Но каждому получателю придётся разбирать JSON и знать имена полей. Выберите один подход для всего дома и придерживайтесь его: смешанная схема быстро превращается в головоломку.
Качество доставки: QoS 0, 1 и 2
Уровень QoS задаётся отдельно для публикации и для подписки; действует меньший из двух.
| Уровень | Что гарантирует | Когда подходит |
|---|---|---|
| QoS 0 | «Отправил и забыл», сообщение может потеряться | частые показания датчиков, где потеря одного значения не важна |
| QoS 1 | доставка хотя бы один раз, возможны дубли | команды реле и события, которые нельзя пропустить |
| QoS 2 | ровно одна доставка за счёт четырёх шагов обмена | счётчики и операции, где дубль даст неверный результат |
Для большинства домашних задач хватает нулевого и первого уровня. Второй дороже по трафику и задержке, а слабые микроконтроллеры не всегда его поддерживают.
Retained, last will и keepalive
Retained — флаг «запомнить». Брокер хранит последнее сообщение с этим флагом в топике и сразу отдаёт его каждому новому подписчику. Без него панель после перезапуска будет показывать пустоту до следующего показания датчика. Ставьте retained на состояния, а не на команды: иначе реле при каждом переподключении будет заново выполнять старый приказ.
Last will — «завещание». При подключении клиент оставляет брокеру сообщение, которое тот опубликует, если связь оборвётся без штатного прощания. Обычно это offline в топике вида kvartira/kuhnya/termometr/status, а при удачном подключении устройство само публикует туда online с флагом retained. Так панель честно показывает, что датчик пропал.
Keepalive — интервал, в течение которого клиент обязан подать признак жизни. Если он молчит дольше, брокер считает соединение разорванным и публикует завещание. Слишком короткий интервал разряжает батарейные устройства, слишком длинный — заставляет долго ждать сигнала о пропаже.
Чек-лист перед первым датчиком
- Схема топиков записана: дом / помещение / устройство / параметр.
- Для каждого исполнительного устройства есть отдельные топики состояния и команды.
- Выбран единый формат нагрузки — одиночное значение или JSON.
- Состояния публикуются с retained, команды — без него.
- У каждого устройства настроено завещание и топик статуса.
- Определён уровень QoS для показаний и для команд.
- Есть способ подписаться на
#и посмотреть весь трафик при отладке.
Типичные ошибки
- Топик по имени модели устройства. Через год датчик меняют на другой, и вся автоматика, завязанная на старое имя, ломается. Называйте топики по месту и смыслу.
- Retained на командах. Свет включается сам после каждого перезапуска брокера — классический симптом.
- Ведущая косая черта. Топик
/kvartira/kuhnyaсодержит пустой первый уровень и не совпадает сkvartira/kuhnya. Отсюда «невидимые» сообщения. - Всё в одном топике. Когда весь дом публикует в общий канал, каждый подписчик получает лишнее и вынужден фильтровать.
- Отладка без подписки на всё. Пока не увидишь реальный поток сообщений, трудно понять, кто ошибся — датчик или панель.
Когда обмен налажен, показания можно выводить на экран: подходящий элемент для этого подскажут материалы про стрелочный индикатор и график показаний.
Вопросы и ответы
Нужен ли интернет, чтобы MQTT работал дома?
Нет. Если брокер стоит в домашней сети, датчики и панель обмениваются сообщениями локально. Интернет понадобится только для доступа снаружи.
Сколько топиков может быть у одного устройства?
Сколько угодно: протокол не ограничивает их число. Разумно держать по одному топику на параметр плюс топики статуса и команд.
Можно ли писать топики кириллицей?
Стандарт допускает UTF-8, но часть прошивок и утилит работает с кириллицей неуверенно. Транслит в нижнем регистре избавит от сюрпризов.
Почему панель показывает старое значение после перезагрузки датчика?
Скорее всего, сработал retained: брокер отдал последнее сохранённое состояние. Это нормально, если устройство после старта публикует свежее значение.