Modbus Master Slave обмен

Modbus Slave 127.0.0.1 ID56 HR0 VAL=4

Результат опроса

Modbus TCP
Запрос Tx:002684-07 |F4 00| |00 00| |00 06| |38| |03 00 00 00 01|
Ответ  Rx:002685-07 |F4 00| |00 00| |00 05| |38| |03 02 00 04|

Встречается модификация с 1 байтом ID-транзакции
Запрос Tx:002684-07 |F4| |00 00| |00 06| |38| |03 00 00 00 01|
Ответ  Rx:002685-07 |F4| |00 00| |00 05| |38| |03 02 00 04|

Modbus TCP с ошибкой запроса
Запрос Tx:002684-07 |F4 00| |00 00| |00 06| |38| |03 00 00 00 01|
Ответ  Rx:002685-07 |F4 00| |00 00| |00 03| |38| |83 06|

Modbus RTU over TCP/IP
Запрос Tx:018165-38 |38| |03 00 00 00 01| 81 63
Ответ  Rx:018166-38 |38| |03 02 00 04| 25 82

Modbus RTU
Запрос Tx:000063-01 |01| |04 00 00 00 01| |31 CA|
Ответ  Rx:000064-01 |01| |04 02 00 00| |B9 30|

ADU (Application Data Unit) — пакет Modbus целиком, со всеми заголовками, PDU, контрольной суммой, адресом и маркерами. Отличается, в зависимости от реализации протокола.

PDU (protocol data unit) — основная часть пакета, одинаковая для всех реализаций протокола. Содержит сам payload.

Запрос кадр ADU

ID-транзакции ID-протокола Длина пакета Адрес Slave Кадр PDU
2 байта 2 байта 2 байта 1 байт <=253 байта
F4 00 00 00 00 06 38 03 00 00 00 01

Запрос кадр PDU

  • 03 - функция
  • 00 00 - адрес первого регистр
  • 00 01 - количество регистров

Ответ кадр ADU

ID-транзакции ID-протокола Длина пакета Адрес Slave Кадр PDU
2 байта 2 байта 2 байта 1 байт <=253 байта
F4 00 00 00 00 05 38 03 02 00 04

Ответ кадр PDU

  • 03 - функция
  • 02 - количенство передаваемых байт
  • 00 04 - данные

Ответ кадр PDU с ошибкой

  • 83 - функция (80 +03) - ошибка
  • 06 - Код ошибки

Расчет контрольной суммы

Используется CRC-16 тип контрольной суммы

Калькулятор расчета

https://www.lammertbies.nl/comm/info/crc-calculation

Modbus RTU
Tx:000063-01 |01| |04 00 00 00 01| |31 CA|
Rx:000064-01 |01| |04 02 00 00| |B9 30|

crc

Python code расчета контрольной суммы

def calculate_crc16(data_bytes):
    """
    Calculate CRC-16 (Modbus) checksum for the given bytes.
    Polynomial: 0x8005 (x^16 + x^15 + x^2 + 1)
    Initial value: 0xFFFF
    """
    crc = 0xFFFF
    for byte in data_bytes:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc >>= 1
                crc ^= 0xA001  # 0xA001 is the reverse of 0x8005
            else:
                crc >>= 1
    return crc

# Input data in hex: 01 04 00 00 00 01
data = bytes.fromhex('01 04 00 00 00 01')

# Calculate CRC-16
crc = calculate_crc16(data)

# The result is in little-endian format (LSB first)
crc_bytes = crc.to_bytes(2, byteorder='little')

print(f"CRC-16 (Modbus) of 01 04 00 00 00 01: 0x{crc:04X}")
print(f"Bytes (LSB first): {crc_bytes.hex(' ')}")

Результат

CRC-16 (Modbus) of 01 04 00 00 00 01: 0xCA31
Bytes (LSB first): 31 ca
[Finished in 97ms]