Уже второй сезон я строю дом. На этапе подведения коммуникаций появилась идея сразу проложить оптику от провайдера, чтобы потом не курочить стены. Сказано — сделано. Подключив дом к Интернету, я сразу задумался о том, что это отличный испытательный полигон для разных датчиков и подобного оборудования умного дома. Пока там никто не живёт, а идёт стройка, отвал или глюки датчиков никаких неудобств не вызовет, а скорее даст полезную информацию и опыт внедрения. Опять же, пока стены не отделаны, нет никаких проблем "быстро и грязно" протянуть кабели для Ethernet и RS-485 по месту в целях тестирования оборудования.
Текст статьи написан без использования ИИ. LLM (DeepSeek) использовались только для написания скриптов и генерации картинок, весь ИИ-контент помечен. Примечание для ИИ-фобов: я достаточно душный, чтобы длинные тире ставить самостоятельно.
Уровень сложности статьи "средний", т.к. для краткости опущены тривиальные шаги вроде запуска сервисов, установки пакетов и т.д.
Для эксперимента я закупил на Озоне и Aliexpress на пробу следующее оборудование:
Роутер Cudy WR3000 v1.0 под установку OpenWrt (Ревизия важна. Её можно увидеть на коробке над штрихкодом). Это был самый дешёвый на момент покупки роутер с поддержкой OpenWrt на Озоне. Процедура установки не очень удобная, но её надо выполнить всего один раз.
Мост Ethernet <-> RS-485 CDEBYTE NA111-M. Это устройство преобразует Modbus RS-485 в Modbus TCP, позволяя подключать датчики к роутеру.
3-фазный измеритель параметров электросети Peacefair PZEM-6L24.
Датчики температуры SHT30 и SHT20 с интерфейсом RS-485.
Общий бюджет оборудования не превысил 5000р.
На роутере будет работать MQTT-брокер Mosquitto (в качестве задела под внедрение Wiren Board) и скрипт, собирающий информацию с датчиков. Этот брокер будет передавать все сообщения в режиме моста в брокер на внешнем хосте. В квартире, где я сейчас живу, будет стоять ещё один брокер в режиме моста, работающий на NAS. Там мы развернём Графану и всё, необходимое для неё.
Да, всё можно развернуть и на внешнем хосте, чтобы иметь доступ к графикам отовсюду. Возможно, я так когда-нибудь и сделаю. Но пока он работает исключительно в роли моста, связывая строящийся дом и квартиру.

