Wb-mqtt-mbgate

From Wiren Board
This is the approved revision of this page, as well as being the most recent.

Сервис wb-mqtt-mbgate: Modbus-slave для внешних SCADA и ПЛК

Описание

Сервис wb-mqtt-mbgate делает контроллер Wiren Board Modbus-slave-устройством. Внешняя система (SCADA, ПЛК, операторская панель, диспетчерский сервер) читает данные с контроллера и отправляет команды по Modbus TCP или Modbus RTU.

Шлюз привязывает выбранные MQTT-каналы контроллера к Modbus-регистрам. Источник канала может быть любой: драйвер wb-mqtt-serial, опрашивающий устройства по RS-485 и другим интерфейсам; правило wb-rules; внешний скрипт.

Шлюз не работает в режиме Modbus-мастера и не опрашивает внешние устройства — для этого используется wb-mqtt-serial.

Когда это нужно

Типичные сценарии:

  • SCADA с Modbus TCP. Контроллер выступает как slave, SCADA опрашивает регистры. Например, MasterSCADA, Inspector SCADA, IRidium Server.
  • ПЛК верхнего уровня по Modbus TCP или RTU — ОВЕН, Schneider Electric, Siemens.
  • Существующая сеть Modbus RTU, где диспетчерский пульт уже опрашивает приборы по RS-485.
  • Операторская панель (HMI), работающая только с Modbus.
  • Несколько slave-устройств в одном контроллере. Каналам назначаются разные unit-id, и внешней системе контроллер представляется как несколько приборов на одной шине или одном TCP-подключении. Удобно при замене устаревших устройств с сохранением старой карты регистров.

Когда не нужно

Если внешняя система поддерживает несколько протоколов, выбирайте подходящий способ интеграции.

Что поддерживает внешняя система Что использовать
MQTT Прямое подключение к MQTT
Modbus TCP или Modbus RTU Шлюз wb-mqtt-mbgate, эта статья
OPC UA OPC UA
МЭК 60870-5-104 Шлюз wb-mqtt-iec104

Если на контроллере работает SCADA-runtime, читающий MQTT напрямую (например, MasterSCADA 4D), промежуточное представление через Modbus не нужно.

Совместимость и требования

Шлюз wb-mqtt-mbgate входит в стандартную поставку прошивки Wiren Board и не требует отдельной установки. Контроллеры WB имеют два или более RS-485-порта — этого достаточно для одновременной работы шлюза в режиме RTU и драйвера wb-mqtt-serial на разных портах.

Внимание. Modbus TCP не аутентифицирует подключения: любой клиент с сетевым доступом к порту 502 может читать регистры, а при наличии привязок Coil или Holding Register на запись — управлять реле и менять уставки. Ограничивайте доступ к порту средствами сети: firewall, отдельный VLAN, разрешение подключений только с адреса SCADA. Логин и пароль из раздела Подключение к MQTT-брокеру защищают соединение с MQTT-брокером, а не Modbus-сторону.

Принцип работы

Шлюз связывает MQTT-топики контроллера с Modbus-регистрами, видимыми внешней системе. Каждый MQTT-канал вида /devices/<DEVICE>/controls/<CHANNEL> можно привязать к Modbus-регистру; в конфигураторе он отображается как <DEVICE>/<CHANNEL> (например, wb-msw-v4_21/Temperature).

Конфигуратор берёт каналы из MQTT-брокера. Выбираются нужные, задаются unit-id и адрес регистра — значения становятся доступны по Modbus.

Поток данных

Поток данных. Чтение — слева направо, запись — справа налево

На запись Modbus-регистра шлюз публикует команду в /on-топик канала, а реагирует на неё обработчик канала: драйвер устройства передаёт команду по шине, обработчик правила wb-rules или внешний скрипт обрабатывает её программно.

