Modbus
Настройка Modbus RTU/TCP, карта адресов и управление ресурсами контроллера
Контроллер поддерживает Modbus RTU по RS-485 и Modbus TCP по Ethernet. В режиме ведомого устройства (Slave) внешняя программа может читать состояние контроллера и значения ресурсов, изменять выходы и пользовательские регистры, запускать и останавливать сохранённые сценарии.
В режиме ведущего устройства (Master) опрос оборудования по Modbus RTU выполняется из сценариев MicroPython или C. API мастера описан в документации модуля dev для MicroPython и C-сценариев. Далее описан режим Slave.
Настройка Slave
В разделе «Modbus» настроек контроллера задайте параметры:
| Параметр | Назначение |
|---|---|
| Использовать TCP | Принимать Modbus TCP на порту 502. |
| Использовать RTU | Принимать Modbus RTU по RS-485. |
| Адрес | Общий адрес для RTU и TCP, 1–248. В TCP-клиенте укажите его как Unit ID / Slave ID. |
| RTU битрейт | 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600 или 115200 бит/с. |
| RTU чётность | none — 8 бит данных, без чётности, 2 стоп-бита (8N2); even — 8E1; odd — 8O1. |
| RTU задержка, мс | Задержка перед ответом: 0–100 мс. Учитывайте её при выборе таймаута клиента. |
RTU и TCP включаются независимо и могут работать одновременно. Изменения применяются без перезагрузки контроллера. Параметры последовательного порта клиента должны совпадать с настройками RTU.
Modbus TCP обслуживает одно соединение одновременно. Перед запуском другого клиента закройте соединение в предыдущей программе. Если TCP-соединение устанавливается, но ответы не приходят, проверьте, не подключена ли другая утилита опроса.
Modbus не использует пароль веб-интерфейса и не шифрует обмен. Доступ к TCP-порту и линии RS-485 означает возможность выполнять разрешённые протоколом команды, включая управление сценариями. Подключайте их к доверенной сети и оборудованию.
Доступ к ресурсам
Для Do, Di, Ao, Ai и Reg используется настройка внешнего доступа, общая для Modbus и SNMP. Она независима от доступа через веб-интерфейс. В сценариях ей соответствует второй символ параметра access.
| Внешний доступ | Результат |
|---|---|
0 — нет доступа | Чтение и запись запрещены. |
1 — чтение | Можно читать значение. |
2 — чтение и изменение | Можно читать и записывать Do, Ao и Reg. Di и Ai остаются только для чтения. |
Неиспользуемые адреса карты не являются дополнительными каналами. Запрос к несуществующему, скрытому или недоступному для выбранной операции ресурсу возвращает ошибку Modbus. Для Reg также учитываются настроенное количество, тип и ограничения значения.
Поддерживаемые функции
Все значения расположены в области Holding Registers. Один Modbus-регистр содержит 16 бит.
| Код функции | Назначение | Количество 16-битных слов |
|---|---|---|
| 03 / 0x03 — Read Holding Registers | Чтение карты адресов ниже. | 1–125 |
| 06 / 0x06 — Write Single Register | Запись одного Do, Ao или регистра управления сценарием. Для пользовательских Reg запрещена. | 1 |
| 16 / 0x10 — Write Multiple Registers | Запись нескольких слов, в том числе полных 32-битных Reg. | 1–123 |
Здесь 16 — десятичный код функции, которому соответствует 0x10. FC06 записывает одно 16-битное слово; FC16 может передать несколько слов в одном запросе.
Чтение Input Registers (FC04), Coils и Discrete Inputs не поддерживается. Для Di также используйте FC03 по адресам из общей карты.
Весь запрошенный диапазон должен быть доступен. Например, чтение от адреса 0 количеством 32 слова завершится ошибкой: адрес 15 и адреса 24–31 зарезервированы. Системные данные можно читать отдельными блоками 0–14 и 16–23.
Карта адресов
В таблицах указаны адреса в запросе, начиная с нуля. Если программа использует обозначения 40001, 40002 и т. д., то 40001 соответствует адресу 0. Проверьте, какой формат ожидает ваш клиент.
R — чтение; RW — чтение и запись с учётом прав ресурса. Адреса обозначают десятичные числа.
Состояние и управление сценарием
| Адрес | Доступ | Значение |
|---|---|---|
| 0 | R | Числовой код модели контроллера. |
| 1 | R | Версия прошивки, младшие 16 бит. |
| 2 | R | Время с запуска контроллера в секундах, младшее слово uint32. |
| 3 | R | То же, старшее слово. |
| 4 | R | Подключение к серверу: 0 — неизвестно; 1 — отключено; 2 — подключение; 3 — подключено; 4 — ошибка. |
| 5 | R | Битовая маска системных аварий, см. ниже. |
| 6 | R | Битовая маска пользовательских аварий, биты 0–15. |
| 7 | R | Битовая маска пользовательских аварий, биты 16–31. |
| 8 | RW | При чтении — код текущего/последнего сценария. При записи — выбор кода сохранённого сценария для следующей команды запуска. Допустимо 0–65534; код 65535 запрещён. |
| 9 | RW | При чтении: 0 — сценарий ещё не запускался; 1 — работает; 2 — остановлен. При записи: 0 — остановить, 1 — запустить сценарий, выбранный записью в адрес 8. Запись 2 и других значений запрещена. |
| 10 | R | Тип сценария: 0 — нет; 1 — MicroPython; 2 — C; 3 — двоичный C-сценарий. |
| 11 | R | Причина запуска, см. таблицу ниже. |
| 12 | R | Причина остановки, см. таблицу ниже. |
| 13 | R | Время работы сценария в секундах, младшее слово uint32. |
| 14 | R | То же, старшее слово. |
| 15 | — | Резерв, чтение и запись запрещены. |
| 16 | R | Текущий IPv4-адрес локального Ethernet-интерфейса: первые два октета, (a << 8) | b. |
| 17 | R | Тот же IPv4-адрес: последние два октета, (c << 8) | d. |
| 18–23 | R | UID процессора: 12 байт, по два байта в регистр, первый байт пары — старший. |
| 24–31 | — | Резерв, чтение и запись запрещены. |
Выбранный записью в адрес 8 код хранится до следующего выбора или перезапуска контроллера и является общим для TCP и RTU. Само чтение адреса 8 возвращает код сценария, а не отложенный выбор. Перед каждым запуском записывайте нужный код явно.
Для запуска сценария с кодом 301 выполните FC06: адрес 8, значение 301; затем FC06: адрес 9, значение 1. Можно передать оба слова одним FC16: начало 8, количество 2, слова [301, 1]. Для остановки запишите FC06: адрес 9, значение 0. Подтверждение команды запуска не означает завершение сценария; отслеживайте состояние по адресу 9. Если сценарий не найден или команду выполнить не удалось, возвращается exception 04.
Биты системных аварий по адресу 5:
| Бит | Авария |
|---|---|
| 0 | HardFault в прошивке контроллера. |
| 1 | HardFault в пользовательском сценарии. |
| 2 | Срабатывание watchdog. |
| 3 | Переполнение стека. |
| 4 | Ошибка выполнения пользовательского сценария. |
| 5 | Автозапуск сценария отключён из-за нестабильной работы контроллера. |
В случае критических ошибок контроллер перезапускается посредством watchdog с последующей активацией системной аварии. Чтение аварий по Modbus не сбрасывает их.
Адреса 11 и 12 используют одинаковые коды причин:
| Код | Причина |
|---|---|
| 0 | Не задана. |
| 1 | Ошибка. |
| 2 | Автоматическое действие. |
| 3 | Действие пользователя. |
| 4 | Штатное завершение сценария. |
| 5 | Алгоритм. |
| 6 | Расписание. |
| 7 | Веб-интерфейс. |
| 8 | Внешний доступ, в том числе Modbus. |
| 9 | Пульт управления. |
Текущий IP-адрес и UID
Регистры 16–23 доступны в актуальной прошивке X0 и X1 через RTU и TCP. Они предназначены только для чтения: попытка записи FC06 или FC16 возвращает exception 02 — Illegal Data Address. В старой прошивке эти адреса также могут возвращать exception 02 при чтении.
Чтобы узнать IP-адрес, отправьте FC03, начало 16, количество 2. Возвращается фактический адрес локального Ethernet-интерфейса, в том числе полученный по DHCP. До назначения адреса возвращается 0.0.0.0; в этом случае повторите чтение после ожидания DHCP.
Слова передаются по порядку октетов IPv4, внутри слова первым идёт старший байт. Например:
Адрес 16: слово 0xC0A8, байты C0 A8 → октеты 192.168
Адрес 17: слово 0x326C, байты 32 6C → октеты 50.108
IP-адрес: 192.168.50.108
При чтении обоих слов одним запросом используется один снимок IP-адреса, поэтому изменение адреса DHCP не смешивает две его половины. В клиенте с обозначениями Holding Registers от 40001 это регистры 40017 и 40018. Узнав IP через RTU, можно обратиться к HTTP API контроллера; проверка GET /ping подтверждает доступность HTTP.
Для чтения UID отправьте FC03, начало 18, количество 6. Для строкового представления объедините шесть слов по порядку, записывая каждое четырьмя шестнадцатеричными цифрами с ведущими нулями:
Слова: 0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xAABB
UID: 00112233445566778899aabb
Порядок байтов совпадает с полем idCpu в ответе GET /json?static. Это позволяет проверить, что Modbus и HTTP отвечают от одного контроллера. IP и UID можно прочитать вместе: FC03, начало 16, количество 8.
Входы, выходы и пользовательские Reg
| Адрес | Доступ | Ресурс и формат |
|---|---|---|
| 32–63 | RW | Do(n), адрес 32 + n: 0 — выключен, 1 — включён. |
| 64–95 | R | Di(n), адрес 64 + n: 0 или 1. |
| 96–127 | RW | Ao(n), адрес 96 + n: целое 0–1000, сотые доли вольта; 1000 = 10,00 В. |
| 128–159 | R | Ai(n), адрес 128 + n: 16-битное значение, формат зависит от режима и канала, см. ниже. |
| 160–223 | RW | Reg(n): младшее слово по адресу 160 + 2*n, старшее — 161 + 2*n. Максимум 32 пользовательских Reg. |
Диапазоны зарезервированы под карту, но фактическое число физических каналов меньше: Do — 3 у X0 и 5 у X1; Di — 5, Ao — 3, Ai — 3 у обеих моделей. Например, у X0 адреса Do 35–63 недоступны. Нумерация каналов начинается с нуля. Количество пользовательских Reg задаётся в настройках: при десяти Reg доступны адреса 160–179.
Ai: интерпретируйте слово как знаковое 16-битное целое. В обработанном режиме температурные каналы возвращают десятые доли °C: -125 означает −12,5 °C, на линии это слово 0xFF83. Значение 32767 / 0x7FFF означает ошибку измерения температуры. На X0 все три входа температурные. На X1 входы 0 и 1 температурные, вход 2 в обработанном режиме возвращает напряжение в сотых долях вольта. В режиме raw передаётся исходный отсчёт АЦП без пересчёта в температуру или напряжение.
Формат и запись 32-битных Reg
Reg передаётся как исходное 32-битное значение настроенного типа:
bool— 0 или 1;int— знаковое 32-битное целое в дополнительном коде;uint— беззнаковое целое 0–4294967295;float— двоичный формат IEEE 754 binary32;enum— целый индекс варианта, начиная с нуля.
Число десятичных знаков отображения Reg не меняет его передаваемое значение. Для float требуется интерпретация битов как float32, а не числовое преобразование uint32 в float.
Внутри каждого 16-битного слова сначала передаётся старший байт. Для 32-битного значения сначала передаётся младшее слово, затем старшее. Например:
Reg(0) = 0x12345678
адрес 160: слово 0x5678, байты 56 78
адрес 161: слово 0x1234, байты 12 34
Для чтения Reg(0) отправьте FC03, начало 160, количество 2. Значение собирается как low | (high << 16). Для float 1.0 исходные биты равны 0x3F800000, слова на линии — [0x0000, 0x3F80]. В программах опроса выбирайте порядок младшего слова первым; названия опций могут отличаться.
При чтении двух половин в одном запросе они соответствуют одному снимку Reg, даже если сценарий меняет значение. Чтение одной половины разрешено, но два отдельных запроса не гарантируют согласованную пару. Снимок всех Reg одновременно не гарантируется. Для системных 32-битных счётчиков и масок по адресам 2–3, 6–7 и 13–14 отдельный согласованный снимок также не гарантируется.
Правила записи
Пользовательский Reg записывается только целиком одним FC16, с младшим и старшим словом в одном запросе:
Функция: 16 (0x10)
Начало: 160
Количество: 2 слова
Данные: [0x5678, 0x1234]
Результат: Reg(0) = 0x12345678
Данные PDU этого запроса, без адреса устройства, MBAP и CRC:
10 00 A0 00 02 04 56 78 12 34
Нельзя записывать Reg двумя FC06 или двумя отдельными запросами с одной половиной. Такие запросы отклоняются с exception 03 и не сохраняют отложенную половину. Для блока Reg начальный адрес должен приходиться на младшее слово, а количество слов — быть чётным. Например, десять Reg записываются одним FC16: начало 160, количество 20 слов.
Контроллер собирает конечное 32-битное значение, проверяет его тип и заданные ограничения, затем записывает целиком. Промежуточного значения из новой младшей и старой старшей половин не возникает. Повторная запись уже установленного допустимого значения успешна.
Перед выполнением записи проверяется весь диапазон: неверный адрес, доступ, значение или неполная пара не должны менять предыдущие ресурсы блока. Однако весь запрос не является общей атомарной транзакцией: если условия изменились во время выполнения или команда сценария завершилась ошибкой, уже выполненные действия не откатываются.
Широковещательные запросы RTU
Адрес 0 используется для RTU broadcast. Допустимые записи FC06/FC16 выполняются без ответа, чтение игнорируется. Для пользовательских Reg по-прежнему нужны полные пары FC16. Отсутствие ответа на broadcast ожидаемо и не подтверждает успешную запись; результат можно проверить обычным чтением по адресу устройства.
Для обычных запросов RTU контроллер проверяет адрес и CRC. Запросы с чужим адресом или неверной CRC игнорируются без ответа. В TCP указывайте настроенный адрес контроллера; широковещательный режим RTU на TCP не переносится.
Ошибки и диагностика
| Exception | Значение для клиента |
|---|---|
| 01 — Illegal Function | Неподдерживаемая функция. |
| 02 — Illegal Data Address | Неверный/несуществующий адрес, скрытый ресурс, запрет доступа или попытка записи в ресурс только для чтения. Весь диапазон должен быть доступен. |
| 03 — Illegal Data Value | Неверная длина, количество слов, byteCount, значение, команда управления сценарием или неполная/смещённая пара Reg. |
| 04 — Slave Device Failure | Ошибка выполнения, например сценарий не найден или его не удалось запустить/остановить. |
| 06 — Slave Device Busy | Ресурс занят; запрос можно повторить позднее. |
Некорректный заголовок Modbus TCP (Protocol ID или длина MBAP) приводит к закрытию соединения. Подключитесь заново после исправления запроса. TCP допускает передачу запроса частями и нескольких запросов в одном пакете; сопоставляйте ответы по Transaction ID.
Если нет ответа, проверьте включение нужного интерфейса, адрес устройства, доступность TCP-порта 502 или параметры RS-485, затем права и адреса ресурсов. Для TCP также закройте другие клиенты. Журнал доступен в веб-интерфейсе и по HTTP /txt?log; сведения о hardFault и переполнении стека — /txt?sys.