Batch reading registers

Parsing multiple registers with Struct

When reading a large number of registers, it is often more efficient to read them in batches rather than one at a time. The AsyncModbusClient class provides methods to read multiple registers in a single request.

For this, we leverage the struct.Struct class from the Python standard library to define the data structure of the registers we want to read. You can pass any struct.Struct object into the read_struct_format() method.

For example, if we want to read 10 consecutive holding registers starting from address 100, and interpret them as two 32-bit integers followed by a 32-bit float, we can do the following:

import asyncio
import struct

from tmodbus import create_async_tcp_client


async def main() -> None:
    """Show example of reading a batch of Modbus registers."""
    register_format = ">iif"  # Big-endian: 2x int32, 1x float32 # codespell:ignore iif
    async with create_async_tcp_client("127.0.0.1", 502, unit_id=1) as client:
        response = await client.read_struct_format(
            start_address=100,
            format_struct=register_format,
        )
        int1, int2, float1 = response
        print(f"Read values: int1={int1}, int2={int2}, float1={float1}")


if __name__ == "__main__":
    asyncio.run(main())

As one Modbus register is 16 bits (2 bytes), the above example reads a total of 6 registers (4 bytes for each int32 and float32, totaling 12 bytes).

Word ordering

Modbus defines registers as 16-bit values, but many devices use 32-bit or 64-bit values that span multiple registers. While the order of the bytes within each register is defined by the Modbus specification (big-endian), the order for values spanning multiple registers (word order) is not standardized and can vary between devices.

The AsyncModbusClient class allows you to specify the word order when reading structured data. You can set the word_order attribute to either “big” or “little” to control the order of the registers.

By default, the word order is set to “big”, meaning that the first register read will be the most significant word.

This library includes an utility class OrderAwareStruct that extends struct.Struct to handle word order automatically. So even if your device uses little-endian word order and/or byte order, you can still use the same struct format string as you would for big-endian.

Note

The word_order attribute only affects methods that read or write structured data using struct.Struct, such as read_struct_format() and write_struct_format(), and all the methods that build on them (like read_uint32(), and write_float()). It does not affect methods that read or write raw registers, such as read_holding_registers() or write_multiple_registers().