Для физических Modbus-устройств, привязанных через wb-mqtt-serial, поток выглядит следующим образом. Чтение направлено от устройства к внешней системе: устройство → драйвер → MQTT → wb-mqtt-mbgate → Modbus-регистр → SCADA. Запись — в обратную сторону: SCADA → Modbus-регистр → wb-mqtt-mbgate → MQTT-топик /on канала → драйвер → устройство.

Запись асинхронная. Получив Modbus-команду записи, шлюз публикует значение в соответствующий MQTT-топик и сразу возвращает SCADA штатный Modbus-ответ — не дожидаясь, пока команда достигнет устройства. Драйвер передаёт значение по физической шине и при успешной записи публикует это же значение в MQTT как новое состояние канала. Шлюз обновляет кэш регистра, и SCADA получает его при следующем Modbus-чтении. Фактическое состояние устройства драйвер сверяет уже в следующем цикле опроса.

Типы Modbus-регистров

Шлюз использует все четыре стандартных типа регистров.

Тип Доступ Ширина Подходящий MQTT-канал
Discrete Input чтение 1 бит дискретный, только чтение (например, состояние входа)
Coil чтение и запись 1 бит дискретный, чтение и запись (реле)
Input Register чтение 16+ бит числовой, только чтение (температура, напряжение)
Holding Register чтение и запись 16+ бит числовой, чтение и запись (уставка, яркость)

При добавлении канала конфигуратор предлагает подходящий тип по метаданным MQTT-канала; при необходимости тип меняется вручную. В read-only канал записи не пройдут даже через Coil/Holding Register: шлюз вернёт SCADA штатный Modbus-ответ, но обработчика записи на стороне MQTT-источника нет — состояние не изменится.

Шлюз поддерживает только функциональные коды чтения и записи стандартных регистров: 0x01, 0x02, 0x03, 0x04 (чтение), 0x05, 0x06, 0x0F, 0x10 (запись). Запрос с любым другим кодом функции (диагностика, работа с файлами, encapsulated transport) в текущей версии приводит к аварийному завершению сервиса — systemd перезапускает его примерно через 10 секунд. На стороне клиента используйте только перечисленные коды. Подробнее о функциональных кодах — Modbus.

Адресация и unit-id

Привязка канала к регистру задаётся двумя адресными параметрами: unit-id (1…255, в конфигураторе ID данных Modbus) — Modbus-адрес slave-устройства, и начальный адрес (0…65535) — позиция регистра внутри unit-id. Разным каналам можно назначать разные unit-id — внешней системе контроллер представляется как несколько slave-устройств на одном TCP-подключении или RTU-шине.

Один контроллер представляется как несколько slave-устройств. Все запросы проходят по одному TCP-подключению или RS-485-линии

Автоприсваивание адресов

По умолчанию unit-id и адрес регистра считаются автоматически — конфигуратор хеширует имя канала. Алгоритм детерминирован: одинаковые имена каналов на разных контроллерах дают одинаковые адреса, и конфигурацию можно копировать без правки. Unit-id 0, 1, 2 и 247…255 автоприсваивание не использует — они оставлены для ручного назначения под каналы, которые SCADA или ПЛК ждёт на конкретном slave-адресе.

Совместимость с клиентами на unit-id = 1

Часть Modbus TCP-клиентов игнорирует unit-id и всегда подставляет 1 (реже — 255). Автоприсваивание выбирает unit-id из диапазона 3…246; эти unit-id недоступны такому клиенту — назначьте нужным каналам unit-id = 1 вручную.

Нумерация адресов регистров

Адреса регистров в wb-mqtt-mbgate нумеруются с нуля. Часть SCADA и Modbus-клиентов отображает их со сдвигом +1: регистр 100 в файле конфигурации показывается во внешней системе как 101. Если чтение не работает, проверьте, как нумерует регистры ваш клиент.

Формат данных

