{"id":34917323,"url":"https://github.com/wirenboard/wb-mqtt-serial","last_synced_at":"2026-05-20T07:14:00.722Z","repository":{"id":37409168,"uuid":"75732018","full_name":"wirenboard/wb-mqtt-serial","owner":"wirenboard","description":"Wiren Board MQTT serial protocol driver","archived":false,"fork":false,"pushed_at":"2026-04-10T10:18:42.000Z","size":7422,"stargazers_count":62,"open_issues_count":13,"forks_count":29,"subscribers_count":22,"default_branch":"master","last_synced_at":"2026-04-10T12:35:53.846Z","etag":null,"topics":["modbus","mqtt"],"latest_commit_sha":null,"homepage":"https://wirenboard.com/wiki/Wb-mqtt-serial_driver","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wirenboard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-12-06T13:07:09.000Z","updated_at":"2026-04-10T09:55:07.000Z","dependencies_parsed_at":"2023-12-26T15:13:03.289Z","dependency_job_id":"ab163fa8-ed87-47c2-aa41-013ac2f8f692","html_url":"https://github.com/wirenboard/wb-mqtt-serial","commit_stats":null,"previous_names":[],"tags_count":668,"template":false,"template_full_name":null,"purl":"pkg:github/wirenboard/wb-mqtt-serial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wirenboard%2Fwb-mqtt-serial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wirenboard%2Fwb-mqtt-serial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wirenboard%2Fwb-mqtt-serial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wirenboard%2Fwb-mqtt-serial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wirenboard","download_url":"https://codeload.github.com/wirenboard/wb-mqtt-serial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wirenboard%2Fwb-mqtt-serial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31835821,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T07:17:56.427Z","status":"ssl_error","status_checked_at":"2026-04-15T07:17:30.007Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["modbus","mqtt"],"created_at":"2025-12-26T12:48:03.010Z","updated_at":"2026-05-20T07:14:00.701Z","avatar_url":"https://github.com/wirenboard.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wb-mqtt-serial\n\nSerial device \u003c==\u003e MQTT bridge which follows [Wiren Board MQTT Conventions](https://github.com/wirenboard/conventions/blob/main/README.md).\nIt's designed to be used on [Wiren Board](https://wirenboard.com/en/) family of programmable automation controllers.\n\nДрайвер master-slave протоколов для устройств, работающих через\nпоследовательный порт. Драйвер предназначен для устройств [Wiren Board](https://wirenboard.com/ru) и соответствует [Конвенции Wiren Board MQTT](https://github.com/wirenboard/conventions/blob/main/README.md).\n\n**Содержание**\n\n- [Сборка](#сборка)\n- [Описание](#описание)\n  - [Поддерживаемые протоколы](#поддерживаемые-протоколы)\n  - [Управление драйвером](#управление-драйвером)\n- [Файл конфигурации и шаблоны](#файл-конфигурации-и-шаблоны)\n  - [Общая информация](#общая-информация)\n  - [Шаблоны конфигурации](#шаблоны-конфигурации)\n  - [Перевод названий параметров устройств](#перевод-названий-параметров-устройств)\n- [Особенности работы драйвера](#особенности-работы-драйвера)\n  - [Таймауты и количество неудачных циклов](#таймауты-и-количество-неудачных-циклов)\n  - [Замечания для TCP или MODBUS TCP порта](#замечания-для-tcp-или-modbus-tcp-порта)\n  - [Диаграмма таймаутов цикла опроса](#диаграмма-таймаутов-цикла-опроса)\n  - [Объединенное чтение регистров и его авто-отключение](#объединенное-чтение-регистров-и-его-авто-отключение)\n  - [Автоматическое отключение опроса регистров](#автоматическое-отключение-опроса-регистров)\n  - [Поведение в случае, если отключен опрос всех каналов, кроме каналов с событиями](#поведение-в-случае-если-отключен-опрос-всех-каналов-кроме-каналов-с-событиями)\n  - [Список сконфигурированных портов](#список-сконфигурированных-портов)\n  - [Прямое чтение и запись в порт](#прямое-чтение-и-запись-в-порт)\n  - [Чтение и запись по протоколу Modbus](#чтение-и-запись-по-протоколу-modbus)\n  - [Прямая установка параметров связи устройства](#прямая-установка-параметров-связи-устройства)\n  - [Поиск устройств, используя Быстрый Модбас](#поиск-устройств-используя-быстрый-модбас)\n  - [Получение информации об устройстве Wiren Board](#получение-информации-об-устройстве-wiren-board)\n  - [Чтение настроек устройств](#чтение-настроек-устройств)\n  - [Чтение каналов и параметров устройств](#чтение-каналов-и-параметров-устройств)\n  - [Запись каналов и параметров устройств](#запись-каналов-и-параметров-устройств)\n  - [Управление опросом устройств](#управление-опросом-устройств)\n  - [Обработка счётчиков нажатий](#обработка-счётчиков-нажатий)\n- [Протоколы](#протоколы)\n  - [Поддержка различных протоколов на одной шине](#поддержка-различных-протоколов-на-одной-шине)\n  - [Modbus](#modbus)\n  - [Широковещательные сообщения](#широковещательные-сообщения)\n  - [IEC/ГОСТ МЭК 61107 режим С](#iecгост-мэк-61107-режим-с)\n  - [Энергомера ГОСТ МЭК 61107](#энергомера-гост-мэк-61107)\n  - [Энергомера протокол СЕ](#энергомера-протокол-се)\n  - [НЕВА МТ 32х ГОСТ МЭК 61107](#нева-мт-32х-гост-мэк-61107)\n  - [DLMS/COSEM и СПОДЭС](#dlmscosem-и-сподэс)\n  - [Somfy SDN](#somfy-sdn)\n  - [WinDeco](#windeco)\n  - [Dooya](#dooya)\n  - [Dauerhaft](#dauerhaft)\n  - [Меркурий 200](#меркурий-200)\n  - [Меркурий 230](#меркурий-230)\n  - [Таблица шаблонов device\\_type](#таблица-шаблонов-device_type)\n\n## Сборка\n\nИнструкции по сборке (сборка .deb-пакетов через wbdev и рабочий процесс devcontainer-cli) описаны в [BUILD.md](https://github.com/wirenboard/codestyle/blob/devcontainer-cli/BUILD.md) в репозитории `codestyle`.\n\n## Описание\n\n### Поддерживаемые протоколы\n\nДрайвер wb-mqtt-serial поддерживает устройства, работающие по протоколам:\n\n- [Modbus](https://modbus.org),\n- [Uniel](http://smart.uniel.ru),\n- [ИВТМ](http://www.eksis.ru/catalog/measures-of-relative-humidity-and-temperature/),\n- [Меркурий 230](http://www.incotexcom.ru/m230art.htm),\n- [Меркурий 200](http://www.incotexcom.ru/m200.htm),\n- [Милур](http://www.milur.ru),\n- [Энергомера ГОСТ МЭК 61107 и протокол СЕ](http://www.energomera.ru),\n- [НЕВА МТ 32х ГОСТ МЭК 61107](https://www.meters.taipit.ru),\n- [DLMS/COSEM](https://www.dlms.com), СПОДЭС (ГОСТ Р 58940-2020),\n- [Somfy SDN](https://www.somfy.com),\n- WinDeco,\n- Dooya,\n- Dauerhaft/[A-OK](https://a-okmotors.com),\n- IEC/ГОСТ МЭК 61107 режим С.\n\n### Управление драйвером\n\nПо умолчанию драйвер установлен на всех контроллерах Wiren Board и запускается автоматически при наличии непустого конфигурационного файла `/etc/wb-mqtt-serial.conf`. При первоначальной установке пакета создаётся конфигурационный файл, в котором не описано ни одного подключенного устройства. Добавьте устройства в `/etc/wb-mqtt-serial.conf`, либо воспользуйтесь онлайн-редактором настроек для начала работы.\n\nНастройка пользователем производится через веб-интерфейс контроллера, но вы можете управлять им вручную:\n\n- `systemctl start wb-mqtt-serial` — запустить\n- `systemctl stop wb-mqtt-serial` — остановить\n- `systemctl status wb-mqtt-serial` — узнать состояние\n\nВозможен запуск демона вручную, что может быть полезно\nдля работы в отладочном режиме:\n\n```\n# service wb-mqtt-serial stop\n# wb-mqtt-serial -c /etc/wb-mqtt-serial.conf -d\n```\n\n## Файл конфигурации и шаблоны\n\n### Общая информация\n\nКонфигурационный файл построен по трёхуровневой схеме:\nпорты (ports) -\u003e устройства (devices) -\u003e каналы (channels).\n\nКонфигурация устройства device может быть задана двумя способами: вручную прописать все параметры или задать только несколько параметров.\n\nПример файла конфигурации с описанием параметров:\n\n```jsonc\n{\n    // По DeviceType драйвер будет искать в папках с шаблонами описаний устройств\n    \"device_type\": \"DeviceType\",\n\n    // отображаемое имя устройства. Публикуется как\n    // .../meta/name в MQTT\n    // По умолчанию name берется из шаблона и добавляется slave_id, т.е.\n    // \"name\" + \" \" + \"slave_id\"\n    \"name\": \"somename\",\n\n    // уникальный идентификатор устройства в MQTT.\n    // каждый элемент в devices должен иметь уникальный id\n    // topic'и, относящиеся в MQTT к данному устройству,\n    // имеют общий префикс /devices/\u003cидентификатор топика\u003e/...\n    // также по умолчанию берется из шаблона с добавлением slave_id:\n    // \"deviceID\" + \"_\" + slave_id\n    \"id\": \"deviceID\",\n\n    // идентификатор slave\n    \"slave_id\": slaveID,\n\n    // включить/выключить устройство. В случае задания\n    // \"enabled\": false опрос устройства и запись значений\n    // его каналов не происходит. По умолчанию - true.\n    \"enabled\": true,\n\n    // если используется шаблон устройства, определения\n    // каналов совмещаются. Если имя (name) в определении\n    // канала устройства совпадает с именем канала в шаблоне,\n    // свойства каналов из шаблона и определения устройства\n    // совмещаются, при этом значения свойств из определения\n    // устройства (в файле конфигурации) имеют преимущество.\n    // Это можно использовать, например, для задания индивидуальных\n    // интервалов опроса каналов. Если канал с таким же\n    // именем, как канал в определении устройства, отсутствует\n    // в шаблоне, создаётся новый канал.\n    \"channels\": [\n        {\n            // имя канала. topic'и, соответствующие каналу,\n            \"name\": \"Temp 1\",\n            \"read_period_ms\": 10000\n        }\n    ]\n}\n```\n\nНиже приведён пример конфигурационного файла `/etc/wb-mqtt-serial.conf`\n\n```jsonc\n{\n    // опция debug включает или выключает отладочную печать.\n    // Опция -d командной строки wb-mqtt-serial также\n    // включает отладочную печать и имеет приоритет над\n    // данной опцией.\n    \"debug\": false,\n\n    // Задаёт интервал в секундах, в течение которого неизменяющиеся значения не будут публиковаться в MQTT.\n    // По истечении интервала (но не чаще периода чтения канала) полученное значение будет опубликовано, даже если оно не изменилось.\n    // Минимальный интервал - 5 секунд.\n    // Если установлено отрицательное значение, то значения будут публиковаться только при изменении. Это поведение по умолчанию.\n    \"max_unchanged_interval\": -1,\n\n    // Задаёт максимальное число чтений регистров в секунду.\n    // Не влияет на чтение регистров с заданным интервалом опроса.\n    // Для снижения нагрузки на процессор рекомендуется задавать значение не более 100 для WB6 и не более 800 для WB7\n    \"rate_limit\": 100,\n\n    // список портов\n    \"ports\": [\n        {\n            // тип порта:\n            // - \"serial\": последовательные порты RS-485 или RS-232. Это значение выбирается по умолчанию.\n            // - \"tcp\": serial over TCP/IP. Пакеты, формируемые для работы с последовательными портами, передаются без изменений через TCP/IP.\n            // - \"modbus tcp\": передача по MODBUS TCP. В секции устройств с таким типом порта могут использоваться только те, что поддерживают MODBUS.\n            \"port_type\": \"serial\",\n\n            // устройство, соответствующее порту RS-485 (если выбран тип порта serial)\n            \"path\": \"/dev/ttyRS485-1\",\n\n            // IP адрес или имя хоста (если выбран тип порта TCP или MODBUS TCP)\n            \"address\": \"127.0.0.1\",\n\n            // TCP порт (если выбран тип порта TCP или MODBUS TCP)\n            \"port\": 3000,\n\n            // скорость порта\n            \"baud_rate\": 9600,\n\n            // чётность - N, O или E (по умолчанию - N)\n            \"parity\": \"N\",\n\n            // количество бит данных (по умолчанию - 8)\n            \"data_bits\": 8,\n\n            // количество стоп-бит\n            \"stop_bits\": 2,\n\n            // Таймаут ответа (мс)\n            // Максимальное время ответа устройств, подключенных к этому порту, в миллисекундах\n            // Если не установлено, то принимается равным 500 мс\n            // Этот параметр задан в шаблонах описания устройств, переопределять его можно только в случае некорректной работы с устройствами\n            \"response_timeout_ms\": 100,\n\n            // Дополнительная задержка перед записью в порт (мкс)\n            \"guard_interval_us\": 1000,\n\n            // Таймаут подключения (мс)\n            // Если в течение указанного времени опрашиваемые устройства были помечены как отключенные, \n            // а также истек \"connection_max_fail_cycles\",\n            // соединение будет разорвано и произойдет попытка переподключения\n            \"connection_timeout_ms\": 5000,\n\n            // Количество неудачных циклов опроса\n            // Если в течение указанного количества последовательных циклов опроса опрашиваемые устройства были помечены как отключенные,\n            // а также истек \"connection_timeout_ms\",\n            // соединение будет разорвано и произойдет попытка переподключения\n            \"connection_max_fail_cycles\": 2,\n\n            // Включить/выключить порт. \n            // В случае задания \"enabled\": false опрос порта и запись значений каналов в устройствах на данном порту не происходит.\n            // По умолчанию - true.\n            \"enabled\": true,\n\n            // Применимо для MODBUS TCP. \n            // Признак того, что это подключение к шлюзу WB-MGE v.3.\n            // Разрешает использование Быстрого Модбаса для устройств, подключенных к шлюзу.\n            // По умолчанию - false.\n            \"connected_to_mge\": true,\n\n            // список устройств на данном порту\n            \"devices\": [\n                {\n                    // тип устройства, в системе должен быть корректный шаблон для этого типа\n                    \"device_type\": \"MSU34+TLP\",\n\n                    // Название устройства\n                    // отображаемое имя устройства. Публикуется как\n                    // .../meta/name в MQTT\n                    \"name\": \"MSU34+TLP\",\n\n                    // Идентификатор устройства в MQTT\n                    // уникальный идентификатор устройства в MQTT.\n                    // каждое элемент в devices должен иметь уникальный id\n                    // topic'и, относящиеся в MQTT к данному устройству,\n                    // имеют общий префикс /devices/\u003cидентификатор топика\u003e/...\n                    \"id\": \"msu34tlp\",\n\n                    // идентификатор устройства.\n                    // Если не указан, то используются широковещательные запросы\n                    \"slave_id\": 2,\n\n                    // Опрашивать устройство\n                    // В случае задания \"enabled\": false опрос устройства и запись значений каналов не происходит.\n                    // По умолчанию - true.\n                    \"enabled\": true,\n\n                    // протокол передачи устройства, если не задан, то используется \"modbus\"\n                    \"protocol\": \"modbus\",\n\n                    // Включить режим непрерывного чтения регистров.\n                    // Поддерживается только в устройствах Wiren Board.\n                    \"enable_wb_continuous_read\": true,\n\n                    // Максимальное число считываемых промежуточных регистров\n                    // Драйвер в целях оптимизации может считывать регистры \"пачкой\". \n                    // При этом, если какие-либо регистры не были включены в конфигурацию, но в целях ускорения опроса (чтобы не разрывать \"пачку\") их всё-таки можно считывать, можно указать значение max_reg_hole больше 0.\n                    // В данный момент поддерживается только устройствами Modbus.\n                    \"max_reg_hole\": 10,\n\n                    // Максимальное число считываемых промежуточных бит\n                    // То же самое, что max_reg_hole, но для однобитовых регистров (coils и discrete inputs в Modbus).\n                    // В данный момент поддерживается только устройствами Modbus.\n                    \"max_bit_hole\": 80,\n\n                    // Максимальное число регистров, считываемых за один запрос\n                    // Максимальное количество 16-битных регистров в одной пакетной операции чтения (в данный момент поддерживается только устройствами Modbus).\n                    // Если равно нулю или превышает максимально допустимое значение, используется максимально допустимое значение (125).\n                    // Если не указано, используется значение по умолчанию (1).\n                    \"max_read_registers\": 10,\n\n                    // Минимальное число регистров, считываемых за один запрос\n                    // Минимальное количество регистров в одной пакетной операции чтения (в данный момент поддерживается только устройствами Modbus).\n                    \"min_read_registers\": 1,\n\n                    // Максимальное число регистров, записываемых за один запрос\n                    // Максимальное количество 16-битных регистров в одной пакетной операции записи (в данный момент поддерживается только устройствами Modbus).\n                    // Используется для пакетной записи параметров устройств при запуске wb-mqtt-serial.\n                    // Следует иметь в виду, что пакетная запись не поддерживает запись отдельных битов в регистры, такие регистры будут записываться по одному. \n                    // Если равно нулю или превышает максимально допустимое значение, используется максимально допустимое значение (123).\n                    // Если не указано, используется значение по умолчанию (1).\n                    // Значение может быть проигнорировано в случае, если оно меньше, чем количество 16-битных регистров в записываемом параметре.\n                    \"max_write_registers\": 10,\n\n                    // Максимальное время ответа устройства в миллисекундах.\n                    // Если не установлено, то принимается равным 500 мс.\n                    // Если значение этого параметра, установленное для порта, больше указанного здесь, используется значение порта.\n                    // Этот параметр задан в шаблонах описания устройств, переопределять его можно только в случае некорректной работы с устройством.\n                    \"response_timeout_ms\": 100,\n\n                    // Задержка между сообщениями (мс)\n                    // Минимально необходимая задержка между посылками в миллисекундах.\n                    // Используется в некоторых протоколах для определения границ посылок.\n                    // Этот параметр задан в шаблонах описания устройств, переопределять его можно только в случае некорректной работы с устройством.\n                    // По умолчанию 20 мс\n                    \"frame_timeout_ms\": 100,\n\n                    // Разделять сообщения по времени\n                    // Принудительно использовать определения границ пакета по времени между передачей байт.\n                    // В протоколе MODBUS для ускорения опроса после получения нужного числа байт в ответе, сразу шлётся следующий запрос.\n                    // Включение этого параметра приведёт к тому, что сервис будет ожидать время,\n                    // равное frame_timeout_ms, даже после получения полного ответа.\n                    // Данные, полученные сверх ожидаемой длины ответа, будут отброшены, а частота опроса может уменьшиться.\n                    // По умолчанию false\n                    \"force_frame_timeout\": true,\n\n                    // Дополнительная задержка перед записью в порт (мкс)\n                    // Установите этот параметр в значение не менее 3.5 символа при выбранной скорости — это не нужно для устройств Wiren Board, но может потребоваться для устройств сторонних производителей.\n                    // Для скорости 9600 бит/с guard_interval_us ≈ 4000 мкс. Для справки, формула: guard_interval_us = (3.5*11*10^6)/(скорость в бит/с).\n                    // Если не установлено, то используется значение, заданное в frame_timeout_ms, с учётом флага force_frame_timeout.\n                    \"guard_interval_us\": 0,\n\n                    // Время ожидания устройства (мс)\n                    // Интервал после последнего успешного обмена данными с устройством,\n                    // по истечении которого, а также после превышения \"device_max_fail_cycles\",\n                    // устройство будет помечено отключенным.\n                    \"device_timeout_ms\": 3000,\n\n                    // Максимальное число неудачных циклов опроса\n                    // Если в течение указанного количества полных циклов опроса ни по одному регистру устройства не поступило данных,\n                    // а также истек \"device_timeout_ms\", устройство будет помечено отключенным.\n                    // Если установлено -1, устройство считается всегда подключенным.\n                    \"device_max_fail_cycles\": 2,\n\n                    // Время повторных попыток записать регистр (с)\n                    // Время в секундах, в течение которого будут продолжаться попытки записи регистра\n                    // При нуле, будет произведена только одна попытка записи регистра\n                    \"max_write_fail_time_s\": 600,\n\n                    // Минимальное время в миллисекундах между получением ответа от устройства и следующим запросом к нему\n                    \"min_request_interval\": 10,\n\n                    // Пароль в виде массива байт\n                    // Пароль для доступа к устройству, массив байт\n                    \"password\": [1, 2, 3],\n\n                    // Уровень доступа\n                    // Используется для обмена с счётчиками электроэнергии\n                    \"access_level\": 1,\n\n                    // Параметр, заданный в шаблоне устройства\n                    \"param1\": 10,\n\n                    // Список каналов устройства\n                    \"channels\": [\n                        {\n                            // Имя канала, используется в диагностических сообщениях и, если не задан параметр id, для формирования названия MQTT topic'ов канала\n                            \"name\": \"Temp 1\",\n\n                            // Имя MQTT контрола канала. \n                            // Topic'и, соответствующие каналу, публикуются как /devices/\u003cидентификатор устройства\u003e/controls/\u003cID канала\u003e.\n                            // Если не задан, то используется значение параметра \"name\".\n                            \"id\": \"Temp\",\n\n                            // Включает канал в цикл опроса, по умолчанию - true\n                            \"enabled\": true,\n\n                            // Тип регистра.\n                            // Возможные значения для Modbus:\n                            //   \"coil\" - 1 бит, чтение/запись\n                            //   \"discrete\" - 1 бит, только чтение\n                            //   \"holding\" - 16 бит, чтение/запись, код функции на запись выбирается автоматически, в зависимости от размера\n                            //   \"input\" - 16 бит, только чтение\n                            //   \"holding_single\" - то же, что и \"holding\", но регистры записываются всегда по одному, кодом 06\n                            //   \"holding_multi\" - то же, что и \"holding\", но регистры записываются всегда кодом 16\n                            //   \"press_counter\" - то же, что \"input\", предназначен для регистров, которые обнуляются после перезагрузки устройства (0 после перезагрузки не публикуется в MQTT, публикуется следующее полученное ненулевое значение)\n                            \"reg_type\": \"input\",\n\n                            // Адрес первого регистра канала.\n                            // Можно читать отдельные биты полученных регистров, для этого запишите адрес в формате:\n                            //   \"address\":\"reg:shift:width\", где\n                            //     reg — адрес первого регистра,\n                            //     shift — смещение от младшего бита первого регистра,\n                            //     width — количество считываемых битов.\n                            // Например, \"address\":\"109:1:2\" — прочитать второй и третий биты регистра, расположенного по адресу 109.\n                            \"address\": 0,\n\n                            // Адрес первого регистра канала только для записи (задается при необходимости).\n                            // Если задан, то чтение параметра производится по адресу, указанному в \"address\", а запись — по адресу, указанному в \"write_address\".\n                            // При этом, если параметр \"address\" не указывать, то будет доступна только запись по адресу в \"write_address\".\n                            \"write_address\": 10,\n\n                            // Тип элемента управления, например: \"value\", \"text\", \"switch\"\n                            \"type\": \"value\",\n                            // Единицы измерения, например: \"deg C\", \"V\", \"A\"\n                            \"units\": \"deg C\",\n\n                            // Формат канала, задаётся для регистров типа \"holding\" и \"input\".\n                            // Возможные значения:\n                            //   \"u16\" - беззнаковое 16-битное целое (используется по умолчанию)\n                            //   \"s16\" - знаковое 16-битное целое\n                            //   \"u8\" - беззнаковое 8-битное целое\n                            //   \"s8\" - знаковое 8-битное целое\n                            //   \"u32\" - беззнаковое 32-битное целое (занимает 2 регистра, начиная с указанного)\n                            //   \"s32\" - знаковое 32-битное целое (занимает 2 регистра, начиная с указанного)\n                            //   \"s64\" - знаковое 64-битное целое (занимает 4 регистра, начиная с указанного)\n                            //   \"u64\" - беззнаковое 64-битное целое (занимает 4 регистра, начиная с указанного)\n                            //   \"float\" - число с плавающей точкой IEEE 754, 32 bit (занимает 2 регистра, начиная с указанного)\n                            //   \"double\" - число с плавающей точкой двойной точности IEEE 754, 64 bit (занимает 4 регистра, начиная с указанного)\n                            //   \"char8\" - однобайтовый символ в кодировке ASCII\n                            //   \"bcd8\" -  8-битный BCD (двоично-десятичный код)\n                            //   \"bcd16\" - 16-битный BCD (двоично-десятичный код)\n                            //   \"bcd24\" - 24-битный BCD (двоично-десятичный код)\n                            //   \"bcd32\" - 32-битный BCD (двоично-десятичный код)\n                            //   \"string\" - строка с настраиваемым количеством символов\n                            //              Для протокола Modbus строки считываются по одному символу на регистр из младшего или старшего байта, в зависимости от значения параметра \"byte_order\" (для \"big_endian\" используется младший байт, для \"little_endian\" - старший).\n                            //              При использовании формата string обязательно указывать параметр \"string_data_size\".\n                            //              Строка вычитывается до достижения указанного размера или пока не появится байт 0x00 или 0xFF.\n                            //   \"string8\" - то же, что и \"string\", но по два символа на регистр, порядок символов в регистре зависит от значения параметра \"byte_order\"\n                            \"format\": \"s8\",\n\n                            // Порядок 16-битных слов для каналов, имеющих размер больше 16 бит.\n                            // В данный момент не поддерживается для регистров типа \"holding_single\".\n                            // Возможные значения:\n                            //  \"big_endian\" (по умолчанию): [0xAA 0xBB] [0xCC 0xDD] =\u003e 0xAABBCCDD\n                            //  \"little_endian\":  [0xAA 0xBB] [0xCC 0xDD] =\u003e 0xCCDDAABB\n                            \"word_order\": \"big_endian\",\n\n                            // Порядок байт в 16-битных словах.\n                            // Возможные значения:\n                            //  \"big_endian\" (по умолчанию): [0x12 0x34] =\u003e 0x1234\n                            //  \"little_endian\":  [0x12 0x34] =\u003e 0x3412\n                            \"byte_order\": \"big_endian\",\n\n                            // Период чтения данного канала в миллисекундах.\n                            // Рекомендуется использовать для каналов, данные от которых надо получать с минимальными задержками.\n                            // Чтение каналов с заданным периодом имеет больший приоритет, чем чтение других каналов.\n                            // Если не указан, чтение будет производиться в свободное от опроса других каналов время.\n                            \"read_period_ms\": 10,\n\n                            // Интервал в миллисекундах, который должен пройти между двумя последовательными чтениями канала.\n                            // Учитывается, если не задан параметр \"read_period_ms\".\n                            // Не рекомендуется к использованию, поддерживается для обратной совместимости с ранее созданными конфигурационными файлами.\n                            // Вместо него рекомендуется использовать read_period_ms.\n                            \"read_rate_limit_ms\": 10000,\n\n                            // Значение, получаемое при последовательном чтении диапазона регистров, если устройство не поддерживает запрашиваемый регистр.\n                            // Этот параметр используется некоторыми протоколами, чтобы определить доступность регистров устройства.\n                            \"unsupported_value\": \"0xFFFE\",\n\n                            // Максимальное значение регистра, используется для построения интерфейса веб-конфигуратора\n                            \"max\": 100,\n\n                            // Коэффициент, на который умножается значение регистра перед публикацией в MQTT\n                            \"scale\": 0.5,\n\n                            // Значение, которое прибавляется к значению регистра перед публикацией в MQTT\n                            \"offset\": -12.5,\n\n                            // Порядок, до которого будет округляться значение после всех преобразований\n                            \"round_to\": 0.1,\n\n                            // Включение событий быстрого Modbus, если они поддерживаются прошивкой устройства.\n                            // Используйте \"sporadic\" для дискретных каналов — будут использоваться события вместо опроса и \"semi-sporadic\" для аналоговых — будут использоваться события совместно с опросом, это позволит избежать «застывания» значений аналогового канала.\n                            \"sporadic\": true,\n\n                            // Список возможных значений\n                            \"enum\": [1, 2, 3],\n\n                            // Надписи значений\n                            \"enum_titles\": [\"one\", \"two\", \"three\"],\n\n                            // Доступен ли канал для записи через MQTT\n                            \"readonly\": true,\n\n                            // Значение, которое будет записано в регистр, при записи единицы в on-топик в MQTT\n                            \"on_value\": \"0xFF\",\n\n                            // Значение, которое будет записано в регистр, при записи нуля в on-топик в MQTT\n                            \"off_value\": \"0xAA\",\n\n                            // Значение регистра, полученное от устройства, которое обозначает ошибку\n                            \"error_value\": \"0xAA\",\n\n                            // Количество используемых регистров для протокола Modbus. Необходимая опция, если выбран формат \"string\" или \"string8\"\n                            \"string_data_size\": 5,\n\n                            // Скрывает канал из web-интерфейса, канал публикуется всегда.\n                            // Используется для служебных каналов, которые не должны быть отключены пользователем.\n                            \"hidden\": true\n                        },\n                        {\n                            // Ещё один канал\n                            \"name\": \"Illuminance\",\n                            \"reg_type\": \"input\",\n                            \"address\": 1,\n                            \"type\": \"text\"\n                        },\n                        {\n                            \"name\": \"Pressure\",\n                            \"reg_type\": \"input\",\n                            \"address\": 2,\n                            \"type\": \"text\",\n                            \"scale\": 0.075\n                        },\n                        {\n                            \"name\": \"Temp 2\",\n                            \"reg_type\": \"input\",\n                            \"address\": 3,\n                            \"type\": \"value\",\n                            \"units\": \"deg C\",\n                            \"format\": \"s8\"\n                        }\n                    ]\n                },\n                {\n                    // ещё одно устройство на канале\n                    \"name\": \"DRB88\",\n                    \"id\": \"drb88\",\n                    \"enabled\": true,\n                    \"slave_id\": 22,\n\n                    // секция инициализации\n                    \"setup\": [\n                        {\n                            // Название регистра.\n                            // Выводится в случае включённой отладочной печати.\n                            \"title\": \"Input 0 type\",\n\n                            // Адрес первого регистра для записи.\n                            // Для Modbus устройств можно записывать отдельные биты указанных регистров, для этого запишите адрес в формате:\n                            //   \"address\": \"reg:shift:width\", где\n                            //     reg — адрес первого регистра,\n                            //     shift — смещение от младшего бита первого регистра,\n                            //     width — количество записываемых битов\n                            // Например, \"address\": \"109:1:2\" — записать второй и третий биты регистра, расположенного по адресу 109.\n                            \"address\": 1,\n\n                            // Значение для записи\n                            \"value\": \"1\",\n\n                            // Тип регистра, для Modbus по умолчанию - \"holding\"\n                            \"reg_type\": \"holding\",\n\n                            // Формат регистра, для Modbus по умолчанию - u16\n                            \"format\": \"s8\"\n                        },\n                        {\n                            \"title\": \"Input 0 module\",\n                            \"address\": 3,\n                            \"value\": \"3\"\n                        }\n                    ],\n                    \"channels\": [\n                        {\n                            \"name\": \"Relay 1\",\n                            \"reg_type\": \"coil\",\n                            \"address\": 0,\n                            \"type\": \"switch\"\n                        },\n                        {\n                            \"name\": \"Relay 2\",\n                            \"reg_type\": \"coil\",\n                            \"address\": 1,\n                            \"type\": \"switch\"\n                        },\n                        // ...\n                        {\n                            \"name\": \"Input 2\",\n                            \"reg_type\": \"input\",\n                            \"address\": 1,\n                            \"type\": \"switch\",\n                            \"on_value\": 101\n                        },\n                        {\n                            \"name\": \"Input 3\",\n                            \"reg_type\": \"input\",\n                            \"address\": 2,\n                            \"type\": \"switch\",\n                            \"on_value\": 101\n                        },\n                        // ...\n                    ]\n                }\n            ]\n        },\n        {\n            // ещё один порт со своим набором устройств\n            \"path\": \"/dev/ttyNSC1\",\n            \"baud_rate\": 9600,\n            \"parity\": \"N\",\n            \"data_bits\": 8,\n            \"stop_bits\": 1,\n            \"enabled\": true,\n            \"devices\": [\n                {\n                    \"name\": \"tM-P3R3\",\n                    \"id\": \"tmp3r3\",\n                    \"enabled\": true,\n                    \"slave_id\": 1,\n                    \"channels\": [\n                        {\n                            \"name\": \"Relay 0\",\n                            \"reg_type\": \"coil\",\n                            \"address\": 0,\n                            \"type\": \"switch\"\n                        },\n                        // ...\n                    ]\n                },\n                // ...\n            ]\n        }\n    ]\n}\n```\n\nОбратите внимание что начиная с 2026г. вместо старых специфичных типов - вроде температуры:\n\n```jsonc\n    \"type\": \"temperature\",\n```\n\nНужно использовать общий тип `value` и отдельно указывать `unit`:\n\n```jsonc\n    \"type\": \"value\",\n    \"units\": \"deg C\",\n```\n\nАктуальные разрешенные типы и единицы измерения можно посмотреть в конвенции WB:\n- https://github.com/wirenboard/conventions\n\n### Шаблоны конфигурации\n\nШаблоны создаются для удобства конфигурации Modbus-устройств.\n\nШаблон конфигурации представляет собой .json файл, который содержит информацию о регистрах и параметрах устройства.\n\nДля примера в [файле](config.sample.json) приведены разные варианты записи параметров:\n\n- первое устройство задано через шаблон;\n- второе устройство тоже через шаблон, но параметры \"name\" и \"id\" заданы, и можно добавить конфигурацию для канала, который добавится к тем, что есть в шаблоне;\n- параметры третьего устройства записаны явно;\n- для четвёртого задано значение одного из параметров. Параметр описан в шаблоне.\n\nВ состав пакета изначально входят шаблоны конфигурации некоторых поддерживаемых устройств. Они хранятся на контроллере в папке `/usr/share/wb-mqtt-serial/templates`. Для хранения пользовательских шаблонов используется папка `/etc/wb-mqtt-serial.conf.d/templates`.\n\nДля создания собственного шаблона можно скопировать существующий из папки `/usr/share/wb-mqtt-serial/templates`, изменить в нем нужные параметры, добавить необходимые регистры устройства и сохранить его в папку `/etc/wb-mqtt-serial.conf.d/templates` с новым именем.\n\n#### Устаревание шаблона\n\nЕсли необходимо внести обратно-несовместимые изменения в шаблон:\n\n1. **Создайте копию** текущего шаблона, которая станет устаревшей.\n\n2. **Переименуйте** созданную копию, добавив к имени файла постфикс `-deprecated`.\n\n   В итоге у вас получится два разных файла, например:\n\n   ```files\n   templates/config-danfoss-ekc-204a1-wb-ref-deprecated.json (Старый переименованный шаблон)\n   templates/config-danfoss-ekc-204a1-wb-ref.json (Новый актуальный шаблон, который будем изменять)\n   ```\n\n3. **Отметьте старый шаблон как устаревший**, чтобы скрыть его при новых установках.\n   Для этого добавьте параметр `\"deprecated\": true` внутрь JSON-схемы старого шаблона.\n\n4. **Обновите `device_type`** в файле *нового* шаблона, добавив префикс с версией (`tpl1_`, `tpl2_` и т.д.).\n   Например:\n   * Было (в старом): `\"device_type\": \"danfoss_ekc_204a1_wb_ref\",`\n   * Станет (в новом): `\"device_type\": \"tpl1_danfoss_ekc_204a1_wb_ref\",`\n\n \u003e **Обратите внимание:** значение `device_type` в старом (устаревшем) шаблоне менять нельзя! \n \u003e Это необходимо, чтобы он продолжал работать у пользователей, которые уже его используют.\n\n#### Пример шаблона\n\nПосле добавления шаблона конфигурации вы можете выбрать новое устройство из выпадающего списка в настройках serial устройств в веб-интерфейсе контроллера Wiren Board.\n\n```jsonc\n{\n    // Название типа устройства, оно указывается в поле device_type в файле настроек\n    \"device_type\": \"Device type name\",\n\n    // Группа устройств в выпадающем списке в настройках последовательного порта в веб-интерфейсе контроллера\n    // Если не задано устройство будет отображаться внизу списка\n    // Возможные варианты:\n    // g-wb — устройства Wiren Board\n    // g-wb-old — устаревшие устройства Wiren Board\n    // g-adapter — адаптеры протоколов\n    // g-climate-sensor — датчики климата\n    // g-level — датчики уровня\n    // g-dimmer — диммеры\n    // g-air-conditioning — кондиционеры\n    // g-climate-control — контроллеры вентиляции и климата\n    // g-refrigeration — контроллеры холодильного оборудования\n    // g-io — модули ввода-вывода\n    // g-relay — модули реле\n    // g-curtain — моторы для штор / электрокарнизы\n    // g-control-panel — панели управления\n    // g-water-meter — счетчики воды\n    // g-heat-meter — счетчики тепла\n    // g-power-meter — счетчики электроэнергии\n    // g-thermostat — термостаты\n    // g-motor-control — управление двигателями (преобразователи частоты)\n    // g-custom — произвольные устройства\n\n    \"group\": \"g-wb\",\n\n    // Название типа устройства, которое будет отображаться в веб-конфигураторе.\n    // Необязательный параметр. Если не указан, используется device_type.\n    \"title\": \"Device\",\n\n    // Признак того, что шаблон устарел, он не будет доступен для выбора при добавлении устройства в веб-конфигураторе\n    // Также для такого шаблона будут игнорироваться возможные ошибки валидации на соответствие JSON схеме\n    \"deprecated\": true,\n\n    // Список сигнатур устройств, которые поддерживает шаблон\n    \"hw\": [\n        {\n            // Сигнатура устройства WirenBoard\n            \"signature\": \"WBMR6C\",\n\n            // Версия прошивки, начиная с которой можно использовать данный шаблон\n            // Этот параметр может отсутствовать, тогда шаблон подходит к любой версии прошивки\n            \"fw\": \"1.1.1\"\n        }\n    ],\n\n    \"device\": {\n        // отображаемое имя устройства. Публикуется как\n        // .../meta/name в MQTT\n        \"name\": \"New device\",\n\n        // Остальные параметры устройства, описанные выше в примере конфигурационного файла, кроме slave_id\n        ...\n\n        // Шаблон может иметь собственную секцию с настройками\n        \"setup\": [\n            {\n                \"title\": \"s1\",\n                \"address\": 20000,\n                \"value\": \"0xfff2\"\n            },\n            ...\n        ],\n\n        // Если true, то параметры из секции \"setup\" записываются в устройство в том порядке, в котором они перечислены в шаблоне.\n        // По умолчанию параметры сортируются по типу регистра и адресу.\n        \"preserve_setup_order\": false,\n\n        // Секция с описанием параметров устройства.\n        // Значение параметра можно задать в файле конфигурации или через веб-конфигуратор\n        \"parameters\": [\n            {\n                // Имя параметра, которе будет использовано в конфигурационном файле\n                \"id\": \"param1\",\n\n                // Название параметра в веб-конфигураторе\n                \"title\": \"s22\",\n\n                // Адрес первого регистра (должен быть одинаковым для параметров с одинаковым значением \"id\").\n                // Для Modbus устройств можно использовать отдельные биты указанных регистров, для этого запишите адрес в формате:\n                //   \"address\": \"reg:shift:width\", где\n                //     reg — адрес первого регистра,\n                //     shift — смещение от младшего бита первого регистра,\n                //     width — количество записываемых битов\n                // Например, \"address\": \"109:1:2\" — использовать второй и третий биты регистра, расположенного по адресу 109.\n                \"address\": 9992,\n\n                // Адрес первого регистра параметра только для записи (задается при необходимости).\n                // Если задан, то чтение параметра производится по адресу, указанному в \"address\", а запись — по адресу, указанному в \"write_address\".\n                // Должен быть одинаковым для параметров с одинаковым значением \"id\".\n                \"write_address\": 9995,\n\n                // Минимальная версия прошивки устройства, поддерживающая параметр.\n                // Если не задана - параметр поддерживается любой прошивкой.\n                // Значение должно быть одинаковым для параметров с одинаковым значением \"id\".\n                \"fw\": \"1.2.3\",\n\n                // Тип регистра\n                \"reg_type\": \"input\",\n\n                // Формат регистра\n                \"format\": \"s8\",\n\n                // Список возможных значений\n                \"enum\": [1, 2, 3],\n\n                // Надписи в списке выбора в веб-конфигураторе\n                \"enum_titles\": [\"one\", \"two\", \"three\"],\n\n                // Значение по умолчанию в веб-конфигураторе\n                \"default\": 2,\n\n                // Минимально возможное значение\n                \"min\": 1,\n\n                // Максимально возможное значение\n                \"max\": 3,\n\n                // Коэффициент, на который делится значение параметра перед записью в регистр\n                \"scale\": 2,\n\n                // Значение, которое прибавляется к значению параметра перед записью в регистр\n                \"offset\": 10,\n\n                // Этот параметр должен быть обязательно задан в wb-mqtt-serial.conf\n                \"required\": true,\n\n                // Порядок отображения параметра в веб-конфигураторе\n                \"order\": 1,\n\n                // Группа, к которой относится параметр\n                \"group\": \"group1\",\n\n                // Условие, при выполнении которого, значение параметра будет записано в устройство\n                // В состав условия могут входить целые числа, названия других параметров,\n                // операции сравнения (\u003e, \u003c, \u003e=, \u003c=, ==, !=), логические операции (\u0026\u0026, ||) и функция isDefined.\n                // При вычислении условия вместо имён параметров подставляются их значения из конфигурационного файла.\n                // Если какой-то параметр из условия не задан в конфигурационном файле,\n                // то операции сравнения (\u003e, \u003c, \u003e=, \u003c=, ==) с ним возвращают false, операция проверки неравенства (!=) - возвращает true.\n                // Функция isDefined позволяет определить задан ли параметр в конфигурационном файле\n                // Условие также определяет доступность параметра для редактирования в интерфейсе веб-конфигуратора\n                \"condition\": \"isDefined(param1)\u0026\u0026(param2==1)||(param3\u003e5)\",\n\n                // Признак того, что значение параметра не записывается в регистры устройства и не читается из них.\n                // Параметр никак не связан с устройством, поля адреса игнорируются. \n                // Может быть использовано для организации настроек в веб-конфигураторе.\n                // В сочетании с полем condition в зависимости от значения будут отображены только нужные каналы и параметры.\n                // Значение параметра можно менять, оно будет сохраняться в конфигурационном файле\n                \"readonly\": true\n            }\n        ],\n\n        // Список каналов\n        \"channels\": [\n            // Пример канала, описывающий данные одного регистра\n            {\n                \"name\": \"Temperature\",\n                \"reg_type\": \"input\",\n                \"format\": \"s32\",\n                \"address\": \"0x0504\",\n                \"group\": \"group1\",\n\n                // Условие, при выполнении которого, канал будет доступен для опроса\n                \"condition\": \"(param2==1)||(param3==5)\"\n            },\n            ...\n        ],\n\n        // Группы используются для удобной организации интерфейса веб-конфигуратора\n        // и не влияют на структуру конфигурационного файл\n        \"groups\": [\n            // Описание группы\n            {\n                // Уникальное имя группы, отображается в веб-конфигураторе\n                \"title\": \"Group 1\",\n\n                // Идентификатор группы\n                \"id\": \"group1\",\n\n                // Группа вложена в другую группу\n                \"group\": \"group2\",\n\n                // Описание группы\n                \"description\": \"Group description\"\n            },\n            ...\n        ]\n    }\n}\n```\n\n### Перевод названий параметров устройств\n\nПереводы используются в веб-конфигураторе для формирования интерфейса настройки устройств. Они описываются в шаблоне устройства в секции `translations`. Эта секция содержит переводы строк, которые могут встречаться в названиях параметров, отчетах об ошибках и т.д. Секция содержит набор параметров `\"Язык\": \"Строка на английском\": \"Перевод\"`.\nНапример:\n\n```jsonc\n{\n    \"device_type\": \"Example\",\n    \"title\": \"Device\",\n    \"device\": {\n        \"name\": \"Example device\",\n        \"channels\": [\n            {\n                \"name\": \"Temperature\",\n                \"reg_type\": \"holding\",\n                \"address\": 1,\n                \"group\": \"group 1\"\n            }\n        ],\n        \"parameters\": [\n            {\n                \"id\": \"timeout\",\n                \"title\": \"Timeout\",\n                \"address\": 9992,\n                \"group\": \"group 1\"\n            }\n        ],\n        \"groups\": [\n            {\n                \"title\": \"Group 1\",\n                \"id\": \"group1\",\n                \"order\": 3\n            }\n        ],\n        \"translations\": {\n            \"ru\": {\n                \"Device\": \"Устройство\",\n                \"Temperature\": \"Температура\",\n                \"Timeout\": \"Задержка\",\n                \"Group 1\": \"Группа 1\"\n            }\n        }\n    }\n}\n```\n\n## Особенности работы драйвера\n\n### Таймауты и количество неудачных циклов\n\nЦиклом опроса устройства считается внутренний цикл опроса драйвера внутри которого был опрошен хотя бы один из регистров данного устройства.\n\n`connection_timeout_ms` и `connection_max_fail_cycles` - Необходимы для автоматического восстановления TCP-соединения TCP или переинициализации последовательного порта. Если в течение `connection_timeout_ms` и более чем `connection_max_fail_cycles` подряд циклов опроса, опрашиваемые устройства были помечены как отключенные, соединение сбрасывается и происходит попытка переподключения. Можно использовать только один тип таймаута, для этого нужно выставить значение 0 другому типу таймаута (например, чтобы осуществлять обнаружение разрыва соединения только по времени, нужно выставить \"`connection_max_fail_cycles`\": 0). При большом количестве устройств на порту, длительность цикла опроса устройств может сильно варьироваться в зависимости от числа отвечающих устройств, т.к. они вносят дополнительные задержки на ожидание ответа, поэтому если нужно обозначить минимальное количество циклов опроса до отключения вне зависимости от числа устройств, можно использовать вариант `connection_max_fail_cycles`. При использовании только `connection_timeout_ms`, на количество попыток обращения к порту будут влиять другие временные настройки, такие как `poll_interval`, `guard_interval`, `response_timeout` и при их изменении возможно придется подстраивать значение `connection_timeout_ms`. Если же нужно исключить срабатывание таймаута на каких-то кратковременных случайных ошибках, которые не стоит считать обрывом связи, то нужно использовать `connection_timeout_ms`. При использовании параметров вместе, таймаут сработает только когда выполнятся оба условия, т.е. пройдет нужное время и количество циклов.\n\n`device_timeout_ms` и `device_max_fail_cycles` - указываются для устройства. По семантике аналогичен `connection_timeout_ms` и `connection_max_fail_cycles`, но только для устройства. Нужен для выявления отключения устройства для повторной отправки setup - секции при переподключении устройства. Если в течение `device_timeout_ms` и более чем `device_max_fail_cycles` подряд циклов ни один из опрошенных регистров не был успешно прочитан, то устройство будет помечено как отсоединенное и будет опрашиваться в ограниченном режиме, т.е. при наличии у устройства setup - секции, драйвер будет пытаться записать ее, а в противном случае, будет пытаться опросить устройство. Если первое обращение к устройству в ограниченном режиме закончилось ошибкой, драйвер считает что устройство все еще отключено и больше не опрашивает его в этом цикле. Это позволяет тратить меньше времени на отключенные устройства. Первый успешный запрос к устройству будет расценен как переподключение устройства.\n\n#### Значения по умолчанию\n|Параметр                   | Значение  |\n|:--------------------------|----------:|\n|device_timeout_ms          | 3000      |\n|device_max_fail_cycles     | 2         |\n|connection_timeout_ms      | 5000      |\n|connection_max_fail_cycles | 2         |\n\n### Замечания для TCP или MODBUS TCP порта\n\nПри использовании TCP мостов, драйвер не видит разницы между двумя ситуациями:\n\n- физически отключены все устройства от моста\n- разорвано TCP соединение с мостом\n\nтак как в обоих случаях никаких данных драйвер не получает. Поэтому, в этом случае, connection_timeout для порта и device_timeout для устройств истекают одновременно.\n\nЭто влечет за собой периодический сброс TCP соединения и переподключение к мосту в ситуации когда физически отключены все устройства от моста, хотя с самим TCP соединением проблем нет.\n\nВ ситуации когда хотя бы одно из опрашиваемых устройств подключено к мосту без проблем с TCP соединением, TCP подключение не будет сбрасываться, а таймаут будет отсчитываться только для отключенных устройств.\n\n### Диаграмма таймаутов цикла опроса\n\n![Диаграмма таймаутов цикла опроса](doc/timeouts.svg)\n\n### Объединенное чтение регистров и его авто-отключение\n\nДля ускорения опроса регистров устройств, драйвер объединяет чтение соседних регистров в один запрос (см. `max_reg_hole`, `max_bit_hole`), однако, считывание т.н. \"пустых\" регистров может привести к ошибкам на некоторых устройствах. Как только драйвер получает от устройства ошибку при считывании множества регистров, среди которых есть пустые, которая могла быть вызвана чтением пустых регистров (для Modbus: `ILLEGAL_DATA_ADDRESS`, `ILLEGAL_DATA_VALUE`), драйвер перестает объединённо считывать эти регистры.\nУстройства Wiren Board поддерживают [режим сплошного чтения регистров](https://wirenboard.com/wiki/Modbus#%D0%A0%D0%B5%D0%B6%D0%B8%D0%BC_%D1%81%D0%BF%D0%BB%D0%BE%D1%88%D0%BD%D0%BE%D0%B3%D0%BE_%D1%87%D1%82%D0%B5%D0%BD%D0%B8%D1%8F_%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D0%BE%D0%B2). Для его активации надо установить параметр `enable_wb_continuous_read` в шаблоне или настройках устройства.\n\n### Автоматическое отключение опроса регистров\n\nОпрос регистров может быть автоматически отключен по одной из следующих причин:\n- Modbus-исключение `ILLEGAL_FUNCTION`, `ILLEGAL_DATA_ADDRESS` или `ILLEGAL_DATA_VALUE` (кроме случая, описанного [здесь](#объединенное-чтение-регистров-и-его-авто-отключение))\n- считанное значение совпадает со значением параметра `unsupported_value` в настройках канала\n- канал использует события быстрого Modbus (`\"sporadic\": true` в настройках канала)\n\nПоведение для Modbus-исключений можно изменить параметром `continue_polling_on_unsupported` в шаблоне или настройках устройства. Если он установлен в `true`, регистры, на которые устройство отвечает `ILLEGAL_FUNCTION`/`ILLEGAL_DATA_ADDRESS`/`ILLEGAL_DATA_VALUE`, остаются в опросе, а соответствующие контролы публикуются в MQTT с признаком ошибки. \n\n### Поведение в случае, если отключен опрос всех каналов, кроме каналов с событиями\n\nВ случае, если отключен опрос всех каналов, кроме каналов с событиями (`\"sporadic\": true`), один из каналов с событиями автоматически добавляется в цикл опроса. Это необходимо для того, чтобы отслеживать доступность устройства и своевременно генерировать событие `.../meta/error` в MQTT, если связь с устройством потеряна. Период опроса этого канала устанавливается равным значению параметра `read_period_ms` в настройках канала. Если параметр `read_period_ms` не настроен, используется значение по умолчанию (500 мс).\n\n### Список сконфигурированных портов\n\nСписок портов можно получить, выполнив MQTT RPC запрос `wb-mqtt-serial/ports/Load`. Он возвращает JSON массив следующего вида:\n\n```jsonc\n[\n   {\n       \"baud_rate\": 9600,\n       \"data_bits\": 8,\n       \"parity\": \"N\",\n       \"path\": \"/dev/ttyRS485-2\",\n       \"stop_bits\": 1\n   },\n   {\n       \"address\": \"127.0.0.1\",\n       \"port\": 2000\n   },\n   ...\n]\n```\n\n### Прямое чтение и запись в порт\n\nСуществует возможность выполнить запись и чтение из порта посредством MQTT RPC запроса. Выполнение запроса встраивается в цикл опроса устройств таким образом, что запрос выполнится с высоким приоритетом сразу после окончания текущего цикла опроса.\nДля упрощенного использования данного функционала написана [Python-библиотека](https://github.com/wirenboard/python-mqtt-rpc/). Также по [ссылке](https://github.com/wirenboard/modbus-utils-rpc) доступна утилита для работы с modbus-устройствами при помощи RPC-функционала wb-mqtt-serial.\nДля выполнения запроса необходимо отправить в топик `wb-mqtt-serial/port/Load/client_id`, где `client_id` - произвольное имя клиента, посылающего запрос, сообщение типа JSON со следующими параметрами:\n\n#### Параметры\n\n|Параметр          |Тип          |Значение по умолчанию |Описание |\n|------------------|-------------|----------------------|---------|\n|`msg`             |обязательный |                      |текстовое сообщение для отправки в порт\n|`response_size`   |обязательный |                      |количество байт, которое нужно прочитать из порта\n|`format`          |опциональный |`STR`                 |при указании значения `STR` содержимое параметра `msg` интерпретируется как строка и передается в порт как есть. При указании значения `HEX` содержимое `msg` интерпретируется как шестнадцатеричная строка и перед отправкой в порт преобразуется в массив байт\n|`response_timeout`|опциональный |500 ms                |таймаут чтения первого байта в миллисекундах. Значение сравнивается с параметром `response_timeout_ms` соответствующего порта в настройках wb-mqtt-serial и используется максимальное\n|`frame_timeout`   |опциональный |20 ms                 |таймаут чтения каждого последующего байта в миллисекундах\n|`total_timeout`   |опциональный |10000 ms              |таймаут выполнения RPC-запроса в миллисекундах\n\n##### Обязательные параметры запроса в последовательный порт\n|Параметр   |Описание |\n|-----------|---------|\n|`path`     |путь к последовательному порту, в который будет отправлено сообщение\n|`baud_rate`|скорость порта\n|`parity`   |чётность - N, O или E\n|`data_bits`|количество бит данных\n|`stop_bits`|количество стоп-бит\n\n##### Обязательные параметры запроса в TCP порт\n|Параметр |Описание |\n|---------|---------|\n|`ip`     |IP-адрес или имя хоста клиента, которому будет отправлено сообщение\n|`port`   |номер порта на указанном адресе клиента\n\nВ качестве ответа в топике `wb-mqtt-serial/port/Load/client_id/reply` будет опубликовано сообщение типа JSON со следующими параметрами:\n\n|Параметр | Описание|\n|---------|---------|\n|`result`|В случае неуспешного выполнения запроса содержит `null`. В случае успешного выполнения содержит в себе единственное поле `response`. В случае, если при запросе в поле `format` было указано `STR`, значение поля представляет собой набор символов, полученных из порта, и передается запрашивающей стороне как есть. В случае значения `HEX` содержимое поля представляет собой шестнадцатеричную строку\n|`id`| Порядковый номер запроса|\n|`error`|В случае успешного выполнения запроса содержит значение `null`. В ином случае содержит параметры, приведенные в таблице ниже\n\n|Параметр|Описание|\n|--------|--------|\n|`message`|Строка с кратким описанием ошибки|\n|`code`| Код ошибки. Возможные коды ошибок приведены в таблице ниже|\n|`data`| Строка с подробным описанием места и условий возникновения ошибки|\n\n|Код ошибки|Описание|\n|----------|--------|\n|`-32700`|Ошибка разбора JSON запроса|\n|`-32000`|Ошибка выполнения запроса|\n|`-32600`|Таймаут выполнения запроса|\n\nПримеры выполнения запросов:\n\n1. Успешное выполнение запроса:\n   ```\n   RPC Client -\u003e {\"params\": {\"total_timeout\": 10000, \"response_size\": 8, \"format\": \"HEX\", \"path\": \"/dev/ttyRS485-2\", \"baud_rate\": 9600, \"parity\": \"N\", \"data_bits\": 8, \"stop_bits\": 2, \"msg\": \"0A03008000018499\"}, \"id\": 1}\n   RPC Client \u003c- {\"error\":null,\"id\":1,\"result\":{\"response\":\"1605000aff00af1f\"}}\n   ```\n\n2. Ошибка разбора JSON запроса\n   ```\n   RPC Client -\u003e {\"params\"\"path\": \"/dev/ttyRS485-34534\", \"response_size\": 8, \"total_timeout\": 10000, \"msg\": \"1605000aff00af1f\", \"format\": \"HEX\"}, \"id\": 1}\n   RPC Client \u003c-{\"error\":{\"code\":-32700,\"message\":\"Parse error\"},\"id\":null}\n   ```\n\n3. Ошибка выполнения запроса (ошибка ввода-вывода)\n   ```\n   RPC Client -\u003e {\"params\": {\"total_timeout\": 10000, \"response_size\": 8, \"format\": \"HEX\", \"path\": \"/dev/ttyRS485-2\", \"baud_rate\": 9600, \"parity\": \"N\", \"data_bits\": 8, \"stop_bits\": 2, \"msg\": \"0A03008000018499\"}, \"id\": 1}\n   RPC Client \u003c- {\"error\":{\"code\":-32000,\"data\":\"Port IO error: request timed out\",\"message\":\"Server error\"},\"id\":1,\"result\":null}\n   ```\n\n4. Ошибка выполнения запроса (запрос в несуществующий порт)\n   ```\n   RPC Client -\u003e {\"params\": {\"total_timeout\": 10000, \"response_size\": 8, \"format\": \"HEX\", \"path\": \"/dev/ttyRS485-31337\", \"baud_rate\": 9600, \"parity\": \"N\", \"data_bits\": 8, \"stop_bits\": 2, \"msg\": \"0A03008000018499\"}, \"id\": 1}\n   RPC Client -\u003e {\"error\":{\"code\":-32000,\"data\":\"Requested port doesn't exist\",\"message\":\"Server error\"},\"id\":1,\"result\":null}\n   ```\n\n5. Таймаут выполнения запроса (слишком малое значение таймаута)\n   ```\n   RPC Client -\u003e {\"params\": {\"response_size\": 8, \"format\": \"HEX\", \"path\": \"/dev/ttyRS485-2\", \"baud_rate\": 9600, \"parity\": \"N\", \"data_bits\": 8, \"stop_bits\": 2, \"msg\": \"0A03008000018499\", \"total_timeout\": 10}, \"id\": 1}\n   RPC Client \u003c- {\"error\":{\"code\":-32600,\"data\":\"Request handler is not responding @ src/rpc_handler.cpp:179\",\"message\":\"Request timeout\"},\"id\":1,\"result\":null}\n   ```\n\n### Чтение и запись по протоколу Modbus\n\nСуществует возможность выполнить запись и чтение регистров произвольного устройства по протоколу Modbus посредством MQTT RPC запроса. Выполнение запроса встраивается в цикл опроса устройств таким образом, что запрос выполнится с высоким приоритетом сразу после окончания текущего цикла опроса.\nДля упрощенного использования данного функционала написана [Python-библиотека](https://github.com/wirenboard/python-mqtt-rpc/). Также по [ссылке](https://github.com/wirenboard/modbus-utils-rpc) доступна утилита для работы с modbus-устройствами при помощи RPC-функционала wb-mqtt-serial.\nДля выполнения запроса необходимо отправить в топик `wb-mqtt-serial/port/Load/client_id`, где `client_id` - произвольное имя клиента, посылающего запрос, сообщение типа JSON со следующими параметрами:\n\n#### Параметры\n\n|Параметр          |Тип          |Значение по умолчанию |Описание |\n|------------------|-------------|----------------------|---------|\n|`protocol`        |обязательный |`modbus`, `modbus-tcp`|протокол. При указании значения `modbus` совместно с `ip` и `port` в сокет отправляется Modbus RTU сообщение, что используется при работе через прозрачные шлюзы Modbus RTU-over-TCP. Для работы по Modbus TCP укажите `modbus-tcp`.\n|`slave_id`        |обязательный |                      |адрес устройства\n|`function`        |обязательный |                      |код Modbus-команды\n|`address`         |обязательный |                      |адрес первого Modbus-регистра\n|`count`           |опциональный | 1                    |число Modbus-регистров\n|`write_address`   |опциональный |                      |адрес первого Modbus-регистра для записи, в случае использования функции `23` (для других функций записи необходимо использовать параметр `address`)\n|`write_count`     |опциональный | 1                    |число Modbus-регистров для записи, в случае использования функции `23` (для других функций записи необходимо использовать параметр `count`)\n|`msg`             |опциональный |                      |данные Modbus-запроса, если необходимо\n|`format`          |опциональный |`STR`                 |при указании значения `STR` содержимое параметра `msg` интерпретируется как строка и передается в порт как есть. При указании значения `HEX` содержимое `msg` интерпретируется как шестнадцатеричная строка и перед отправкой в порт преобразуется в массив байт\n|`response_timeout`|опциональный |500 ms                |таймаут чтения первого байта в миллисекундах. Значение сравнивается с параметром `response_timeout_ms` соответствующего порта в настройках wb-mqtt-serial и используется максимальное\n|`frame_timeout`   |опциональный |20 ms                 |таймаут чтения каждого последующего байта в миллисекундах\n|`total_timeout`   |опциональный |10000 ms              |таймаут выполнения RPC-запроса в миллисекундах\n\n##### Обязательные параметры запроса в последовательный порт\n|Параметр   |Описание |\n|-----------|---------|\n|`path`     |путь к последовательному порту, в который будет отправлено сообщение\n|`baud_rate`|скорость порта\n|`parity`   |чётность - N, O или E\n|`data_bits`|количество бит данных\n|`stop_bits`|количество стоп-бит\n\n##### Обязательные параметры запроса в TCP порт\n|Параметр |Описание |\n|---------|---------|\n|`ip`     |IP-адрес или имя хоста клиента, которому будет отправлено сообщение\n|`port`   |номер порта на указанном адресе клиента\n\n##### Запрос с использованием идентификатора устройства\n\nЗапрос можно выполнить, указав идентификатор устройства в MQTT (часть имени топика `/devices/\u003cИДЕНТИФИКАТОР\u003e/controls/...`), он должен передаваться в параметре `device_id`. В этом случае настройки порта, протокол и адрес устройства берутся из конфигурационного файла `wb-mqtt-serial`, указывать их в запросе не надо.\n\n##### Структура ответа\n\nВ качестве ответа в топике `wb-mqtt-serial/port/Load/client_id/reply` будет опубликовано сообщение типа JSON со следующими параметрами:\n\n|Параметр | Описание|\n|---------|---------|\n|`result`|В случае неуспешного выполнения запроса содержит `null`. В случае успешного выполнения содержит в себе поле `response` или `exception`. Если при запросе в поле `format` было указано `STR`, значение поля `response` представляет собой только данные ответа, и передается запрашивающей стороне как есть. В случае значения `HEX` содержимое поля представляет собой шестнадцатеричную строку. Если устройство вернуло ответ с ошибкой, то передаётся поле `exception`, содержащее структуру из двух полей `code` и `msg`. Где `code` - код Modbus-исключения, `msg` - его текстовая расшифровка.\n|`id`| Порядковый номер запроса|\n|`error`|В случае успешного выполнения запроса содержит значение `null`. В ином случае содержит параметры, приведенные в таблице ниже\n\n|Параметр|Описание|\n|--------|--------|\n|`message`|Строка с кратким описанием ошибки|\n|`code`| Код ошибки. Возможные коды ошибок приведены в таблице ниже|\n|`data`| Строка с подробным описанием места и условий возникновения ошибки|\n\n|Код ошибки|Описание|\n|----------|--------|\n|`-32700`|Ошибка разбора JSON запроса|\n|`-32000`|Ошибка выполнения запроса|\n|`-32600`|Таймаут выполнения запроса|\n\n\n### Прямая установка параметров связи устройства\n\nСуществует возможность выполнить прямую установку параметров связи Modbus-устройства Wiren Board с помощью MQTT RPC запроса. Выполнение запроса встраивается в цикл опроса устройств таким образом, что запрос выполнится с высоким приоритетом сразу после окончания текущего цикла опроса.\nДля упрощенного использования данного функционала написана [Python-библиотека](https://github.com/wirenboard/python-mqtt-rpc/). Также по [ссылке](https://github.com/wirenboard/modbus-utils-rpc) доступна утилита для работы с modbus-устройствами при помощи RPC-функционала wb-mqtt-serial.\nДля выполнения запроса необходимо отправить в топик `wb-mqtt-serial/port/Setup/client_id`, где `client_id` - произвольное имя клиента, посылающего запрос, сообщение типа JSON со следующими параметрами:\n```json\n{\n    // Путь к последовательному порту, в который будет отправлено сообщение\n    \"path\": \"/dev/ttyRS485-1\",\n\n    \"items\":[\n        {\n            // Адрес устройства\n            \"slave_id\": 111,\n\n             // Скорость порта\n            \"baud_rate\": 115200,\n\n            // Чётность - N, O или E\n            \"parity\": \"N\",\n\n            // Количество бит данных\n            \"data_bits\": 8,\n\n            // Количество стоп-бит\n            \"stop_bits\": 2\n\n            // Настройки, которые будут применены к устройству\n            \"cfg\": {\n                // Скорость порта\n                \"baud_rate\": 115200,\n\n                // Чётность, задаётся числом\n                //   N - 0,\n                //   O - 1,\n                //   E - 2\n                \"parity\": 1,\n\n                // Количество стоп-бит\n                \"stop_bits\": 2\n            }\n        },\n        ...\n    ]\n}\n```\nПри успешном выполнении запроса возвращается ответ следующей структуры:\n```json\n{\n    \"error\": null,\n    \"id\": ID_ЗАПРОСА,\n    \"result\": {}\n}\n```\nОтветы с ошибками аналогичны прямому чтению и записи в порт.\n\n\n### Поиск устройств, используя Быстрый Модбас\n\nСуществует возможность выполнить поиск устройств, используя Быстрый Модбас, с помощью MQTT RPC запроса. Выполнение запроса встраивается в цикл опроса устройств таким образом, что запрос выполнится с высоким приоритетом сразу после окончания текущего цикла опроса.\nДля упрощенного использования данного функционала написана [Python-библиотека](https://github.com/wirenboard/python-mqtt-rpc/).\nДля выполнения запроса необходимо отправить в топик `wb-mqtt-serial/port/Scan/client_id`, где `client_id` - произвольное имя клиента, посылающего запрос, сообщение типа JSON со следующими параметрами:\n```json\n{\n    // Путь к последовательному порту, на которому будет производиться поиск\n    \"path\": \"/dev/ttyRS485-1\",\n\n    // Скорость порта\n    \"baud_rate\": 115200,\n\n    // Чётность - N, O или E\n    \"parity\": \"N\",\n\n    // Количество бит данных\n    \"data_bits\": 8,\n\n    // Количество стоп-бит\n    \"stop_bits\": 2,\n\n    // команда Быстрого Модбаса, которая будет использоваться при поиске\n    // актуальная - 70(0x46), устаревшая - 96(0x60)\n    // если не задана, то используется 70(0x46)\n    \"command\": 70,\n\n    // режим поиска\n    // all - будет произведён поиск всех устройств на шине\n    // start - начать поиск до нахождения первого устройства\n    // next - искать следующее устройство, если поиск был уже начат, используя режим start\n    \"mode\": \"start\"\n}\n```\n\nили\n\n```json\n{\n    // IP адрес или имя хоста шлюза\n    \"ip\": \"1.1.1.1\",\n\n    // Порт шлюза\n    \"port\": 1234,\n\n    // команда Быстрого Модбаса, которая будет использоваться при поиске\n    // актуальная - 70(0x46), устаревшая - 96(0x60)\n    // если не задана, то используется 70(0x46)\n    \"command\": 70,\n\n    // режим поиска\n    // all - будет произведён поиск всех устройств на шине\n    // start - начать поиск до нахождения первого устройства\n    // next - искать следующее устройство, если поиск был уже начат, используя режим start\n    \"mode\": \"start\",\n\n    // При указании значения `modbus` в сокет отправляется Modbus RTU сообщение, что используется при работе через прозрачные шлюзы Modbus RTU-over-TCP.\n    // Для работы по Modbus TCP укажите `modbus-tcp`.\n    // Необязательный параметр. Если не указан, используется \"modbus\"\n    \"protocol\": \"modbus-tcp\"\n}\n```\n\nПри успешном выполнении запроса","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwirenboard%2Fwb-mqtt-serial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwirenboard%2Fwb-mqtt-serial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwirenboard%2Fwb-mqtt-serial/lists"}