Утилита mbscan - быстрый поиск Modbus устройств на линии
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 Справка
Как это работает внутри
Алгоритм прямолинейный, но дьявол в деталях:
-
Открывает последовательный порт, настраивает скорость, чётность, количество стоп-битов через
termios. -
Для каждого адреса в диапазоне:
- Сбрасывает буфер порта от предыдущих данных
- Формирует 8-байтовый запрос Modbus RTU FC03 с CRC16
- Отправляет запрос и ждёт ответ с настроенным таймаутом
- Валидирует ответ: проверяет CRC, адрес slave, код функции
- Если всё сходится — выводит найденное устройство с содержимым регистров
-
Между запросами выдерживает межкадровую паузу 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, как и остальные наши инструменты.