Для дискретных регистров формат не настраивается — это всегда один бит. Для числовых регистров задаются тип данных (format — Signed/Unsigned integer, BCD, IEEE 754 float, Varchar), размер (size — в байтах для чисел и в регистрах для текста), множитель (scale) и порядок байт и слов (byteswap, wordswap).

Размер числовых значений

Один Modbus-регистр содержит 2 байта.

Тип значения Размер Регистров
16-битное целое 2 байта 1
32-битное целое 4 байта 2
64-битное целое 8 байт 4
float (одинарная точность) 4 байта 2
double (двойная точность) 8 байт 4
varchar задаётся в регистрах по заданной длине

Многорегистровые значения должны читаться целиком одним запросом. При размещении учитывайте, что они занимают несколько последовательных адресов регистров: float размером 4 байта с адреса 100 занимает регистры 100 и 101, следующее значение — не раньше 102.

Множитель

Параметр scale задаёт коэффициент пересчёта между MQTT и Modbus. Чтение: значение в Modbus = значение в MQTT × scale. Запись: значение в MQTT = значение в Modbus ÷ scale. Пример: температура 23.6 °C при scale = 10 передаётся как 236 (0x00EC); напряжение 220.3 В при scale = 10 — как 2203 (0x089B).

Внимание. При scale = 0 шлюз не выдаёт ошибку, но портит данные без какой-либо диагностики: на чтение в регистре всегда оказывается 0 независимо от значения в MQTT, на запись в MQTT-топик публикуется строка "inf". Если масштабирование не нужно, оставляйте scale = 1.

Порядок байт и слов

По умолчанию используется big-endian — стандарт Modbus. Два флага меняют порядок: Обратный порядок байт в словах (byteswap) переставляет байты внутри 16-битного регистра, Обратный порядок слов (wordswap) — 16-битные слова в составном значении. Для полного little-endian нужны оба флага сразу; по одному они дают промежуточные раскладки, которые тоже встречаются в реальных системах.

Для 16-битных значений (один регистр) wordswap не имеет эффекта — у одного регистра нет второго слова для перестановки. byteswap нужен только если устройство нестандартно возвращает байты внутри регистра в обратном порядке; большинство устройств следуют стандарту Modbus, и флаг остаётся выключенным. На 32- и 64-битных типах (включая float и double) актуальны оба флага.

Раскладка четырёхбайтового значения в двух регистрах при разных флагах. Байты обозначены A-D, A — старший, D — младший

Кэш и латентность чтения

Шлюз держит локальный кэш всех привязанных регистров и обновляет его на каждое MQTT-сообщение. Modbus-запрос на чтение возвращает значение из кэша на момент запроса — без обращения к брокеру.

Задержка чтения определяется частотой публикаций в MQTT, а не быстродействием Modbus: для датчика, публикующего раз в минуту, значение в кэше может отставать от реального до минуты — независимо от того, как часто его опрашивает SCADA.

При записи (Coil или Holding Register) шлюз обновляет кэш сразу же. Modbus-клиент, читающий регистр сразу после собственной записи, прочитает свою команду — даже если устройство ещё не подтвердило исполнение.

Быстрый старт: Modbus TCP

Контроллер передаёт в SCADA по Modbus TCP две величины с датчика WB-MSW v.4: температуру и влажность. SCADA подключается по IP контроллера и читает Input Registers.

Итоговая карта регистров:

MQTT-канал Тип регистра unit-id Адрес Формат Множитель
wb-msw-v4_21/Temperature Input Register 1 100 signed, 2 байта 10
wb-msw-v4_21/Humidity Input Register 1 101 unsigned, 2 байта 10

При температуре 23.6 °C SCADA прочитает в регистре 100 значение 236.

Настройка через веб-интерфейс.

