Кадр Modbus: из чего состоит запрос
Любой обмен в Modbus — это пара «запрос — ответ». Внутри каждого сообщения лежит одна и та же конструкция: номер функции и её данные. Всё остальное — обёртка, которая зависит от среды передачи.
PDU и ADU
Сообщение Modbus состоит из двух вложенных частей. PDU (protocol data unit) — смысловая часть: один байт кода функции и данные этой функции. Она не зависит от того, по какой линии идёт обмен. ADU (application data unit) — то, что реально уходит в линию: PDU плюс обрамление, которое отвечает за адресацию и целостность.
Возьмём один запрос — «прочитать три регистра хранения начиная с адреса 107» — и посмотрим, как он выглядит на каждом уровне.
Шаг 1. PDU — пять байт, одинаковых для RTU и TCP:
03 00 6B 00 03
| Байты | Поле PDU | Значение |
|---|---|---|
| 03 | Код функции | чтение регистров хранения |
| 00 6B | Данные функции: начальный адрес | 107 |
| 00 03 | Данные функции: количество регистров | 3 |
Обратите внимание: ни адреса устройства, ни контрольной суммы здесь нет — PDU не знает, кому адресован запрос и по какой среде поедет.
Шаг 2. Тот же PDU в обёртке RTU — восемь байт:
01 03 00 6B 00 03 74 17
| Часть ADU | Байты | Зачем |
|---|---|---|
| Обрамление спереди | 01 | адрес ведомого устройства: кому предназначен кадр |
| PDU | 03 00 6B 00 03 | без изменений |
| Обрамление сзади | 74 17 | CRC16: проверка целостности, младший байт первым |
Шаг 3. Тот же PDU в обёртке TCP — двенадцать байт:
00 01 00 00 00 06 01 03 00 6B 00 03
| Часть ADU | Байты | Зачем |
|---|---|---|
| Заголовок MBAP | 00 01 | идентификатор транзакции: по нему ответ сопоставляется с запросом |
| Заголовок MBAP | 00 00 | идентификатор протокола: для Modbus всегда ноль |
| Заголовок MBAP | 00 06 | длина: сколько байт идёт дальше (unit id + PDU) |
| Заголовок MBAP | 01 | идентификатор устройства: адрес прибора за шлюзом |
| PDU | 03 00 6B 00 03 | снова без изменений |
| Обрамление сзади | — | контрольной суммы нет: целостность обеспечивает TCP |
Шаг 4. Ответ устроен так же. PDU ответа несёт код функции, число байт данных и сами данные:
03 06 02 2B 00 00 00 64
| Байты | Поле PDU ответа | Значение |
|---|---|---|
| 03 | Код функции | тот же, что в запросе — значит, устройство отработало без ошибки |
| 06 | Число байт данных | 6 байт, то есть три регистра по два байта |
| 02 2B | Данные: первый регистр | 0x022B = 555 — регистр 107, с которого начинали чтение |
| 00 00 | Данные: второй регистр | 0 — регистр 108 |
| 00 64 | Данные: третий регистр | 0x0064 = 100 — регистр 109 |
Адресов регистров в ответе нет — только значения подряд. Соответствие «значение ↔ адрес» мастер восстанавливает по своему же запросу: читали три регистра с адреса 107, значит первое значение относится к 107, второе к 108, третье к 109.
В линии RS-485 к этому PDU добавляются адрес и контрольная сумма — всего одиннадцать байт:
01 03 06 02 2B 00 00 00 64 05 7A
| Часть ADU | Байты | Зачем |
|---|---|---|
| Обрамление спереди | 01 | адрес ведомого: кто именно ответил. В линии несколько устройств, и мастер по этому байту понимает, чей ответ |
| PDU | 03 06 02 2B 00 00 00 64 | смысловая часть — та же, что была бы и в сети |
| Обрамление сзади | 05 7A | CRC16 по всем предыдущим байтам, младший байт первым. Не сошёлся — кадр молча отбрасывается |
В сети вместо адреса и CRC появляется заголовок MBAP — всего пятнадцать байт:
00 01 00 00 00 09 01 03 06 02 2B 00 00 00 64
| Часть ADU | Байты | Зачем |
|---|---|---|
| Заголовок MBAP | 00 01 | идентификатор транзакции: совпадает с номером из запроса, по нему ответ и сопоставляется |
| Заголовок MBAP | 00 00 | идентификатор протокола: для Modbus всегда ноль |
| Заголовок MBAP | 00 09 | длина: девять байт дальше — один байт unit id плюс восемь байт PDU |
| Заголовок MBAP | 01 | идентификатор устройства: тот же, что в запросе |
| PDU | 03 06 02 2B 00 00 00 64 | снова без изменений |
| Обрамление сзади | — | контрольной суммы нет: целостность обеспечивает TCP |
Сравните два последних кадра: середина у них совпадает байт в байт, различаются только начало и конец. Именно поэтому шлюз RS-485 ↔ Ethernet не «переводит» протокол, а лишь меняет обёртку — и поэтому при отладке через шлюз сравнивать надо PDU, а не кадры целиком.
Ограничения размеров. Максимальный PDU — 253 байта. Отсюда получаются и остальные пределы:
| Что | Размер | Откуда |
|---|---|---|
| PDU | до 253 байт | ограничение протокола |
| ADU в RTU | до 256 байт | 1 байт адреса + 253 байта PDU + 2 байта CRC |
| ADU в TCP | до 260 байт | 7 байт заголовка MBAP + 253 байта PDU |
| Регистров за один запрос | 125 | 125 × 2 байта + код функции + счётчик байт = 252 байта — влезает в PDU |
| Бит за один запрос | 2000 | 2000 бит = 250 байт данных + служебные байты |
Эти цифры — не абстракция: попытка прочитать 130 регистров одним запросом вернёт исключение, потому что ответ просто не помещается в PDU.
Разбор запроса RTU
Прочитаем три регистра хранения, начиная с адреса 107, у устройства с адресом 1:
01 03 00 6B 00 03 74 17
| Байты | Что это | Значение |
|---|---|---|
| 01 | Адрес ведомого устройства | 1 |
| 03 | Код функции | Чтение регистров хранения |
| 00 6B | Начальный адрес | 107 — первый читаемый регистр |
| 00 03 | Количество регистров | 3 |
| 74 17 | Контрольная сумма CRC16 | младший байт первым |
Обратите внимание на порядок: все числовые поля в PDU передаются старшим байтом вперёд, и только CRC — младшим. Это классическая ловушка при написании своего мастера.
Разбор ответа
01 03 06 02 2B 00 00 00 64 05 7A
| Байты | Что это | Значение |
|---|---|---|
| 01 | Адрес ведомого | ответило устройство 1 |
| 03 | Код функции | тот же, что в запросе — значит, ошибки нет |
| 06 | Число байт данных | 6 байт = 3 регистра |
| 02 2B | Регистр 107 | 0x022B = 555 |
| 00 00 | Регистр 108 | 0 |
| 00 64 | Регистр 109 | 0x0064 = 100 |
| 05 7A | CRC16 |
Ответ не содержит адресов регистров — только значения подряд. Сопоставление «значение ↔ регистр» держит у себя мастер, по своему же запросу. Поэтому при разборе ответа важно не потерять контекст запроса.
Тот же обмен в Modbus TCP
00 01 00 00 00 06 01 03 00 6B 00 03
| Байты | Что это | Значение |
|---|---|---|
| 00 01 | Идентификатор транзакции | по нему ответ сопоставляется с запросом |
| 00 00 | Идентификатор протокола | всегда 0 для Modbus |
| 00 06 | Длина | 6 байт дальше: unit id и PDU |
| 01 | Идентификатор устройства (unit id) | адрес за шлюзом; при прямом подключении часто 1 или 255 |
| 03 00 6B 00 03 | PDU | та же функция с теми же параметрами |
Отличия от RTU ровно два: спереди семибайтный заголовок, сзади нет CRC. PDU не меняется — поэтому конвертер RS-485 ↔ Ethernet может просто переупаковывать кадры.
Как считается CRC16
Контрольная сумма считается по всем байтам ADU, кроме самой суммы. Алгоритм: регистр инициализируется значением 0xFFFF, каждый байт складывается по исключающему ИЛИ с младшим байтом регистра, затем восемь раз сдвиг вправо; если при сдвиге ушла единица, регистр складывается с полиномом 0xA001. В кадр результат укладывается младшим байтом вперёд.
Проверка: для запроса 01 03 00 6B 00 03 получается 0x1774 — в линии это выглядит как «74 17».
Тайминги RTU
В последовательной линии нет ни длины, ни признака конца кадра — границы задаются паузами. Внутри кадра пауза не должна превышать 1,5 символа, между кадрами должна быть не менее 3,5 символов. На скорости 9600 бод символ длится около миллисекунды, то есть межкадровая пауза — около 4 мс.
Отсюда две частые беды: слишком короткая пауза — и устройство склеивает два кадра в один, а слишком длинная задержка внутри кадра — и кадр рвётся на части. На скоростях выше 19200 бод пауза обычно фиксируется на уровне 1,75 мс.





















































































































