Документация
Руководство для начинающих
Modbus Sandbox - бесплатный сервер в интернете, который притворяется сразу десятью Modbus-устройствами. К нему подключают свой клиент - программу, опрашивающую приборы, - и проверяют, как она читает и пишет данные. Регистрация не нужна, всё происходит в реальном времени.
Что нужно знать перед стартом:
- Modbus-TCP общается через адрес и порт. У нас это
modbus-sendbox.electroman.by, порт5020. - Внутри живут 10 устройств - их называют слейвами и нумеруют от 1 до 10, а номер устройства в запросе зовут unit ID.
- Данные лежат в регистрах - ячейках с числами или битами. Есть четыре области: HR (holding registers) читаются и пишутся, IR (input registers) только читаются, CL (coils) - биты с записью, DI (discrete inputs) - биты только для чтения.
- Клиент общается с сервером функциями: 1 и 2 читают биты, 3 и 4 читают регистры, 5 и 6 пишут одну ячейку, 16 пишет до двух регистров сразу. Остальные функции сервер не поддерживает.
Первая проверка по шагам:
- Откройте ModbusToolkit (или любой другой Modbus-клиент) и создайте соединение Modbus TCP с хостом
modbus-sendbox.electroman.byи портом5020. - Укажите unit ID 1 и прочитайте 10 holding registers с адреса 1 функцией 3. В ответ придёт список чисел.
- Запишите в первый регистр своё значение функцией 6 и прочитайте снова - значение совпадёт.
- Откройте раздел «Карта регистров» этого сайта и найдите тот же адрес: вы увидите то же значение. Его видят все подключённые пользователи, и любой может его перезаписать.
- Загляните в «Поведения», чтобы назначить регистру генератор (счётчик, пила, синус), и в «Инъекции», чтобы устроить слейву ошибку или задержку ответа.
Коротко о разделах сайта:
- «Обзор» - адрес, порт и форматы данных по слейвам.
- «Карта регистров» - живые значения и редактирование.
- «Поведения» - автоматическая генерация значений.
- «Часовые пояса» - 27 зон времени и Unix timestamp слейва 10.
- «Инъекции» - имитация сбоев устройства.
- «Журнал» - кто и что менял.
- «Инструменты» - конвертер байтов, готовые конфиги для программ и сравнение снимков.
Примеры использования
Ниже - короткие сценарии проверки клиента на живых данных, каждый занимает несколько минут. Примеры написаны для ModbusToolkit, но подойдёт любой Modbus-клиент. Демо-слоты заполняют себя сами, а свободные регистры общие - их может перезаписать другой участник.
Проверить сборку 32-битных значений (uint32 CDAB)
Порядок слов CDAB - самая частая ошибка: старшее слово хранится вторым. Откройте «Карту регистров», slave 3, и найдите демо-слот 1009 или синус на слоте 1015. Прочитайте из клиента функцией 3 два регистра с этого адреса (device_id=3), соберите из них значение так, как умеет ваш клиент, и сверьте с тем, что показывает интерфейс. Совпало - порядок слов у вас правильный. Затем запишите своё значение функцией 6 в свободный регистр с адресом 1 и прочитайте обратно.
Проверить реакцию на пропавший ответ (timeout)
Клиент не должен зависать, когда устройство молчит. В разделе «Инъекции» добавьте инъекцию типа timeout на slave 1 с TTL 30 секунд и запустите в клиенте цикл чтения с тайм-аутом 1-2 секунды. Пока инъекция жива, ответов нет: клиент обязан показать ошибку, а не висеть. Когда TTL закончится, чтение восстановится само, без перезапуска клиента. Полезная вариация - инъекция exception с кодом 0x06: клиент должен корректно обработать ответ Device Busy.
Проверить float с перевёрнутыми байтами (float DCBA)
Порядок DCBA легко пропустить: мусорное число иногда выглядит правдоподобно. Возьмите slave 9 и демо-слот 1015 с синусом, прочитайте из клиента два регистра и соберите float. Затем в разделе «Инструменты» вставьте эти же два регистра в конвертер: он покажет значение во всех четырёх порядках. Ваше значение должно совпасть со строкой DCBA и с живым значением слота.
Проверить время и часовые пояса (slave 10)
Раздел «Часовые пояса» показывает 27 зон с датой, временем и смещением UTC, а также Unix timestamp в регистрах 300..301. Прочитайте их из клиента и сверьте с системным временем: расхождение больше пары секунд означает ошибку в разборе uint32 или часового пояса.
Спецификация форматов
Каждый unit ID эмулирует свой формат данных для HR/IR. Значение 32-битного формата занимает два регистра N и N+1, адрес значения - адрес старшего регистра (сетка 1, 3, …, 1023). Буквы ABCD - это байты значения от старшего (A) к младшему (D).
ABCD- слова big-endian (старшее первым), байты в словах big-endian;CDAB- слова little-endian (младшее первым), байты в словах big-endian;BADC- слова big-endian, байты в словах little-endian (swap bytes);DCBA- слова little-endian, байты в словах little-endian (swap bytes и swap words).
| Unit ID | Формат HR/IR | HR | IR | CL | DI |
|---|---|---|---|---|---|
| 1 | uint16 | 1..1024 | 1..1024 | 1..64 | 1..64 |
| 2 | uint32 ABCD | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 3 | uint32 CDAB | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 4 | uint32 BADC | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 5 | uint32 DCBA | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 6 | float ABCD | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 7 | float CDAB | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 8 | float BADC | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 9 | float DCBA | 1,3,…,1023 | 1,3,…,1023 | 1..64 | 1..64 |
| 10 | uint16 | - | 1..268, 300..301 | - | - |
Slave 10 отдаёт только IR. Адреса 1..268 - метрики 27 часовых поясов (8 регистров на зону, незанятые регистры блока равны нулю), адреса 300..301 - Unix timestamp (uint32 ABCD, погрешность 0.5 с). Полная спецификация лежит в docs/HRIR_SPEC.md репозитория.
Ограничения
- Допустимы функции
1, 2, 3, 4, 5, 6, 16. Любая другая отклоняется как Illegal Function (0x01). - FC 5 и FC 6 пишут один CL/HR, FC 16 пишет HR и принимает не более 2 регистров за запрос (иначе Illegal Data Value, 0x03).
- Slave 10 отдаёт только input registers. Чтение CL/DI/HR возвращает Illegal Data Address (0x02).
- Адресация совпадает с docs/HRIR_SPEC.md, пересчёт не нужен. Адрес значения равен адресу на проводе.
- Ограничения окружения: соединений с одного IP - 20, Modbus-записей в секунду на соединение - 200, записей из UI в минуту на IP - 50.
- Инъекции ошибок действуют не дольше 300 с (INJECTION_MAX_TTL_S).
- Журнал хранит последние 1000 событий. Значения регистров и вся история живут только в памяти, перезапуск сервера сбрасывает всё.
Правила многопользовательскости
- HR и CL общие (shared last-write-wins). Запись любого пользователя сразу видна остальным и в любой момент может быть перезаписана кем-то ещё. Это принцип работы сервера, а не ошибка.
- IR и DI генерируются автоматически движком поведений (счётчики, паттерны) и доступны только для чтения.
- Настройки поведений и инъекции ошибок тоже общие для всех подключений.
- Аутентификации нет. Не передавайте через сервер чувствительные данные и не используйте его как стенд с реальным оборудованием.
API
REST-интерфейс описан в Swagger UI по адресу /api/docs (префикс /api/modbus/). WebSocket /ws/state отдаёт live-состояние слейва, /ws/events - события журнала. Примеры подключения собраны ниже.
Открыть Swagger UI Схема OpenAPI (JSON)
Подключение из ModbusToolkit
ModbusToolkit - отдельный десктопный Modbus-клиент того же автора, Modbus Sandbox к нему не привязан и работает с любым клиентом. Создайте Modbus TCP соединение к песочнице и проверьте клиентский код на живых данных.
- Создайте соединение Modbus TCP: хост
modbus-sendbox.electroman.by, порт5020, Unit ID 1..10. - Прочитайте 10 регистров HR слейва 1 функцией 3 с адреса 1. Формат значений slave 1 - uint16.
- Запишите значение функцией 6 и убедитесь, что данные общие. Запись видна всем подключённым клиентам (shared last-write-wins).
Скачать ModbusToolkit можно на modbustoolkit.electroman.by. Документация продукта: docs.html. Новости и поддержка: t.me/modbustoolkit.
Примеры кода
Готовые сниппеты для pymodbus, modbus-serial и NModbus. Адресация - как в docs/HRIR_SPEC.md, без пересчёта; unit ID - 1..10. HR/CL общие для всех подключений (shared last-write-wins).
Python · pymodbus
# pip install "pymodbus>=3.6"
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient("modbus-sendbox.electroman.by", port=5020)
if not client.connect():
raise SystemExit("нет соединения с сервером")
# FC 3: читаем 10 регистров HR слейва 1 (uint16) с адреса 1
result = client.read_holding_registers(1, count=10, device_id=1)
if result.isError():
raise SystemExit(f"ошибка Modbus: {result}")
print(result.registers)
# FC 4: input registers (на slave 10 только они)
result = client.read_input_registers(1, count=8, device_id=10)
# FC 5 / FC 6 / FC 16: запись CL и HR (значения общие для всех!)
client.write_coil(1, True, device_id=1)
client.write_register(1, 42, device_id=1)
client.write_registers(1, [1, 2], device_id=1) # FC 16, не более 2 регистров
client.close()
Node.js · modbus-serial
// npm install modbus-serial
const ModbusRTU = require("modbus-serial");
async function main() {
const client = new ModbusRTU();
await client.connectTCP("modbus-sendbox.electroman.by", { port: 5020 });
client.setID(1); // unit ID 1..10
// FC 3: 10 регистров HR с адреса 1
const result = await client.readHoldingRegisters(1, 10);
console.log(result.data);
// FC 4: input registers (на slave 10 только они)
client.setID(10);
console.log((await client.readInputRegisters(1, 8)).data);
// FC 5 / FC 6: запись CL и HR (значения общие для всех!)
client.setID(1);
await client.writeCoil(1, true);
await client.writeRegister(1, 42);
client.close();
}
main().catch(console.error);
C# · NModbus
// dotnet add package NModbus
using System.Net.Sockets;
using NModbus;
var tcp = new TcpClient("modbus-sendbox.electroman.by", 5020);
var client = new ModbusFactory().CreateMaster(tcp);
// FC 3: 10 регистров HR слейва 1 (uint16) с адреса 1
ushort[] regs = client.ReadHoldingRegisters(slaveId: 1, startAddress: 1, numberOfPoints: 10);
Console.WriteLine(string.Join(", ", regs));
// FC 4: input registers (на slave 10 только они)
ushort[] ir = client.ReadInputRegisters(1, 1, 8);
// FC 5 / FC 6: запись CL и HR (значения общие для всех!)
client.WriteSingleCoil(1, 1, true);
client.WriteSingleRegister(1, 1, 42);
Исходный код и свой сервер
Код сервера открыт и лежит на GitHub: StarLeG/modbustoolkit-sandbox. Свою копию удобно поднять в закрытой сети стенда без доступа в интернет или там, где инъекции и записи не должны задевать других пользователей.
1. Клонировать репозиторий
git clone https://github.com/StarLeG/modbustoolkit-sandbox.git
cd modbustoolkit-sandbox
2. Запустить через uv (рекомендуется)
# нужен Python 3.11+ и uv: https://docs.astral.sh/uv/
uv sync
uv run python -m app
…или в Docker
cp .env.example .env # поправьте MODBUS_PUBLIC_HOST и порты
docker compose up -d --build
…или через pip, без uv
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install .
python -m app
После запуска web UI открывается на http://127.0.0.1:8765, Modbus слушает порт 5020 на всех интерфейсах (переменные WEB_HOST/WEB_PORT и MODBUS_TCP_PORT). Задайте MODBUS_PUBLIC_HOST - IP или имя вашей машины, иначе «Обзор» и генератор конфигов покажут адрес публичного сервера. Файл .env читает только Docker Compose; при запуске через python -m app переменные задаются в окружении. Все переменные перечислены в .env.example, варианты с nginx и systemd - в docs/DEPLOY.md.
Сервер бесплатный и останется таким. Сейчас собираем на испытательный стенд с реальными приборами, на котором будет проверяться каждая версия ModbusToolkit.