Настройки шлюза в режиме Modbus TCP
  1. Откройте Настройки → Конфигурационные файлы → Шлюз MQTT - Modbus RTU/TCP slave.
  2. Выберите режим TCP. Оставьте значения по умолчанию: IP-адрес *, Порт 502.
  3. В разделе Соответствия каналов и регистров добавьте канал wb-msw-v4_21/Temperature со значениями: ID данных Modbus1, Начальный адрес100, Тип данныхЗнаковое целое, Размер в байтах2, Множитель10.
  4. Аналогично добавьте wb-msw-v4_21/Humidity: Начальный адрес101, Тип данныхБеззнаковое целое, остальные поля те же.
  5. Нажмите Сохранить. Сервис перезапустится автоматически.
Заполненная карта регистров: два канала привязаны как Input Registers

JSON-конфигурация (/etc/wb-mqtt-mbgate.conf):

{
  "debug": false,
  "modbus": { "host": "*", "port": 502 },
  "mqtt": { "host": "/var/run/mosquitto/mosquitto.sock", "port": 0 },
  "registers": {
    "discretes": [],
    "coils": [],
    "inputs": [
      { "enabled": true, "topic": "wb-msw-v4_21/Temperature",
        "unitId": 1, "address": 100,
        "format": "signed", "size": 2, "scale": 10,
        "byteswap": false, "wordswap": false },
      { "enabled": true, "topic": "wb-msw-v4_21/Humidity",
        "unitId": 1, "address": 101,
        "format": "unsigned", "size": 2, "scale": 10,
        "byteswap": false, "wordswap": false }
    ],
    "holdings": []
  }
}

Проверка. С компьютера в той же сети или с самого контроллера:

modbus_client --debug -mtcp -a1 -t0x04 -r100 -c1 -p502 192.168.1.50

Флаги: -mtcp — TCP-режим, -a1unit-id, -t0x04 — чтение Input Registers, -r100 — адрес регистра, -c1 — число регистров, -p502 — TCP-порт, последний аргумент — IP контроллера. Ожидаемый вывод — Data: 0x00ec, после деления на множитель — температура 23.6 °C.

Быстрый старт: Modbus RTU

Контроллер подключён к ПЛК по RS-485 и работает как Modbus-slave в режиме RTU с unit-id 10. ПЛК читает температуру с датчика WB-MSW v.4 и управляет реле K1 на модуле WB-MR6C v.3.

Карта регистров:

MQTT-канал Тип регистра unit-id Адрес Формат
wb-msw-v4_21/Temperature Input Register 10 100 signed, 2 байта, множитель 10
wb-mr6c_12/K1 Coil 10 0

Подготовка порта. Шлюз будет отвечать через /dev/ttyRS485-2. Этот порт нельзя одновременно использовать с драйвером wb-mqtt-serial — отключите его в Настройки → Конфигурационные файлы → Настройка драйвера serial-устройств.

Настройка через веб-интерфейс.

  1. Откройте Настройки → Конфигурационные файлы → Шлюз MQTT - Modbus RTU/TCP slave.
  2. Выберите режим RTU. Укажите Путь к устройству /dev/ttyRS485-2. Скорость, число бит, чётность, стоп-биты задайте в соответствии с настройками ПЛК (типичные значения: 9600, 8, N, 2).
  3. Добавьте канал wb-msw-v4_21/Temperature: ID данных Modbus10, Начальный адрес100. Остальные параметры — как в разделе Быстрый старт: Modbus TCP выше.
  4. Добавьте канал wb-mr6c_12/K1: ID данных Modbus10, Начальный адрес0. Канал автоматически попадает в раздел Coils.
  5. Нажмите Сохранить.

JSON-конфигурация:

{
  "debug": false,
  "modbus": { "path": "/dev/ttyRS485-2",
              "baud_rate": 9600, "parity": "N",
              "data_bits": 8, "stop_bits": 2 },
  "mqtt": { "host": "/var/run/mosquitto/mosquitto.sock", "port": 0 },
  "registers": {
    "discretes": [],
    "coils": [
      { "enabled": true, "topic": "wb-mr6c_12/K1",
        "unitId": 10, "address": 0 }
    ],
    "inputs": [
      { "enabled": true, "topic": "wb-msw-v4_21/Temperature",
        "unitId": 10, "address": 100,
        "format": "signed", "size": 2, "scale": 10,
        "byteswap": false, "wordswap": false }
    ],
    "holdings": []
  }
}

