Пульт от телевизора лежит на журнальном столике, а свет в комнате висит на Home Assistant. Каждый вечер одна и та же хореография: пульт в руку, телефон из кармана, приложение, тап по плитке. При этом пульт уже в пальцах и читается за миллисекунды — не хватает одного мостика, который превратит ИК-код в сообщение MQTT. Дальше умный дом справится сам: автоматизация в HA слушает топик и зажигает что угодно, от люстры до подачи корма рыбкам.
Мост — это плата с Wi-Fi, приёмник TSOP и десяток строк PubSubClient. Самодельный на ESP8266 выходит дешевле десяти долларов: сообщество Home Assistant собирает такие ровно из этого набора — плата, приёмник, и по желанию ИК-светодиод для обратной дороги. Готовые коробки вроде Broadlink делают то же самое, но дороже, и прошивка у них чёрный ящик. Мой выбор — самодельный: топики свои, код открыт, поведение подкручивается под свою квартиру, а не под чужую прошивку.
Разберу цепочку целиком: как пультовый кадр становится топиком с payload, что положить в имена топиков, почему повторы удержания надо гасить до брокера, а не в автоматизации, и как гонять мост в обратную сторону — из Home Assistant в кондиционер. В конце — сим-стенд, где протокол публикации видно глазами, без единого пакета в сети.
Цепочка: кнопка — топик — автоматизация
Основа — pub/sub: плата публикует сообщение в топик, брокер раздаёт его подписчикам, автоматизация в HA слушает. Как устроен сам протокол и зачем нужен брокер, я пересказывать не буду — есть базовый урок раздела MQTT. Мосту хватает пары топиков: home/ir/event — код кнопки от пульта, home/ir/bridge/status — жив ли мост. Два имени, и никакого зоопарка; брокер бери локальный на своём железе — публичные для такого сценария на вкус как чужая подушка.
Имена топиков — тонкое место, правила я разбирал в уроке про структуру топиков: не сваливай команду и статус в один канал. Команды от пульта идут вверх, в дом; команды вниз — это отдельный топик home/ir/send, по которому HA просит мост что-то передать. Перепутанные направления — классическая грабля: автоматизация подписывается на свой же командный топик и ловит петлю. Если в доме несколько пультов и надо их различать, добавляй в payload адрес из кадра NEC — как разложить кадр на адрес и команду, я показывал в уроке про несколько пультов. Само подключение Home Assistant к брокеру — логин, порт, обнаружение устройств — шаг за шагом в уроке про подключение HA.
Payload держи простым: строка с числом команды, хоть в hex, хоть в десятичном — главное договориться с собой и не менять формат на ходу. Первая версия моста у меня слала сырые 32-битные слова кадра, и автоматизации в YAML обрастали масками и сдвигами; после переезда на чистые команды по одному байту YAML схлопнулся до сравнения строки с числом. Если захочется уйти в JSON с полями адрес, команда и сырьё — маршруты такие разобраны в разделе MQTT, но строку в один байт я бы сохранял: пульт шлёт ровно одно число за нажатие.
// Wemos D1 mini + TSOP4838 на D5 (GPIO14), питание приёмника от 3.3 В// с конденсатором 4.7 мкФ вплотную к ногам.#include <ESP8266WiFi.h>#include <PubSubClient.h>#include <IRremoteESP8266.h> // форк IRremote под ESP, не путать с оригиналом const char* WIFI_SSID = "HomeNet";const char* WIFI_PASS = "12345678";const char* MQTT_HOST = "192.168.1.50";const char* TOPIC_EVENT = "home/ir/event";const char* TOPIC_STATUS = "home/ir/bridge/status"; WiFiClient net;PubSubClient mqtt(net);IRrecv irrecv(14);decode_results results;unsigned long tLastCmd = 0; void setup() { Serial.begin(115200); WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) { delay(300); Serial.print("."); } mqtt.setServer(MQTT_HOST, 1883); irrecv.enableIRIn();} void loop() { if (!mqtt.connected()) { // последний вздох на случай падения связи: завещание offline if (mqtt.connect("ir-bridge-01", TOPIC_STATUS, 0, true, "offline")) { mqtt.publish(TOPIC_STATUS, "online", true); // статус — retained } return; } mqtt.loop(); if (irrecv.decode(&results)) { unsigned long now = millis(); if (now - tLastCmd > 300) { // удержание шлёт кадр каждые 108-110 мс String payload = String(results.value, HEX); mqtt.publish(TOPIC_EVENT, payload.c_str()); Serial.print(TOPIC_EVENT); Serial.print(" = "); Serial.println(payload); } tLastCmd = now; irrecv.resume(); }}Разбор по строкам. Wi-Fi поднимает WiFi.begin, дальше цикл ожидания с точками — скучно, но надёжно. Приёмник на ESP8266 живёт не в оригинальном IRremote, а в форке IRremoteESP8266: тот же каркас begin-decode-resume, но прерывания и таймеры разведены под чипы ESP — я про это писал в уроке про IRremoteESP8266. Пин любой: на ESP приём сидит на прерываниях, а не на паре выделенных ног. Скетч выше проверен в сборке под Wemos D1 mini и пакетом IRremoteESP8266 2.x — на других версиях компилятор ругается на имена, но каркас тот же.
Фильтр повторов — главная строка моста. Удержание кнопки шлёт кадр каждые 108–110 мс, и без фильтра автоматизация щёлкнет светом девять раз в секунду: включи, выключи, включи. Дебаунс на триста миллисекунд режет повторы до брокера; более тонкий вариант с флагом кадра-повтора я разбирал в уроке про фильтр удержания. Уровень доставки для команд — QoS 0: кадр живёт секунду, потерянный повтор придёт сам, а цена уровней QoS — отдельная тема из раздела MQTT.
Retained на командном топике не ставить никогда: брокер хранит последнее сообщение и вручит его каждому новому подписчику. Схема типичная: автоматизация перезапустилась, подписалась — и получила вчерашнюю команду включить свет. Поэтому у событий retain выключен, а у статуса моста включён: подключившись, HA сразу видит, онлайн ли мост.
Задержку от кнопки до плитки стоит измерить отдельно, потому что она складывается из трёх кусков: кадр NEC идёт 68 мс, фильтр повторов держит мост до 300 мс, брокер доставляет сообщение локально за единицы миллисекунд. С Mosquitto на домашней малине пульт до зажигания света доходит за полсекунды — рука разницы с обычным выключателем не замечает. Облачный брокер добавляет двести-пятьсот миллисекунд и зависимость от провайдера: интернет отвалился — пульт перестал зажигать люстру, хотя и мост, и лампа в полном порядке. Мой порог терпения — четверть секунды: всё, что дольше, ощущается уже как «подумал и сделал». Отсюда правило: всё, что обязано работать как пульт, живёт на локальном брокере, а облаку отдаётся копия событий для истории.
Проверять мост удобно с той стороны брокера: на любом компьютере с mosquitto_sub подпишись на шаблон home/ir/# и смотри кадры вживую — строка home/ir/event 21 приходит без единой строчки автоматизации. Такой просмотр отделяет проблемы моста от проблем HA: если в подписке кадры есть, а плитка в панели не шевелится, виноват YAML, а не плата. Шаблоны подписок с плюсом и решёткой разобраны в уроке про wildcard.
Обратная дорога и жизнь без Wi-Fi
Мост двунаправленный, и вторая дорога часто ценнее первой: HA публикует в home/ir/send, плата ловит это в callback и дёргает sendNEC на ИК-светодиод — кондиционер, колонка, старая люстра получают голос умного дома. Как строить кадры передатчиком, я показывал в уроке про sendNEC. В сообществе такие мосты живут годами и совмещают в одном корпусе и ИК, и 433 МГц радиорозетки — OpenMQTTGateway, Tasmota — все на этом принципе, только с готовой прошивкой и веб-панелью.
Грабля, о которой узнаёшь в первый же день: мост висит на стене, Wi-Fi упал, а пульт обязан работать. Если вся логика уехала в HA, приёмник мёртв до восстановления связи — и это выясняется ровно в тот вечер, когда интернете нет, а свет нужен. Мой вариант — дублировать две-три ключевые команды прямо на плате: код поймал, топик недоступен — зажигаю резервное реле локально. Умный дом вернулся, мост снова только слушает, и никто в доме не замечает падения.
Ограничение честно держим в виду: на Uno такой мост не собрать — Wi-Fi ставить некуда, ESP32 или ESP8266 обязательны, и это единственная крупная покупка в проекте. Симулятор тоже не исполняет сеть, но протокол можно посмотреть глазами: в сим-стенде ниже строка публикации печатается в Serial ровно в том виде, в каком её увидел бы брокер.
И следи за питанием приёмника: на ESP логика живёт от 3.3 В, и TSOP для этого диапазона выбирается специально — 3.3-вольтовые партии существуют, но пятёрочный приёмник от трёх вольт работает на урезанной дальности. Если дальность после переезда с Uno упала вдвое, первым делом смотри на питание, а не на код: я неделю раскапывал чужой скетч, а виноват был уровень на ноге приёмника.
| Вариант моста | Железо | Цена | Что получишь |
|---|---|---|---|
| Самодельный на ESP8266 | плата, TSOP, ИК-диод по желанию | до 10 $ | свои топики, приём и передача, код открыт |
| Tasmota IR Bridge | ESP-плата с ИК-портом | 15–25 $ | готовая прошивка, правила и интеграция из коробки |
| Broadlink RM4 | закрытая коробка | 25–40 $ | приём и передача, интеграция в HA, но прошивка чёрный ящик |
Кнопка → 32-битный кадр: AGC, инверсии и repeat видны глазом; включите помехи и поймайте бит, который не прошёл проверку.
Подгружаем интерактив…
- Мост — плата с Wi-Fi, приёмник и PubSubClient: самодельный на ESP8266 стоит до 10 долларов против 25–40 у готовых коробок
- Два топика достаточно: home/ir/event для кодов от пульта и home/ir/bridge/status для жизни моста
- Удержание кнопки шлёт кадры каждые 108–110 мс — фильтр 300 мс ставится на мосту, до брокера
- Команды — QoS 0 без retained; статус — retained плюс last will, иначе мёртвый мост выглядит живым
- Обратная дорога: HA публикует в home/ir/send, мост отыгрывает кадр sendNEC в кондиционер или люстру
- На ESP8266 приём живёт в форке IRremoteESP8266, а не в оригинальной библиотеке
- 1. Куда мост кладёт код нажатой кнопки пульта?
- 2. Удержание кнопки шлёт кадры каждые 108–110 мс. Что будет без фильтра повторов?
- 3. Почему на командном топике нельзя ставить retained?
- 4. Почему для команд пульта хватает QoS 0?
- 5. На какой плате собирают такой мост?
Пульт теперь говорит с умным домом на языке топиков, и можно повесить на кнопки что угодно — от света до подачи корма. Проверяй мост подпиской mosquitto_sub, держи команды одноразовыми, а статус — с завещанием, и мост переживёт и рестарты брокера, и падение Wi-Fi. Следующий урок закрывает физическую часть этого сценария: сервер на серводвигателе, который сам откроет и закроет штору, а кнопка пульта поставит её на паузу посреди хода. Мост из этого урока и автомат из следующего складываются в один проект без лишних деталей.
Частые вопросы по теме урока
- Куда мост кладёт код нажатой кнопки пульта?
- Мост публикует события от пульта вверх, в home/ir/event; топик home/ir/send — обратное направление, из HA в мост-передатчик. Публикует home/ir/event, а автоматизация в HA слушает этот топик
- Удержание кнопки шлёт кадры каждые 108–110 мс. Что будет без фильтра повторов?
- Каждый кадр-повтор для брокера полноценное сообщение. Фильтр 300 мс на мосту режет поток до того, как он стал потоком тогглов в HA. Автоматизация сработает на каждый кадр — свет замигает девять раз в секунду
- Почему на командном топике нельзя ставить retained?
- Retained хранит последнее сообщение. Перезапустилась автоматизация, подписалась — получила вчерашнюю команду включить свет. У событий retain выключен, у статуса включён. Брокер вручит старую команду каждому новому подписчику — случится фантомное срабатывание
- Почему для команд пульта хватает QoS 0?
- Команда пульта живёт мгновение; удержание шлёт девять повторов в секунду, и потеря одного кадра невидима. Подробная цена уровней доставки — в уроке про QoS. Кадр живёт секунду, потерянный повтор придёт сам — подтверждения только тормозят
- На какой плате собирают такой мост?
- Мост без сети не мост. ESP8266 даёт Wi-Fi, приёмник через форк IRremoteESP8266 и подписку на топики через PubSubClient. ESP8266 или ESP32 — нужен Wi-Fi и место под PubSubClient