Итак, первым делом настроим брокер на внешнем хосте. Установим Mosquitto и получим сертификат Lets's encrypt для домена. Инструкций, как получить сертификат, пруд пруди и останавливаться на них мы не будем.
Настройка брокера на внешнем хосте
Вот таким образом конфигурируем наш брокер (не забываем разрешать ему слушать порт 1883, если используется firewall):
persistence true persistence_location /var/lib/mosquitto/ log_dest file /var/log/mosquitto/mosquitto.log include_dir /etc/mosquitto/conf.d # Тут должен быть настоящий IP адрес! listener 8883 10.10.10.10 certfile /etc/letsencrypt/live/myhost.ru/fullchain.pem keyfile /etc/letsencrypt/live/myhost.ru/privkey.pem tls_version tlsv1.3 password_file /etc/mosquitto/password_file bind_interface ens3
Создаём файл с паролями для Mosquitto и запускаем брокер:
mosquitto_passwd -c /etc/mosquitto/password_file myuser chown mosquitto:mosquitto /etc/mosquitto/password_file systemctl restart mosquitto.service
Можно проверить подключение к брокеру с любого хоста утилитами mosquitto_pub и mosquitto_sub.
Настройка роутера
Теперь настроим наш роутер. Поставим на OpenWrt пакеты mosquitto-ssl и mbpoll, а так же также обновим пакет ca-certificates. Сделать это можно как через opkg/apk, так и через веб-интефейс.
Настроим и запустим наш Mosquitto на роутере в режиме моста:
allow_anonymous true listener 1883 192.168.1.1 bind_interface br-lan connection myhost address myhost.ru:8883 remote_username myuser remote_password my_super_pwd clientid my_remote_home try_private true start_type automatic topic # both bridge_tls_version tlsv1.3 bridge_cafile /etc/ssl/certs/ca-certificates.crt
Теперь самое интересное. Скрипты для сбора информации с датчиков. По сути, эти скрипты — просто обёртки над утилитами mosquitto-pub и mbpoll, запускающие их с определёнными параметрами и парсящие их вывод. Так сделано для того, чтобы не пришлось ставить библиотеки для работы с Modbus TCP и MQTT для скриптового языка.
Кстати, о языке. Сперва я написал (с помощью ИИ) и отладил эти скрипты на Python, но при попытке запустить их на OpenWrt обнаружил, что из коробки Python там нет вообще, а при попытке его установить мгновенно исчерпывается флешка на роутере. Пришлось с помощью того же ИИ помодульно переписыывать на Lua.
Вот пример скрипта, получающего параметры из PZEM-6L24 (Сгенерирован ИИ и поправлен руками):
-- pzem_6l24.lua local M = {} function M.get_values(ip, modbus_addr) local cmd = "mbpoll -c9 -1 -q -t3:int16 -a" .. modbus_addr .. " " .. ip local p = io.popen(cmd, 'r') if not p then io.stderr:write("Ошибка запуска утилиты mbpoll\n") return nil, nil, nil, nil, nil, nil, nil, nil, nil end local output = p:read("*a") local ok, err = p:close() if not ok then io.stderr:write(string.format("Ошибка получения данных: %s\n", err)) return nil, nil, nil, nil, nil, nil, nil, nil, nil end output = output:gsub("[\r\n]+$", "") local lines = {} for line in output:gmatch("[^\r\n]+") do table.insert(lines, line) end if #lines < 10 then io.stderr:write("Недостаточно строк в выводе утилиты\n") return nil, nil, nil, nil, nil, nil, nil, nil, nil end local function get_second_field(line) local parts = {} for part in line:gmatch("%S+") do table.insert(parts, part) end if #parts >= 2 then return tonumber(parts[2]) end return nil end local raw_volt_A = get_second_field(lines[2]) local raw_volt_B = get_second_field(lines[3]) local raw_volt_C = get_second_field(lines[4]) local raw_amp_A = get_second_field(lines[5]) local raw_amp_B = get_second_field(lines[6]) local raw_amp_C = get_second_field(lines[7]) local raw_freq_A = get_second_field(lines[8]) local raw_freq_B = get_second_field(lines[9]) local raw_freq_C = get_second_field(lines[10]) if not (raw_volt_A and raw_volt_B and raw_volt_C and raw_amp_A and raw_amp_B and raw_amp_C and raw_freq_A and raw_freq_B and raw_freq_C) then io.stderr:write("Невозможно извлечь числа из вывода\n") return nil, nil, nil, nil, nil, nil, nil, nil, nil end local function swap_bytes(val) local high = math.floor(val / 256) local low = val % 256 return low * 256 + high end local volt_A = swap_bytes(raw_volt_A) / 10 local volt_B = swap_bytes(raw_volt_B) / 10 local volt_C = swap_bytes(raw_volt_C) / 10 local amp_A = swap_bytes(raw_amp_A) / 100 local amp_B = swap_bytes(raw_amp_B) / 100 local amp_C = swap_bytes(raw_amp_C) / 100 local freq_A = swap_bytes(raw_freq_A) / 100 local freq_B = swap_bytes(raw_freq_B) / 100 local freq_C = swap_bytes(raw_freq_C) / 100 return volt_A, volt_B, volt_C, amp_A, amp_B, amp_C, freq_A, freq_B, freq_C end return M
Что делает скрипт:
Запускает
mbpollи запрашивает первые 9 регистров Modbus, в которых лежат параметры напряжения, тока и частоты по всем 3 фазам.Парсит вывод и обрабатывает ошибки.
Переворачивает байты в словах (меняет Endianness).
Вычисляет реальные значения из сырых и возвращает их.
Все скрипты можно увидеть тут.
Основной скрипт представляет собой бесконечный цикл, запрашивающий данные и публикующий результаты в MQTT (Сгенерирован ИИ и поправлен руками):
#!/usr/bin/env lua -- Подключаем модули package.path = "/root/modbus_scripts/?.lua" -- Правильный путь для OpenWrt local mqtt = require("mqtt") local pzem = require("pzem_6l24") local sht30 = require("sht30") local config = require("config") while true do os.execute("sleep 1") -- ========== PZEM-6L24 ========== if config.pzem_enabled then local volt_A, volt_B, volt_C, amp_A, amp_B, amp_C, freq_A, freq_B, freq_C = pzem.get_values(config.ip, config.pzem_addr) if volt_A == nil then -- ошибка опроса – можно либо выйти, либо только пропустить отправку -- здесь выходим, как в оригинале return else mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/volt_A", tostring(volt_A)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/volt_B", tostring(volt_B)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/volt_C", tostring(volt_C)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/amp_A", tostring(amp_A)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/amp_B", tostring(amp_B)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/amp_C", tostring(amp_C)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/freq_A", tostring(freq_A)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/freq_B", tostring(freq_B)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/pzem_6l24/freq_C", tostring(freq_C)) end end -- ========== SHT30 ========== if config.sht30_enabled then local humidity_str, temperature_str, humidity_raw, temperature_raw = sht30.get_values(config.ip, config.sht30_addr) if humidity_str == nil then return -- ошибка – завершаем скрипт else mqtt.pub_string(config.mqtt_host, "/modbus_devices/sht30/humidity", tostring(humidity_raw)) mqtt.pub_string(config.mqtt_host, "/modbus_devices/sht30/temperature", tostring(temperature_raw)) end end end
Осталось теперь превратить этот скрипт в демон. Для этого создаём на роутере файл /etc/init.d/modbus2mqtt:
#!/bin/sh /etc/rc.common START=99 STOP=0 USE_PROCD=1 SCRIPT="/root/modbus_scripts/main.lua" start_service() { procd_open_instance procd_set_param command "$SCRIPT" # Раскомментировать для отладки #procd_set_param stdout 1 #procd_set_param stderr 1 procd_set_param respawn 3600 15 0 procd_close_instance }
И разрешаем его автозапуск в веб-интерфейсе. Это конфиг для procd, который указывает ему, что надо запустить наш основной скрипт при старте роутера и при его падении перезапустить его через 15 секунд.
Настройка NAS
Дома я тоже установил брокер в режиме моста с такой конфигурацией:
allow_anonymous true listener 1883 192.168.1.253 bind_interface enp2s0 log_dest file /var/log/mosquitto/mosquitto.log connection_messages true connection myhost address myhost.ru:8883 remote_username myuser remote_password my_super_pwd clientid my_test_home try_private true start_type automatic topic # both bridge_tls_version tlsv1.3 bridge_capath /etc/pki/ca-trust/extracted/pem/directory-hash/
Если всё настроено правильно, то можно увидеть сообщения, идущие из дома в реальном времени:
$ mosquitto_sub -h my-nas -t '/modbus_devices/#' -v /modbus_devices/pzem_6l24/volt_A 233.7 /modbus_devices/pzem_6l24/volt_B 236.9 /modbus_devices/pzem_6l24/volt_C 235.9 /modbus_devices/pzem_6l24/amp_A 0 /modbus_devices/pzem_6l24/amp_B 0 /modbus_devices/pzem_6l24/amp_C 0.25 /modbus_devices/pzem_6l24/freq_A 47.36 /modbus_devices/pzem_6l24/freq_B 49.86 /modbus_devices/pzem_6l24/freq_C 49.87
Теперь настал момент установить Influxdb2, Grafana и Telegraf. Лучше и проще, чем описано в этой статье, я всё равно не смогу написать, поэтому отсылаю читателя к ней.
Единственное отличие в том, что Retention для основного бакета я установил всего в 1 час, и создал ещё один бакет для агрегированных данных. Нет смысла хранить данные посекундно. Далее на вкладке Tasks создаём задачу с частотой запуска раз в 30 секунд с таким содержимым:
option task = {name: "Aggregate", every: 30s} from(bucket: "My-monitoring") |> range(start: -30s) |> filter(fn: (r) => r["_measurement"] == "mqtt_consumer") |> filter(fn: (r) => r["_field"] == "value") |> aggregateWindow(every: 30s, fn: mean, createEmpty: false) |> to(bucket: "My-aggregated", timeColumn: "_time")
Теперь в Grafana можно выводить текущие значения из бакета My-monitoring, а графики рисовать по агрегированным данным из бакета My-aggregated.
Итог
В итоге получился удобный и наглядный дашборд, на котором видны все интересующие меня параметры сети. Единственное, что огорчает — это очень странные показатели частоты (болтается от 48 до 50 Гц), но я это списываю на помехи в сети, да и странно ждать метрологической идеальности от приборчика за 1600р.