Проверка. Проверять RTU нужно с устройства, физически подключённого к той же RS-485-шине — внешнего ПЛК, компьютера с USB-RS-485-преобразователем или другого контроллера Wiren Board.

Чтение температуры:

modbus_client --debug -mrtu -b9600 -pnone -s2 /dev/ttyUSB0 -a10 -t0x04 -r100 -c1

Включение реле K1:

modbus_client --debug -mrtu -b9600 -pnone -s2 /dev/ttyUSB0 -a10 -t0x05 -r0 1

Параметры идентичны TCP-сценарию, кроме -mrtu (режим), -b9600 -pnone -s2 (параметры порта) и /dev/ttyUSB0 (USB-RS-485-преобразователь на стороне клиента; на контроллере Wiren Board это /dev/ttyRS485-1 или другой порт). -t0x05 — запись одиночного Coil, последний аргумент — значение (1 для включения). После записи реле физически срабатывает.

Справочник настроек

Шлюз настраивается через веб-интерфейс (раздел Настройки → Конфигурационные файлы → Шлюз MQTT - Modbus RTU/TCP slave) или прямым редактированием /etc/wb-mqtt-mbgate.conf. Оба способа работают с одним и тем же файлом.

Режим работы — Modbus TCP

Поле формы JSON-ключ Описание
IP-адрес host IP-адрес или имя хоста для приёма подключений. * — любой адрес контроллера.
Порт port TCP-порт. По умолчанию 502.

Режим работы — Modbus RTU

Поле формы JSON-ключ Описание
Путь к устройству path Serial-порт, например /dev/ttyRS485-2.
Скорость обмена baud_rate 110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200.
Контроль чётности parity N / E / O.
Число бит данных data_bits 5…8, обычно 8.
Стоп биты stop_bits 1 или 2.

Параметры RTU должны совпадать с настройками внешней системы. Порт не должен использоваться драйвером wb-mqtt-serial.

Подключение к MQTT-брокеру

Поле формы JSON-ключ Описание
Адрес host Адрес брокера. По умолчанию — локальный UNIX-сокет /var/run/mosquitto/mosquitto.sock.
Порт port Порт брокера. 0 для UNIX-сокета, 1883 для стандартного TCP.
Интервал ожидания ответа keepalive Интервал MQTT keep-alive в секундах. По умолчанию 60.
Включить аутентификацию по логину и паролю auth Использовать логин и пароль, если брокер их требует.
Логин username Имя пользователя при включённой аутентификации.
Пароль password Пароль.

В большинстве случаев эти параметры менять не нужно — шлюз работает с локальным брокером контроллера.

Привязка канала к регистру

Общие поля для всех типов регистров:

Поле формы JSON-ключ Описание
Включить enabled Включает привязку. Выключенные привязки игнорируются.
Канал MQTT topic Канал в формате <DEVICE>/<CHANNEL>.
ID данных Modbus unitId unit-id slave-устройства, 1…255.
Начальный адрес address Адрес регистра внутри unitId, 0…65535.

Дополнительно для Input Register и Holding Register:

Поле формы JSON-ключ Описание
Тип данных format signed, unsigned, bcd, float, varchar.
Размер в байтах (в регистрах для текста) size Размер значения.
Множитель scale Коэффициент пересчёта между MQTT и Modbus.
Обратный порядок байт в словах byteswap Меняет порядок байт внутри 16-битных регистров.
Обратный порядок слов wordswap Меняет порядок 16-битных слов в составном значении.

