Перейти к основному содержимому

Утилита mbscan - быстрый поиск Modbus устройств на линии

· 5 мин. чтения
dmn
maintainer

247 адресов за 2.5 секунды, ноль зависимостей, один .c файл. Рассказываем, зачем мы написали свой сканер Modbus-шины и как он работает.


Проблема: «а что вообще висит на шине?»

Кто работал с Modbus RTU, знает ситуацию: подключаешь шлюз к RS-485 шине, а там десяток устройств с неизвестными адресами. Или один датчик, но кто-то поставил ему адрес 117 вместо документированного 1. Или устройство просто не отвечает — и непонятно, проблема в адресе, скорости, чётности или в самом устройстве.

Стандартный подход - mbpoll или любой Modbus-клиент, которым вручную перебираешь адреса. Это работает, но медленно и неудобно: 247 возможных адресов, на каждый нужно отправить запрос, подождать таймаут, проверить ответ.

Мы решили автоматизировать это одной утилитой.

Репозиторий: github.com/lab240/mbscan


Что такое mbscan

mbscan - консольная утилита для сканирования Modbus RTU шины. Открывает последовательный порт, последовательно опрашивает диапазон адресов функцией FC03 (Read Holding Registers) и выводит найденные устройства с содержимым регистров.

Один файл на C, никаких внешних библиотек. Встроенная реализация CRC16, POSIX-совместимый код. Работает на Linux x86_64, aarch64, OpenWrt, Raspberry Pi — везде, где есть терминальный API POSIX.

Быстрый старт

# Сканируем всё на /dev/ttyUSB0 (по умолчанию: 115200-8N1, таймаут 100мс)
mbscan -p /dev/ttyUSB0

# Быстрый скан с таймаутом 10мс
mbscan -p /dev/ttyUSB0 -o 10

# Конкретный диапазон, читаем 4 регистра
mbscan -p /dev/ttyUSB0 -f 1 -t 30 -c 4

# 9600 бод, чётность 8E1
mbscan -p /dev/ttyS1 -b 9600 -d 8E1

Вывод выглядит так:

mbscan: scanning /dev/ttyUSB0 115200-8N1, addresses 1-247, timeout 100ms
mbscan: reading 4 register(s) starting at 0

Found slave 125: [0]=125 [1]=1 [2]=830 [3]=794

mbscan: done. Found 1 device(s).

Нашёл устройство на адресе 125, прочитал 4 регистра — готово.


Параметры

mbscan -p PORT [опции]

-p PORT Последовательный порт (обязательный)
-b BAUD Скорость (по умолчанию: 115200)
-d PARAMS Формат данных: 8N1, 8E1, 8O1, 7E1 и т.д. (по умолчанию: 8N1)
-f FROM Начальный адрес (по умолчанию: 1)
-t TO Конечный адрес (по умолчанию: 247)
-o MS Таймаут на адрес в мс (по умолчанию: 100)
-r REG Начальный регистр, 0-based (по умолчанию: 0)
-c COUNT Количество регистров для чтения (по умолчанию: 1)
-v Подробный вывод
-h Справка

Как это работает внутри

Алгоритм прямолинейный, но дьявол в деталях:

  1. Открывает последовательный порт, настраивает скорость, чётность, количество стоп-битов через termios.

  2. Для каждого адреса в диапазоне:

    • Сбрасывает буфер порта от предыдущих данных
    • Формирует 8-байтовый запрос Modbus RTU FC03 с CRC16
    • Отправляет запрос и ждёт ответ с настроенным таймаутом
    • Валидирует ответ: проверяет CRC, адрес slave, код функции
    • Если всё сходится — выводит найденное устройство с содержимым регистров
  3. Между запросами выдерживает межкадровую паузу Modbus (3.5 символьных времени) — это требование протокола, без него устройства могут путать конец одного кадра и начало другого.

CRC16 реализован встроенный - нет зависимости от libmodbus или других библиотек. Весь код в одном файле mbscan.c.


Скорость сканирования

Скорость определяется таймаутом на адрес. Если устройство не отвечает — ждём полный таймаут. Если отвечает — переходим к следующему сразу после получения ответа.

ТаймаутПолный скан (1–247)Когда использовать
10 мс~2.5 секКороткие кабели, лабораторный стенд
50 мс~12 секБольшинство установок
100 мс~25 секПо умолчанию, надёжно
200 мс~50 секДлинные линии RS-485

На практике 10 мс хватает для стенда с коротким кабелем. Для промышленных линий с десятками метров RS-485 лучше ставить 50-100 мс — на длинных линиях задержки растут из-за переотражений и ёмкости кабеля.


Сборка

Нативная компиляция

cd src
gcc -O2 -Wall -o mbscan mbscan.c

Статическая сборка (один бинарник без зависимостей):

gcc -O2 -Wall -static -o mbscan mbscan.c

Пакет для OpenWrt

Каталог mbscan кладётся в дерево пакетов OpenWrt:

cp -r mbscan /path/to/openwrt/package/
cd /path/to/openwrt
echo "CONFIG_PACKAGE_mbscan=y" >> .config
make package/mbscan/compile -j$(nproc)

Результат — .ipk (или .apk) пакет в bin/packages/*/base/.

Кросс-компиляция для aarch64

Если есть тулчейн OpenWrt:

/path/to/openwrt/staging_dir/toolchain-aarch64_generic_gcc-*/bin/aarch64-openwrt-linux-gcc \
-O2 -Wall -static -o mbscan-linux-aarch64 src/mbscan.c

Готовые бинарники для x86_64 и aarch64 доступны на странице Releases.


Интеграция с luci-app-mbpoll

mbscan - не просто самостоятельная утилита. Он используется как бэкенд для вкладки Scan Bus в веб-интерфейсе luci-app-mbpoll - нашем LuCI-приложении для опроса Modbus-устройств.

Схема простая: пользователь задаёт параметры порта и диапазон адресов в браузере, LuCI вызывает mbscan на устройстве, парсит вывод и отображает найденные устройства в таблице. Не нужно заходить по SSH, не нужно помнить синтаксис - всё через веб-интерфейс.

Репозиторий luci-app-mbpoll: github.com/lab240/luci-app-mbpoll


Где используется

Основная платформа - промышленные IoT-шлюзы NapiLab Napi на базе Rockchip RK3308 под управлением OpenWrt. Napi имеет встроенный RS-485 на /dev/ttyS1 и два USB-порта для дополнительных адаптеров — типичная конфигурация для Modbus-шлюза.

Но mbscan работает на любом Linux с последовательным портом: обычный x86_64 с USB-RS485 адаптером (CH341, CP2102, FTDI), Raspberry Pi, любая embedded-плата.


Лицензия

GPL-2.0 - как и OpenWrt, как и остальные наши инструменты.