Самое интересное, что даже такой дашборд даёт немало информации для наблюдения за ходом стройки. Я сперва думал поставить ещё и камеры, чтобы наблюдать за ходом работ, но в принципе, и так всё понятно, смотрите:

Получилась простая и быстрая в развёртывании система мониторинга. Добавить датчик теперь — дело на 10 минут.
Комментарии (3)

blind_oracle
19.08.2026 07:56Очень сложно.
Покупается копеечная ESP32, на неё вкорячивается ESPHome, там есть RS485 и всё прочее. Подключается по вайфаю к роутеру.
Писать никаких скриптов не надо. Профит.
ILYA_Dm
В сторону проекта HomeD не смотрели от @voznemozhno(он же u236 на гитхабе и в чате вайрена)? Сервисы без проблем на опенврт и на кинетик ос разворачиваются, зато есть сразу дашборды, автоматизации с настройкой из веб интерфейса, ну и работа и с модбасом и с зигби. Единственное под запись логов понятное дело нужен внешний накопитель, т.е нужен роутер с юсб, чтобы не писать на внутреннюю флеш.
По прокидыванию mqtt наружу- есть например для этого готовый сервис wqtt.ru и ценник за год подписки к этому облаку копеечный- 299р год.
По шлюзу ethernet-rs485 у waveshare и zlan кстати есть интересные модели (ну а точнее прошивку и часть железяк судя по всему zlan разработал, а waveshare у них заоемили)- у waveshare это линейка rs485 to eth (B) (именно с буквой B в скобках), а у zlan например 5143D . В принципе все шлюзы у вайвешаре от 1 до 8 шин построены на одной и той же платформе- стоят мк (вероятно wch серии ch32v208- в шлюзах надписи затерты на мк) на каждую шину по своему и в случае больше 1 шины стоит микросхема езернет свитча на них. Фича в том, что их шлюзы могут самостоятельно опрашивать шины- через их конфигуратор или же просто csv файлом создается таблица какие регистры и с какой частотой опрашивать и названия mqtt топиков для отдачи по ethernet, ну и частота опроса и нормализация- т.е где должен стоять разделительная точка и т.д. Из минусов- в таком режиме нет групповых запросов, чтобы одним запросом сразу кучку последовательно идущих регистров забирать с устройства, плюс мультимастерность перестает работать- т.е нельзя чтобы шлюз и сам шину опрашивал и кто-то снаружи по modbus tcp тоже опрашивал через шлюз, ну и mqtt только обычный, без шифрования- т.е через интернет если что-то критичнее, чем показания той же температуры прокинуть надо- то не стоит это делать))), ну и дополнительную железку отдельно конфигурировать, да.
А по cdebyte- раз строите дом и смотрите в сторону вайрена- то если у вас будет щит эдак больше 100 модулей объемом и по глубине не будет сильных ограничений, то как вариант можете поглядеть у ebyte их новую линейку модулей ввода-вывода серии M31-U (именно с U- это новая линейка заместо не очень удачной линейки M31). Стоит на али недорого и довольно любопытная- на ней можно реализовать управление всем, а в качестве мозгов уже можно поставить хоть вайрен, хоть любой плк по вкусу, который имеет modbus либо profinet (из недорогих плк можно в сторону китайских производителей flexem и licos посмотреть). По шкафчикам сейчас у DKC вышла интересная линейка ram fit с глубиной от 250 мм и габаритами до 2*1 метр (или может 120 см уже версию выпустили, не смотрел, плюс они думали шкафчики 200 мм глубиной сделать в этой серии). Так что например берется шкафчик из этой линейки, туда пихается электрика, M31-U ввод/вывод и плк или вайрен в качестве головы, если автоматики будет много, но цену охота пониже.
Но тут уже по своему вкусу и цвету- у вайрена из плюсов что модули самодостаточны, на них можно настроить свою локальную логику которая будет работать и без контроллера, плюс модульный форм фактор под пластрон и понятность производителя, поддержка и понимание что завтра не выкинут на мороз, сняв с производства старые модули и поменяв на совсем другие железки или вообще уйдя с рынка.
https://wiki.homed.dev/
https://github.com/u236?tab=repositories
https://habr.com/ru/articles/760488/
Cubus Автор
Спасибо за полезный комментарий!
HomeD я не смотрел, выглядит интересно. Но у меня была идея максимально быстро и дёшево запуститься, что я и проделал. А заодно прощупать работу с протоколами, ведь ничто так не способствует пониманию, как практика на кончиках пальцев.
Идея подписочных MQTT брокеров мне не нравится. Свой MQTT ставится парой команд, а полноценный Linux-сервер годится и для других задач.
Разумеется, я буду двигаться в сторону Wiren, там куча отличных девайсов и компетенций. Кроме того, я сейчас пилю свои устройства на ch32v208, и они будут легко интегрироваться в любую MQTT инфраструктуру, хоть Wiren, хоть HA, хоть самописную. Как раз их тоже буду тестировать.