bacsys-pymod

Production-grade Modbus TCP / RTU driver for Python.

Warning

Distribution name on PyPI: bacsys-pymod — but the import name is just pymod. So you pip install bacsys-pymod and then import pymod.

Why another Modbus library

bacsys-pymod is built from scratch for industrial deployments where correctness and operability matter. It’s the foundation of an IoT gateway product, so it’s been hardened against the things that actually break in the field:

  • Pipelined TCP requests demuxed by transaction id (no scrambled responses when a slave answers out of order).

  • RTU buses serialised internally — share one client across many tasks, the wire stays clean.

  • Auto-reconnect on TCP drops, with a clean failure surface that distinguishes timeouts from exception responses from connection loss.

  • Heterogeneous batch reads with adjacent-range coalescing (one wire request for Holding(0..4) + Holding(5..14)).

  • All four PLC byte/word orderings (ABCD / CDAB / BADC / DCBA) per item.

  • 16-, 32-, and 64-bit signed/unsigned integers and IEEE 754 float32 / float64.

  • Bit extraction from register blocks (LSB or MSB-first numbering).

  • Sync facade for Flask / scripts; async API for high-throughput services.

  • Server mode is callback-based — bring your own data store.

Install

pip install bacsys-pymod

Note

Requires Python 3.11 or newer. Pure-Python wheel — works on Linux x86_64, Windows, and ARM (Allwinner SoC, Raspberry Pi) without recompilation.

60-second example

import pymod

with pymod.Client.tcp("10.0.0.5", 502, unit_id=1, timeout_s=0.5) as client:
    results = client.read([
        pymod.Holding(start=0,  count=10, dtype="float32"),
        pymod.Holding(start=20, count=5,  dtype="uint16"),
        pymod.Coil(start=0, count=16),
    ])

for r in results:
    if r.ok:
        print(r.values)
    else:
        print(f"failed: {type(r.error).__name__}: {r.error}")

The first two Holding items above coalesce into a single FC03 request on the wire (because they’re adjacent — 0..9 plus 20..24 would be two requests). The Coil item dispatches to FC01. The planner figures this out automatically.

Table of contents

License

MIT — see LICENSE in the source tree.