Skip to content

Quickstart

The whole loop is: connect to a device, read two holding registers, decode them, and close. It assumes you have installed the package with a backend extra.

import asyncio
from modbus_connection import ModbusTcpParams
from modbus_connection.decode import decode_uint32
from modbus_connection.tmodbus import ModbusConnection
async def main() -> None:
connection = ModbusConnection(ModbusTcpParams(host="192.168.1.50"))
try:
unit = connection.for_unit(1)
words = await unit.read_holding_registers(2, 2) # -> [word, word]
print("raw words:", words)
print("as uint32:", decode_uint32(words))
finally:
await connection.close()
asyncio.run(main())

Register reads return raw 16-bit words. The decode module turns them into Python values. Here it turns two words into one unsigned 32-bit integer. To switch backends, replace modbus_connection.tmodbus with modbus_connection.pymodbus. Nothing else changes.

Every operation raises a subclass of ModbusError on failure. A minimal guarded read looks like this:

from modbus_connection import ModbusError
try:
words = await unit.read_holding_registers(2, 2)
except ModbusError as err:
print(f"read failed: {err}")

From here:

  • Connections and units covers ownership, lifecycle, transports, and request spacing.
  • Modbus operations covers the full operation surface and decoding.
  • Device modelling maps registers to typed attributes instead of decoding by hand. Use it for any device with more than a handful of values.
  • Building a library is the pattern for a device library other people use. The Device base class carries its setup and polling. A query helper checks a real device and the mock backend tests without one.