Подробнее о форматах и порядке байт — раздел Формат данных выше.

Параметры командной строки

Сервис запускается через systemd и параметры берёт из файла конфигурации. При ручном запуске:

wb-mqtt-mbgate [-d <level>] [-c <config>]
Флаг Назначение
-d 1 Отладочные сообщения шлюза.
-d 2 Отладочные сообщения MQTT-библиотеки.
-d 3 И то и другое.
-d -1, -d -2, -d -3 Подавление информационных сообщений (шлюза, MQTT, обоих).
-c <path> Альтернативный путь к файлу конфигурации. По умолчанию /etc/wb-mqtt-mbgate.conf.

Сценарии интеграции

Произвольная карта регистров

Если внешняя система ожидает конкретную карту регистров — например, по требованию интегратора — unit-id и адрес регистра задаются вручную. Автоприсваивание не перезаписывает явно заданные значения.

Пример из ТЗ:

Параметр unit-id Регистр Тип
Температура помещения 1 100 Input Register
Влажность помещения 1 101 Input Register
Состояние реле освещения 1 0 Coil
Уставка температуры 1 200 Holding Register

В конфигураторе для каждого канала открывается соответствующий тип, заполняются ID данных Modbus и Начальный адрес, остальные параметры — по форматам из ТЗ.

Несколько unit-id в одной карте

Удобно, когда SCADA ожидает несколько slave-устройств или контроллер заменяет несколько отдельных приборов с сохранением их unit-id и адресов регистров:

Группа unit-id Адреса
Датчики климата 10 100…199
Релейные модули 11 0…99
Уставки 12 200…299

См. раздел Адресация и unit-id выше.

Один unit-id и общая таблица

Если SCADA работает только с одним slave-устройством, всем каналам назначается один unit-id, а данные распределяются по диапазонам адресов регистров:

Диапазон Что хранится
0…99 Дискретные сигналы
100…199 Климат
200…299 Электросчётчики
300…399 Уставки

Диапазон адресов регистров одного unit-id — 0…65535. При размещении многорегистровых значений (32-битное целое, float) учитывайте, что они занимают несколько последовательных адресов регистров — оставляйте место под все регистры значения.

Виртуальное устройство в wb-rules

Если внешней системе нужно отдать не показания одного физического устройства, а вычисленное или агрегированное значение, его собирают в правиле wb-rules как виртуальное устройство и привязывают к Modbus-регистру так же, как любой физический канал.

Задача: SCADA читает один регистр, содержащий сумму активных мощностей с четырёх электросчётчиков meter-1meter-4, опрашиваемых драйвером wb-mqtt-serial по RS-485.

Правило в wb-rules (/etc/wb-rules/power-total.js):

defineVirtualDevice("power-total", {
    title: "Суммарная активная мощность",
    cells: {
        Sum: { type: "power", value: 0, readonly: true, units: "W" }
    }
});

defineRule("update-power-total", {
    whenChanged: ["meter-1/Pa", "meter-2/Pa", "meter-3/Pa", "meter-4/Pa"],
    then: function() {
        dev["power-total/Sum"] =
            (dev["meter-1/Pa"] || 0) +
            (dev["meter-2/Pa"] || 0) +
            (dev["meter-3/Pa"] || 0) +
            (dev["meter-4/Pa"] || 0);
    }
});

После сохранения правила в MQTT-брокере появляется новое устройство power-total с каналом Sum. Канал обновляется при каждом изменении любого из четырёх входных.

Привязка в wb-mqtt-mbgate:

MQTT-канал Тип регистра unit-id Адрес Формат
power-total/Sum Input Register 1 200 signed, 4 байта

SCADA читает регистр 200 на unit-id 1 и получает текущую сумму. Задержка совпадает с частотой публикаций в MQTT (см. раздел Кэш и латентность чтения) — регистр обновляется при каждом изменении любого из четырёх счётчиков.

