Decompose requests

A Modbus requests and responses contains a Application Data Unit (ADU) which contains a Protocol Data Unit (PDU). The ADU is the enveloppe containing a message, the PDU is the message itself. Modbus requests can be send communication layers layers, like RTU or TCP/IP. The ADU for these layers differs. But the PDU, the message, has always the same strcuture, independent of the way it’s transported.

PDU

Note

This section is based on MODBUS Application Protocol Specification V1.1b3

The Protocol Data Unit (PDU) is the request or response message and is indepedent of the underlying communication layer. This module only implements requests PDU’s.

A request PDU contains two parts: a function code and request data. A response PDU contains the function code from the request and response data. The general structure is listed in table below:

Field Size (bytes)
Function code 1
data N

Below you see the request PDU with function code 1, requesting status of 3 coils, starting from coil 100:

>>> req_pdu = b'd'
>>> function_code = req_pdu[:1]
>>> function_code
b''
>>> starting_address = req_pdu[1:3]
>>> starting_address
b'd'
>>> quantity = req_pdu[3:]
>>> quantity
b''

A response PDU could look like this:

>>> resp_pdu = b''
>>> function_code = resp_pdu[:1]
>>> function_code
b''
>>> byte_count = resp[1:2]
>>> byte_count
b''
>>> coil_status = resp[2:]
'b'

ADU for TCP/IP requests and responses

The Application Data Unit (ADU) for Modbus messages carried over a TCP/IP are build out of two components: a MBAP header and a PDU. The Modbus Application Header (MBAP) is what makes Modbus TCP/IP requests and responsen different from their counterparts send over a serial line. Below the components of the Modbus TCP/IP are listed together with their size in bytes:

Component Size (bytes)
MBAP Header 7
PDU N

Below you see an hexidecimal presentation of request over TCP/IP with Modbus function code 1. It requests data of slave with 1, starting at coil 100, for the length of 3 coils:

>>> # Read coils, starting from coil 100 for the length of 3 coils.
>>> adu = b'\x00\x08\x00\x00\x00\x06\x01\x01\x00d\x00\x03'

The length of the ADU is 12 bytes:

>>> len(adu)
12

The MBAP header is 7 bytes long:

>>> mbap = adu[:7]
>>> mbap
b'\x00\x08\x00\x00\x00\x06\x01'

The MBAP header contains the following fields:

Field Length (bytes) Description
Transaction identifier 2 Identification of a Modbus request/response transaction.
Protocol identifier 2 Protocol ID, is 0 for Modbus.
Length 2 Number of following bytes
Unit identifier 1 Identification of a remote slave

When unpacked, these fields have the following values:

>>> transaction_id = mbap[:2]
>>> transaction_id
b'\x00\x08'
>>> protocol_id = mbap[2:4]
>>> protocol_id
b'\x00\x00'
>>> length = mbap[4:6]
>>> length
b'\x00\x06'
>>> unit_id = mbap[6:]
>>> unit_id
b'\0x01'

The request in words: a request with Transaction ID 8 for slave 1. The request uses Protocol ID 0, which is the Modbus protocol. The length of the bytes after the Length field is 6 bytes. These 6 bytes are Unit Identifier (1 byte) + PDU (5 bytes).

ADU for RTU requests and responses

The ADU for Modbus RTU messages differs from Modbus TCP/IP messages. Messages send over RTU don’t have a MBAP header, instead they have an Address field. This field contains the slave id. A CRC is appended to the message. Below all parts of a Modbus RTU message are listed together with their byte size:

Component Size (bytes)
Address field 1
PDU N
CRC 2

The CRC is calculated from the Address field and the PDU.

Below you see an hexidecimal presentation of request over RTU with Modbus function code 1. It requests data of slave with 1, starting at coil 100, for the length of 3 coils:

>>> # Read coils, starting from coil 100 for the length of 3 coils.
>>> adu = b'\x01\x01\x00d\x00\x03=\xd4'

The lenght of this ADU is 8 bytes:

>>> len(adu)
8