Размер базы InfluxDB при длительной записи
Замер: CM4 (RK3566), telegraf 1.28 → InfluxDB 2.7, датчик DL-303 по Modbus TCP, 5 значений (CO, CO2, влажность, температура, точка росы), опрос раз в 1 с, 23–25.10.2026.
Замер: CM4 (RK3566), telegraf 1.28 → InfluxDB 2.7, датчик DL-303 по Modbus TCP, 5 значений (CO, CO2, влажность, температура, точка росы), опрос раз в 1 с, 23–25.10.2026.
Когда разрабатываешь систему мониторинга или SCADA, часто нужно протестировать опрос датчиков — но реального оборудования под рукой нет. Или нужно показать демо заказчику без физических устройств. Или хочется отладить логику мастера не выезжая на объект.
Именно для этого мы написали modbus_slave — эмулятор Modbus RTU slave устройств на C, который работает на Linux и Windows, не требует зависимостей и умеет отдавать реальные данные из файлов.
Идея простая: один процесс слушает RS-485 шину и отвечает на запросы от любого из 30 адресов. Для мастера это выглядит как несколько физических устройств на линии — он не видит разницы.
Мастер (PC/ПЛК) RS-485 шина modbus_slave (NAPI2)
mbpoll -a 1 ──────────────────────────► slave ID 1 → /tmp/cpu.dat
mbpoll -a 2 ──────────────────────────► slave ID 2 → /tmp/time.dat
mbpoll -a 3 ──────────────────────────► slave ID 3 → random
Регистры читаются из обычных текстовых файлов — одно число на строку.
Файлы живут в /tmp (tmpfs — RAM диск), SD карта не изнашивается.
# Скачать статический бинарь
wget https://github.com/lab240/modpoll-slave/raw/main/bin/modbus_slave_aarch64
chmod +x modbus_slave_aarch64
# Запустить — 3 датчика, порт ttyS7
./modbus_slave_aarch64 -p /dev/ttyS7 -b 115200 -a 3
modbus_slave.exe -p COM4 -b 115200 -a 3
mbpoll -m rtu -b 115200 -P none -a 1 -r 1 -c 20 /dev/ttyUSB0
Самое интересное — каждому slave можно привязать файл с реальными значениями. Формат простой: одно целое число на строку.
# Запуск: slave 1 читает данные CPU, slave 2 — время, slave 3 — рандом
./modbus_slave -p /dev/ttyS7 -b 115200 -a 3 \
-f 1:/tmp/cpu.dat \
-f 2:/tmp/time.dat
Файл обновляется внешним скриптом атомарно через mv — никакой гонки данных:
# Температура ядер CPU (°C × 100)
while true; do
for zone in /sys/class/thermal/thermal_zone*/temp; do
val=$(( $(cat $zone) / 10 ))
echo $val
done > /tmp/cpu.tmp
mv /tmp/cpu.tmp /tmp/cpu.dat
sleep 5
done
Значение 4523 в регистре означает 45.23 °C — стандартное соглашение
для передачи дробных чисел в Modbus.
Если скрипт обновления данных упал — файл перестаёт обновляться, но данные в нём остаются старые. Мастер продолжал бы читать устаревшие значения.
Параметр -t задаёт максимальный возраст файла:
./modbus_slave -p /dev/ttyS7 -b 115200 -f 1:/tmp/cpu.dat -t 10
Если файл не обновлялся более 10 секунд — все регистры возвращают 0. Это сразу видно мастеру и SCADA системе. В лог пишется предупреждение (не чаще раза в минуту чтобы не спамить):
file /tmp/cpu.dat is stale (45s > 10s), returning zeros
По умолчанию t=10. Отключить: -t 0.
# Запуск как демон
./modbus_slave -d -p /dev/ttyS7 -b 115200 -a 3 -f 1:/tmp/cpu.dat
# Статус
./modbus_slave -s
# Остановка
./modbus_slave -k
# Логи
journalctl -t modbus_slave -f
Для автозапуска при загрузке — systemd service:
sudo cp modbus_slave /usr/local/bin/
sudo cp service/modbus_slave.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now modbus_slave
Статистика запросов пишется в лог каждые 60 секунд:
stats: slaves=3 total_ok=12480 total_err=0
На Windows всё то же самое, только порт называется COM4 вместо /dev/ttyS7,
файлы в C:\temp\ вместо /tmp/, и -bg вместо -d:
modbus_slave.exe -bg -p COM4 -b 115200 -a 3 ^
-f 1:C:\temp\cpu.dat ^
-f 2:C:\temp\time.dat
Логи пишутся в C:\temp\modbus_slave.log.
Код разделён на три файла:
| Файл | Содержимое |
|---|---|
modbus_core.h | Вся логика Modbus: CRC16, FC03, файлы, статистика |
modbus_slave.c | Linux: termios, fork, syslog, /proc |
modbus_slave_win.c | Windows: Win32 API, CreateFile, DCB |
# Linux x86_64
gcc -O2 -Wall -o modbus_slave src/modbus_slave.c
# Linux aarch64 статический
aarch64-linux-gnu-gcc -O2 -Wall -static -o modbus_slave_aarch64 src/modbus_slave.c
# Windows .exe с Linux
x86_64-w64-mingw32-gcc -O2 -Wall -o modbus_slave.exe src/modbus_slave_win.c
modbus_slave решает конкретную задачу — дать возможность разрабатывать
и тестировать Modbus мастер без реального железа, или превратить одноплатный
компьютер в многоканальный шлюз данных в Modbus.
Код открытый, собирается одной командой, работает на том же NAPI2 где и всё остальное.
Репозиторий: github.com/lab240/modpoll-slave
247 адресов за 2.5 секунды, ноль зависимостей, один .c файл. Рассказываем, зачем мы написали свой сканер Modbus-шины и как он работает.
Кто работал с Modbus RTU, знает ситуацию: подключаешь шлюз к RS-485 шине, а там десяток устройств с неизвестными адресами. Или один датчик, но кто-то поставил ему адрес 117 вместо документированного 1. Или устройство просто не отвечает — и непонятно, проблема в адресе, скорости, чётности или в самом устройстве.
Стандартный подход - mbpoll или любой Modbus-клиент, которым вручную перебираешь адреса. Это работает, но медленно и неудобно: 247 возможных адресов, на каждый нужно отправить запрос, подождать таймаут, проверить ответ.
Мы решили автоматизировать это одной утилитой.
Репозиторий: github.com/lab240/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.
Для каждого адреса в диапазоне:
Между запросами выдерживает межкадровую паузу 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
Каталог 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/.
Если есть тулчейн 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.
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-плата.