Пример минимальный и не отслеживает достоверность входных каналов: если счётчик пропадёт со связи, в сумме продолжит учитываться его последнее значение, а сумма на стороне SCADA не получит признака ошибки. В ответственных задачах добавьте в правило контроль ошибки входных каналов.

Управление сервисом и диагностика

Команды управления

systemctl status wb-mqtt-mbgate     # состояние сервиса
systemctl restart wb-mqtt-mbgate    # перезапуск
journalctl -u wb-mqtt-mbgate -n 100 # последние 100 строк журнала
journalctl -u wb-mqtt-mbgate -f     # журнал в реальном времени

Сервис автоматически перезапускается при сбоях (рестарт через 10 секунд) и не перезапускается при ошибках конфигурации — конфигурацию нужно сначала исправить.

Сохранение конфигурации перезапускает сервис: на время перезапуска опрос прерывается, активные Modbus TCP-подключения разрываются и устанавливаются заново. Правьте карту регистров, когда кратковременный обрыв опроса со стороны SCADA допустим.

Коды выхода

Код Значение Поведение systemd
0 Штатное завершение success
1 Внутренняя ошибка или таймаут остановки автоматический перезапуск
2 Неверные аргументы командной строки не перезапускается
6 Невалидная конфигурация не перезапускается
7 Все каналы выключены (enabled: false) success — сервис в inactive (dead), а не failed

Код 7 — нормальное состояние, если конфигурация ещё не заполнена. Сервис будет автоматически запущен после сохранения конфигурации с включёнными каналами.

Ограничения и особенности эксплуатации

  • Нет режима master. Шлюз работает только как slave. Для опроса внешних Modbus-устройств — wb-mqtt-serial.
  • Один режим на инстанс. Шлюз работает либо в TCP, либо в RTU — одновременно нельзя.
  • RTU-порт занят эксклюзивно. Тот же порт нельзя одновременно использовать в wb-mqtt-serial.
  • Латентность чтения определяется MQTT — см. раздел Кэш и латентность чтения.
  • Modbus TCP без аутентификации. Доступ к порту 502 нужно ограничивать средствами сети — см. Совместимость и требования.
  • Неподдерживаемый код функции завершает сервис (диагностика, файлы, encapsulated transport) — см. Типы Modbus-регистров.
  • scale = 0 тихо портит данные — см. Множитель.

Проверка обмена через modbus_client

Что проверить Функция Пример
Чтение Coil 0x01 состояние реле
Чтение Discrete Input 0x02 состояние входа
Чтение Holding Register 0x03 уставка
Чтение Input Register 0x04 измерение
Запись одиночного Coil 0x05 включить реле
Запись одиночного Holding Register 0x06 задать уставку
Запись нескольких Coils 0x0F групповое управление реле
Запись нескольких Holding Registers 0x10 пакетная запись параметров

Полный синтаксис команд — Modbus-client.

Типичные проблемы

Внешняя система не получает значения. Проверить по порядку: запущен ли сервис (systemctl status wb-mqtt-mbgate); есть ли значение в MQTT (mosquitto_sub -v -t '/devices/+/controls/+'); включена ли привязка в конфигурации (enabled: true); правильно ли указаны unit-id и тип регистра; нет ли сдвига адреса регистра на 1 во внешней системе.

Чтение работает, но значение не соответствует ожидаемому. Проверить размер значения (число регистров, читаемых внешней системой), тип данных, множитель, флаги порядка байт и слов. Например, четырёхбайтовый float, прочитанный как один регистр, даст некорректный результат.

Запись в регистр проходит, но устройство не реагирует. Канал в MQTT должен быть доступен на запись (не помечен как read-only) и привязан к Coil (для дискретных) или Holding Register (для числовых). Проверить, появляется ли новое значение в MQTT после записи: mosquitto_sub -v -t '/devices/<DEVICE>/controls/<CHANNEL>'.

