1
правка
(Создал страницу) |
(адреса в примере и коде json-файла отличаются) |
||
(не показано 68 промежуточных версий 4 участников) | |||
Строка 1: | Строка 1: | ||
{{DISPLAYTITLE: Как | {{DISPLAYTITLE: Как писать шаблоны для сторонних Modbus-устройств}} | ||
== Введение == | |||
Рекомендуем сперва поискать ваше устройств в [[Supported_devices | Таблице поддерживаемых устройств]] — вдруг оно уже там есть. Если устройства в списке нет, но оно поддерживает протокол [[Modbus|Modbus]], то его можно подключить к контроллеру Wiren Board. | |||
Существует два вида протокола Modbus: | |||
# Modbus RTU — устройства соединяются по шине RS-485. | |||
# Modbus TCP — устройства соединяются по локальной сети через Wi-Fi или Ethernet. | |||
Независимо от вида протокола, алгоритм добавления поддержки устройства в наш контроллер будет такой: | |||
# Ищете документацию на ваше устройство с описанием его modbus-регистров. | |||
# Составляете шаблон для нашего драйвера [[wb-mqtt-serial]]. | |||
# Копируете созданный шаблон на контроллер в папку | |||
# Выбираете в веб-интерфейсе контроллера свой шаблон и указываете адрес. | |||
Некоторые производители Modbus-устройств не придерживаются стандартов протокола, что может сказаться на работе всей шины. Поэтому рекомендуем проверять работу новых устройств на отдельной шине и только после того, как добьётесь стабильной работы, подключать к ней другие Modbus-устройства. | |||
== Подготовка == | |||
Устройство с Modbus RTU: | |||
# Откройте документацию на устройство и найдите описание modbus-регистров и параметров подключения (Baud rate, Data bits, Parity, Stop bits, Slave ID). | |||
# Подключите устройство к контроллеру по [[RS-485 | шине RS-485]]. | |||
# Проверьте связь с устройством и правильность подключения: | |||
#* {{Wbincludes:Stop wb-mqtt-serial}} | |||
#* Подключитесь к устройству с помощью утилиты [[modbus_client]] и считайте данные из любого известного вам регистра. | |||
# | Устройство Modbus TCP: | ||
# | # Откройте документацию на устройство и найдите описание modbus-регистров и настроек подключения (адрес, порт). | ||
# Утилита ''modbus_client'' в релизах до 2304 содержит ошибку, из-за которой она не может работать по протоколу Modbus TCP. Если успользуете устаревший релиз, то подключите устройство к компьютеру через Ethernet. | |||
# Попробуйте [[Working_with_WB_devices_without_a_controller |считать из устройства]] значение одного известного вам регистра. | |||
Если вы смогли получить содержимое регистра — вы всё делаете верно и можете продолжать. | |||
== | == Создание шаблона == | ||
Рассказать драйверу wb-mqtt-serial, который в контроллере работает с Modbus-устройствами можно двумя способами: | |||
# [[RS-485:Configuration_via_Web_Interface#without-template|Добавить регистры]] устройства прямо в веб-интерфейсе контроллера. Этот способ удобен для быстрой проверки работы. | |||
# Создать шаблон, который описывает регистры устройства, их тип и другие параметры. Этот способ удобен для масштабирования: просто копируете шаблон на другой контроллер и в нём появляется поддержка вашего устройства. | |||
https:// | Здесь мы рассмотрим создание простого шаблона. Упрощённо шаблон устройства выглядит так: | ||
<syntaxhighlight lang="json"> | |||
{ | |||
"device_type": "my-relay", // тип устройства — уникальный идентификатор | |||
"title": "My Relay", // отображаемое название | |||
"group": "g-relay", // группа, в которой будет отображаться шаблон. Список групп в документации | |||
"device": { | |||
"name": "MY-RELAY", // имя устройства, используется в MQTT | |||
"id": "my-relay", | |||
"groups": [ ], // группы параметров и каналов | |||
"channels": [ ], // каналы, доступно в скриптах и на вкладке Устройства | |||
"parameters": [ ], // параметры, можно менять в настройках устройства | |||
"translations": { } // переводы | |||
} | |||
} | |||
</syntaxhighlight> | |||
'''Полное описание смотрите в [https://github.com/wirenboard/wb-mqtt-serial документации драйвера wb-mqtt-serial на Github].''' | |||
Допустим, у нас есть одноканальное Modbus-реле, у которого таблица регистров, показанная ниже. | |||
{| border="1" class="wikitable" | |||
! Адрес || Тип || Название || Назначение | |||
|- | |||
| 0 || Discrete Input || Input 1 || Состояние входа устройства | |||
|- | |||
| 1 || Input Register || Input 1 Counter || Значение счётчика входов | |||
|- | |||
| 3 || Coil || Relay 1 || Состояние выхода и управление им | |||
|- | |||
| 10 || Holding || Input Mode || Выбор режима взаимодействия входов с выходами | |||
|- | |||
|} | |||
В таком случае шаблон будет выглядеть так: | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"device_type": "my-relay", | |||
"title": "My Relay", | |||
"group": "g-relay", | |||
"device": { | |||
"name": "MY-RELAY", | |||
"id": "my-relay", | |||
"groups": [ | |||
{ | |||
"title": "Channels", | |||
"id": "channels", | |||
"order": 0 | |||
}, | |||
{ | |||
"title": "Settings", | |||
"id": "settings", | |||
"order": 1 | |||
} | |||
], | |||
"channels": [ | |||
{ | |||
"name": "Input 1", | |||
"reg_type": "discrete", | |||
"address": 0, | |||
"type": "switch", | |||
"group": "channels" | |||
}, | |||
{ | |||
"name": "Input 1 Counter", | |||
"reg_type": "input", | |||
"address": 1, | |||
"type": "value", | |||
"group": "channels" | |||
}, | |||
{ | |||
"name": "Relay 1", | |||
"reg_type": "coil", | |||
"address": 3, | |||
"type": "switch", | |||
"group": "channels" | |||
} | |||
], | |||
"parameters": [ | |||
{ | |||
"id": "input1", | |||
"title": "Input Mode", | |||
"reg_type": "input", | |||
"address": 10, | |||
"format": "s8", | |||
"enum": [ | |||
1, | |||
2 | |||
], | |||
"enum_titles": [ | |||
"Switch Relay", | |||
"Not used" | |||
], | |||
"default": 1 | |||
} | |||
], | |||
"translations": { | |||
"ru": { | |||
"Channels": "Каналы", | |||
"Settings": "Настройки", | |||
"Input 1": "Вход 1", | |||
"Input 1 Counter": "Вход 1 счетчик", | |||
"Relay 1": "Реле 1", | |||
"Input Mode": "Режим входа" | |||
} | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
https://support.wirenboard.com/t/wb-mr6hv-i-perestalo-otvechat-po-modbus/8286/18 | == Загрузка шаблона на контроллер == | ||
Когда шаблон готов, его надо загрузить на контроллер: | |||
# Сохраните шаблон в файл, например, <code>my-relay.json</code> и загрузите его на контроллер в папку <code>/etc/wb-mqtt-serial.conf.d/templates</code> по [[View_controller_files_from_your_computer|инструкции]]. | |||
# Проверьте шаблон на синтаксические ошибки командой: | |||
#:<syntaxhighlight lang="bash"> | |||
# wb-mqtt-serial -g | |||
<3>ERROR: [serial config] Failed to parse /etc/wb-mqtt-serial.conf.d/templates/my-best-template.json | |||
Failed to parse JSON /etc/wb-mqtt-serial.conf.d/templates/my-best-template.json:* Line 12, Column 5 | |||
Missing ',' or '}' in object declaration | |||
</syntaxhighlight> | |||
#: в примере в шаблоне ''my-best-template.json'' в строке 12, символ 5 ожидается <code>,</code> or <code>}</code>, а находится что-то другое. | |||
# Если с шаблоном всё в порядке, то перейдите в настройки драйвера и [[RS-485:Configuration_via_Web_Interface|выберите ваш шаблон]]. | |||
== Отклонения от стандарта и что с ними делать == | |||
=== Оптимизация запросов драйвером === | |||
Стандартом Modbus RTU предусмотрен обязательный интервал тишины в 3.5 символа между фреймами данных (под символом подразумевается посылка, состоящая из стартового бита, битов данных, бита четности и стоп-битов). | |||
Для ускорения опроса устройств Wiren Board мы соблюдаем этот интервал только перед первым запросом к следующему в цикле опроса устройству (параметр ''frame_timeout_ms'' в шаблонах устройств). | |||
Поэтому, чтобы соответствовать требованиям протокола Modbus-RTU, нужно для сторонних устройств задавать параметр ''guard_interval_us''. Этот параметр задает задержку перед записью каждого запроса в порт. | |||
Нужное значение рассчитывается по формуле: | |||
<syntaxhighlight lang="bash"> | |||
guard_interval_us = (3.5*11*10^6)/(скорость в бит/с). | |||
</syntaxhighlight> | |||
Например, для скорости 9600 бит/с <code>guard_interval_us = (3.5*11*10^6)/9600 = 4000 мкс</code>. При проблемах с подключением стороннего устройства для теста это значение можно увеличить (например до 100000 мкс), так как сторонние устройства иногда работают не совсем корректно. | |||
=== Если каналы устройства периодически мигают красным === | |||
Со сторонними устройствами довольно частая ситуация, когда вы сделали шаблон, данные идут, но в логах сыпятся ошибки, а каналы устройства в веб-интерфейсе контроллера окрашивают красным. | |||
Основная причина этому — устройство слишком медленно обрабатывает запросы нашего драйвера. Для начала мы рекомендуем подключить такие устройства на отдельную шину, чтобы они не тормозили работу нормальных устройств, а потом использовать рекомендации ниже. | |||
Чтобы починить, попробуйте увеличить параметр <code>guard_interval_us</code> вплоть до тысяч единиц, например, 5000. Если работа стабилизируется, потихоньку уменьшайте это значение до тех пор, пока ошибки не появятся вновь. Предыдущее значение, когда всё работало хорошо и будет вашим значением в шаблоне. | |||
Ещё есть параметр <code>response_timeout_ms</code> — это максимальное время ответа устройства в миллисекундах, по умолчанию 500 мс. С ним тоже можно аккуратно поэкспериментировать. | |||
Не выставляйте без нужды огромных значений в этих параметрах — это замедлит опрос устройств на порту, куда подключено проблемное устройство. Подробнее о том, как работают эти параметры, смотрите в [https://github.com/wirenboard/wb-mqtt-serial#%D0%94%D0%B8%D0%B0%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D1%82%D0%B0%D0%B9%D0%BC%D0%B0%D1%83%D1%82%D0%BE%D0%B2-%D1%86%D0%B8%D0%BA%D0%BB%D0%B0-%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D0%B0 Диаграмме таймаутов цикла опроса]. | |||
Оба параметра пишутся в секцию device шаблона: | |||
<syntaxhighlight lang="json"> | |||
"device": { | |||
"name": "BAC-6000ELNW", | |||
"id": "bac-6000elnw", | |||
"response_timeout_ms": 100, | |||
"guard_interval_us": 5000, | |||
... | |||
} | |||
</syntaxhighlight> | |||
=== Разные регистры для чтения состояния и управления === | |||
Иногда в сторонних устройствах встречаются особенности: | |||
# В некоторые регистры можно только писать информацию, но нельзя считывать. | |||
# Один и тот же параметр может читаться по одному адресу, а записываться по другому. | |||
Для того, чтобы драйвер работал с такими устройствами без ошибок, мы добавили параметр '''write_address'''. | |||
==== Писать можно, читать нельзя ==== | |||
Допустим, в нашем устройстве есть такой регистр только для записи. | |||
{| border="1" class="wikitable" | |||
! Адрес || Тип || Название || Назначение | |||
|- | |||
| 20 || Coil || Relay 1 Switch || Управление выходом. Только для записи. | |||
|- | |||
|} | |||
В этом случае канал надо описывать так: | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"name": "Relay 1 Switch", | |||
"write_address": "20", | |||
"reg_type": "coil", | |||
"type": "pushbutton", | |||
"format": "u16", | |||
"group": "channels", | |||
"on_value": 1 // определяет, что записывать в регистр при нажатии | |||
} | |||
</syntaxhighlight> | |||
==== Писать в один регистр, читать из другого==== | |||
Допустим, в нашем устройстве команда записывается в один регистр, а читается из другого. Этот метод можно использовать только, если у обоих регистров один тип. Если типы разные — создавайте два разных канала: на чтение и на запись. | |||
{| border="1" class="wikitable" | |||
! Адрес || Тип || Название || Назначение | |||
|- | |||
| 20 || Coil || Relay 1 Switch|| Управление выходом. Только для записи. | |||
|- | |||
| 21 || Coil || Relay 1 State|| Состояние выхода. Только для чтения. | |||
|- | |||
|} | |||
В этом случае канал надо описывать так: у нас будет один параметр в контроллере, но при обмене данными запись будет производиться один регистр, а чтение — из другого: | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"name": "Relay 1", | |||
"write_address": "20", // адрес, куда мы записываем команды | |||
"address": "21", // адрес, по которому мы читаем состояние | |||
"reg_type": "coil", | |||
"type": "switch", | |||
"format": "u16", | |||
"group": "channels" | |||
} | |||
</syntaxhighlight> | |||
=== Произвольные значения в регистрах с бинарной логикой === | |||
Иногда бывает так, что по смыслу регистр должен представляться в веб-интерфейсе переключателем ВКЛ/ВЫКЛ, но допустимые значения у него не 1/0. | |||
В этом случае вы описываете обычный канал с типом '''switch''' и указываете значения <code>on_value</code> и <code>off_value</code>, например: | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"name": "Status", | |||
"reg_type": "holding", | |||
"address": "0", | |||
"type": "switch", | |||
"format": "u16", | |||
"on_value": "0x00a5", // ВКЛ | |||
"off_value": "0x005a" // ВЫКЛ | |||
} | |||
</syntaxhighlight> | |||
Теперь драйвер будет автоматически при чтении конвертировать указанные значения в положение переключателя, а при изменении положения переключателя — записывать указанные значения. | |||
== Полезные ссылки == | |||
* [[Modbus|Описание протокола Modbus]] | |||
* [[RS-485|Описание шины RS-485]] | |||
* [https://github.com/wirenboard/wb-mqtt-serial Документация wb-mqtt-serial] | |||
* [https://github.com/wirenboard/conventions/blob/main/README.md Описание соглашений MQTT] | |||
* [http://json.parser.online.fr Онлайн проверка JSON] | |||
* Темы на портале: | |||
** [https://support.wirenboard.com/t/oshibki-pri-chtenii-bolid-s2000-pp/8080 Ошибки при чтении Bolid С2000-ПП] | |||
** [https://support.wirenboard.com/t/request-timed-out/8344/17 Request timed out] | |||
** [https://support.wirenboard.com/t/oshibki-pri-chtenii-modbus-ustrojstv/8253 Ошибки при чтении Modbus устройств] | |||
** [https://support.wirenboard.com/t/wb-mr6hv-i-perestalo-otvechat-po-modbus/8286/18 WB-MR6HV/I перестало отвечать по Modbus] | |||
** [https://support.wirenboard.com/t/konvertor-modbas-dlya-vnutrennego-bloka-kondiczionera-uty-vmsx/9252 Подключение конвертера Modbus] |
правка