Подключение стороннего Modbus-устройства к контроллеру Wiren Board
Введение
Рекомендуем сперва поискать ваше устройств в Таблице поддерживаемых устройств — вдруг оно уже там есть. Если устройства в списке нет, но оно поддерживает протокол 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.
- Проверьте связь с устройством и правильность подключения:
- Остановите драйвер wb-mqtt-serial или иное ПО, которое опрашивает устройство.
- Подключитесь к устройству с помощью утилиты modbus_client и считайте данные из любого известного вам регистра.
Устройство Modbus TCP:
- Откройте документацию на устройство и найдите описание modbus-регистров и настроек подключения (адрес, порт).
- Так как утилита modbus_client содержит ошибку, из-за которой она не может работать по протоколу Modbus TCP, то подключите устройство к компьютеру через Ethernet.
- Попробуйте считать из устройства значение одного известного вам регистра.
Если вы смогли получить содержимое регистра — вы всё делаете верно и можете продолжать.
Создание шаблона
Рассказать драйверу wb-mqtt-serial, который в контроллере работает с Modbus-устройствами можно двумя способами:
- Добавить регистры устройства прямо в веб-интерфейсе контроллера по инструкции. Этот способ удобен для быстрой проверки работы.
- Создать шаблон, который описывает регистры устройства, их тип и другие параметры. Этот способ удобен для масштабирования: просто копируете шаблон на другой контроллер и в нём появляется поддержка вашего устройства.
Здесь мы рассмотрим создание простого шаблона. Упрощённо шаблон устройства выглядит так:
{
"device_type": "my-relay", // тип устройства — уникальный идентификатор
"title": "My Relay", // отображаемое название
"group": "g-relay", // группа, в которой будет отображаться шаблон. Список групп в документации
"device": {
"name": "MY-RELAY", // имя устройства, используется в MQTT
"id": "my-relay",
"groups": [ ], // группы параметров и каналов
"channels": [ ], // каналы, доступно в скриптах и на вкладке Устройства
"parameters": [ ], // параметры, можно менять в настройках устройства
"translations": { } // переводы
}
}
Полное описание смотрите в документации драйвера wb-mqtt-serial.
Допустим, у нас есть одноканальное Modbus-реле, у которого таблица регистров, показанная ниже.
Адрес | Тип | Название | Назначение |
---|---|---|---|
0 | Discrete Input | Input 1 | Состояние входа устройства |
1 | Input Register | Input 1 Counter | Значение счётчика входов |
3 | Coil | Relay 1 | Состояние выхода и управление им |
10 | Holding | Input Mode | Выбор режима взаимодействия входов с выходами |
В таком случае шаблон будет выглядеть так:
{
"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": 1,
"type": "switch",
"group": "channels"
},
{
"name": "Input 1 Counter",
"reg_type": "input",
"address": 2,
"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": "Режим входа"
}
}
}
}
Загрузка шаблона на контроллер
Когда шаблон готов, его надо загрузить на контроллер:
- Сохраните шаблон в файл, например,
my-relay.json
и загрузите его на контроллер в папку/etc/wb-mqtt-serial.conf.d/templates
по инструкции. - Проверьте шаблон на синтаксические ошибки командой:
# 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
- в примере в шаблоне my-best-template.json в строке 12, символ 5 ожидается
,
or}
, а находится что-то другое.
- Если с шаблоном всё в порядке, то перейдите в настройки драйвера и выберите ваш шаблон — инструкция.
Отклонения от стандарта и что с ними делать
Оптимизация запросов драйвером
Стандартом Modbus RTU предусмотрен обязательный интервал тишины в 3.5 символа между фреймами данных (под символом подразумевается посылка, состоящая из стартового бита, битов данных, бита четности и стоп-битов).
Для ускорения опроса устройств Wiren Board мы соблюдаем этот интервал только перед первым запросом к следующему в цикле опроса устройству (параметр frame_timeout_ms в шаблонах устройств).
Поэтому, чтобы соответствовать требованиям протокола Modbus-RTU, нужно для сторонних устройств задавать параметр guard_interval_us. Этот параметр задает задержку перед записью каждого запроса в порт.
Нужное значение рассчитывается по формуле:
guard_interval_us = (3.5*11*106)/(скорость в бит/с).
Например, для скорости 9600 бит/с guard_interval_us = (3.5*11*106)/9600 = 4000 мкс
. При проблемах с подключением стороннего устройства для теста это значение можно увеличить (например до 100000 мкс), так как сторонние устройства иногда работают не совсем корректно.
Разные регистры для чтения состояния и управления
Иногда в сторонних устройствах встречаются особенности:
- В некоторые регистры можно только писать информацию, но нельзя считывать.
- Один и тот же параметр может читаться по одному адресу, а записываться по другому.
Для того, чтобы драйвер работал с такими устройствами без ошибок, мы добавили параметр write_address.
Писать можно, читать нельзя
Допустим, в нашем устройстве есть такой регистр только для записи.
Адрес | Тип | Название | Назначение |
---|---|---|---|
20 | Coil | Relay 1 Switch | Управление выходом. Только для записи. |
В этом случае канал надо описывать так:
{
"name": "Relay 1 Switch",
"write_address": "20",
"reg_type": "coil",
"type": "pushbutton",
"format": "u16",
"group": "channels",
"on_value": 1 // определяет, что записывать в регистр при нажатии
}
Писать в один регистр, читать из другого
Допустим, в нашем устройстве команда записывается в один регистр, а читается из другого. Этот метод можно использовать только, если у обоих регистров один тип. Если типы разные — создавайте два разных контрола: на чтение и на запись.
Адрес | Тип | Название | Назначение |
---|---|---|---|
20 | Coil | Relay 1 Switch | Управление выходом. Только для записи. |
21 | Coil | Relay 1 State | Состояние выхода. Только для чтения. |
В этом случае канал надо описывать так — у нас будет один контрол, а при работе с устройством, записываться будет один регистр, а читаться другой:
{
"name": "Relay 1",
"write_address": "20", // адрес, куда мы записываем команды
"address": "21", // адрес, по которому мы читаем состояние
"reg_type": "coil",
"type": "switch",
"format": "u16",
"group": "channels"
}
Произвольные значения в регистрах с бинарной логикой
Иногда бывает так, что по смыслу регистр должен представляться в веб-интерфейсе переключателем ВКЛ/ВЫКЛ, но допустимые значения у него не 1/0.
В этом случае вы описываете обычный канал с типом switch и указываете значения on_value
и off_value
, например:
{
"name": "Status",
"reg_type": "holding",
"address": "0",
"type": "switch",
"format": "u16",
"on_value": "0x00a5", // ВКЛ
"off_value": "0x005a" // ВЫКЛ
}
Теперь драйвер будет автоматически при чтении конвертировать указанные значения в положение переключателя, а при изменении положения переключателя — записывать указанные значения.