В режиме RTU нет ответа. Проверить: освобождён ли порт от wb-mqtt-serial; совпадают ли скорость, чётность, биты данных, стоп-биты с настройками внешней системы; правильно ли подключены линии A/B и есть ли терминатор; нет ли другого устройства с таким же unit-id на шине.

Как отличить ошибку адресации от проблемы связи. Если клиент получает Modbus-исключение Illegal Data Address — связь и unit-id в порядке, но запрошенный адрес регистра не привязан в карте. Если же клиент получает таймаут (в Modbus TCP — соединение установлено, но ответа на запрос нет), то к запрошенному unit-id не привязан ни один регистр: проверьте unit-id в карте, прежде чем искать физическую проблему с подключением устройств.

Сервис не запускается после ручного редактирования файла конфигурации. Проверить JSON: запятые, кавычки, имена полей, вложенность секций modbus, mqtt, registers. Журнал: journalctl -u wb-mqtt-mbgate -n 100. Код выхода 6 в журнале означает, что конфигурация не прошла валидацию по JSON-схеме.

Значение отличается в 10/100/1000 раз. Проверить Множитель. Если в MQTT температура 23.6, а внешняя система получает целое 236 — это ожидаемое поведение при scale = 10. Чтобы передавать значения без масштабирования, задайте scale = 1.

Часто задаваемые вопросы

Можно ли построить произвольную карту регистров? Да — см. раздел Произвольная карта регистров.

Можно ли использовать разные unit-id в одной карте? Да, разным каналам — разные. См. раздел Несколько unit-id в одной карте.

Можно ли использовать один unit-id для всех данных? Да — см. раздел Один unit-id и общая таблица.

Можно ли использовать весь диапазон адресов регистров 0…65535? Да. При размещении учитывайте, что многорегистровые значения занимают несколько последовательных адресов регистров.

Можно ли отдавать дробные значения через целые регистры? Да, через Множитель. Например, температура 23.6 °C при scale = 10 передаётся как целое 236.

Можно ли записывать значения из SCADA обратно в устройство? Да, если MQTT-канал открыт на запись. Для дискретных значений используется Coil, для числовых — Holding Register. Если канал помечен как read-only, шлюз опубликует команду в MQTT, но фактическое состояние не изменится: у read-only канала нет обработчика записи.

Как контроллер узнает, что связь со SCADA пропала? Сам шлюз потерю связи не отслеживает. Стандартный паттерн: завести в wb-rules виртуальное устройство с записываемым каналом, привязать его как Holding Register или Coil и настроить SCADA на периодическую запись в него. Правило wb-rules следит за обновлением этого канала и переводит систему в безопасное состояние, если значение не обновлялось дольше заданного времени.

Что выбрать: Input Register или Holding Register? Автоматически по метаданным MQTT-канала: только чтение — Input, чтение и запись — Holding. Тип можно изменить вручную.

Нужно ли использовать wb-mqtt-mbgate, если внешняя система умеет MQTT? Обычно нет — предпочтительнее подключиться к MQTT-брокеру контроллера напрямую. Шлюз нужен только тогда, когда внешняя система умеет работать исключительно по Modbus.

Можно ли через wb-mqtt-mbgate опрашивать внешние Modbus-устройства? Нет — wb-mqtt-mbgate работает только в slave-режиме; для master-режима используется wb-mqtt-serial.

Полезные ссылки

  • Modbus — протокол.
  • Modbus-client — командная утилита для проверки обмена.
  • MQTT — MQTT-брокер контроллера.
  • wb-mqtt-serial — драйвер опроса внешних устройств (master-режим).
  • OPC UA — альтернативный шлюз для трансляции MQTT в OPC UA.
  • MasterSCADA on Wiren Board — установка SCADA-runtime прямо на контроллер, без шлюза.
  • WB-MSW v.4, WB-MR6C v.3 — устройства, использованные в примерах статьи.