{"id":4196,"library":"pymodbus","title":"Pymodbus","description":"Pymodbus is a fully featured Modbus protocol stack implemented in Python, offering client and server capabilities for TCP, UDP, and serial communication. It primarily leverages `asyncio` for modern asynchronous operations and is actively maintained with frequent minor releases focusing on bug fixes and incremental improvements. The current version is 3.12.1.","status":"active","version":"3.12.1","language":"en","source_language":"en","source_url":"https://github.com/pymodbus-dev/pymodbus/","tags":["modbus","industrial automation","scada","plc","asyncio","client","server"],"install":[{"cmd":"pip install pymodbus","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"note":"Pymodbus v3.x is primarily asyncio-based. Synchronous clients (e.g., in `pymodbus.client.sync`) were removed or heavily refactored in major version 3 and should not be used for new development.","wrong":"from pymodbus.client.sync import ModbusTcpClient","symbol":"ModbusTcpClient","correct":"from pymodbus.client import ModbusTcpClient"},{"note":"Pymodbus v3.x server implementations are primarily asynchronous. Synchronous servers were removed or heavily refactored. Use `StartAsyncTcpServer` for TCP, `StartAsyncUdpServer` for UDP, or `StartAsyncSerialServer` for serial.","wrong":"from pymodbus.server.sync import ModbusTcpServer","symbol":"StartAsyncTcpServer","correct":"from pymodbus.server import StartAsyncTcpServer"},{"note":"Used for defining server data storage. The `ModbusSequentialDataBlock` is a common block type.","symbol":"ModbusSlaveContext","correct":"from pymodbus.datastore import ModbusSlaveContext, ModbusSequentialDataBlock"}],"quickstart":{"code":"import asyncio\nimport logging\nfrom pymodbus.client import ModbusTcpClient\nfrom pymodbus.server import StartAsyncTcpServer\nfrom pymodbus.datastore import ModbusSlaveContext, ModbusSequentialDataBlock\n\nlogging.basicConfig(level=logging.INFO)\nlog = logging.getLogger(__name__)\n\nasync def run_modbus_server():\n    # Setup a simple Modbus datastore for Slave ID 1\n    store = ModbusSlaveContext(\n        di=ModbusSequentialDataBlock(0, [17]*10),\n        co=ModbusSequentialDataBlock(0, [17]*10),\n        hr=ModbusSequentialDataBlock(0, [17]*10),\n        ir=ModbusSequentialDataBlock(0, [17]*10)\n    )\n    context = ModbusSlaveContext(slaves={0x01: store}, single=False)\n\n    server_task = StartAsyncTcpServer(\n        context=context,\n        address=(\"localhost\", 5020),\n        allow_reuse_address=True\n    )\n    log.info(\"Modbus TCP Server starting on localhost:5020\")\n    await server_task # This will block until cancelled\n\nasync def run_modbus_client():\n    await asyncio.sleep(1) # Give server a moment to start\n    log.info(\"Modbus TCP Client connecting to localhost:5020\")\n    client = ModbusTcpClient(\"localhost\", 5020)\n    if await client.connect():\n        log.info(\"Client connected successfully.\")\n        \n        # Read holding registers (address 0, count 5, slave ID 1)\n        result = await client.read_holding_registers(address=0, count=5, slave=1)\n        if result.is_success():\n            log.info(f\"Read holding registers: {result.registers}\")\n        else:\n            log.error(f\"Failed to read holding registers: {result}\")\n\n        # Write to holding registers (address 0, values [99, 98, 97], slave ID 1)\n        write_result = await client.write_registers(address=0, values=[99, 98, 97], slave=1)\n        if write_result.is_success():\n            log.info(f\"Wrote to holding registers.\")\n        else:\n            log.error(f\"Failed to write holding registers: {write_result}\")\n\n        # Read again to verify write\n        result_after_write = await client.read_holding_registers(address=0, count=5, slave=1)\n        if result_after_write.is_success():\n            log.info(f\"Read holding registers after write: {result_after_write.registers}\")\n        else:\n            log.error(f\"Failed to read holding registers after write: {result_after_write}\")\n\n        client.close()\n        log.info(\"Client disconnected.\")\n    else:\n        log.error(\"Client failed to connect.\")\n\nasync def main():\n    server_task = asyncio.create_task(run_modbus_server())\n    try:\n        await run_modbus_client()\n    finally:\n        server_task.cancel() # Signal server to shut down\n        try:\n            await server_task\n        except asyncio.CancelledError:\n            log.info(\"Server task cancelled successfully.\")\n        except Exception as e:\n            log.error(f\"Server task ended with unexpected error: {e}\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())","lang":"python","description":"This quickstart demonstrates a basic asynchronous Modbus TCP server and client. The server runs on localhost:5020, serving a simple datastore. The client connects, reads initial holding registers, writes new values, and then reads again to verify the write operation, before gracefully shutting down the server."},"warnings":[{"fix":"Rewrite client/server logic using `asyncio` and the new `pymodbus.client` and `pymodbus.server` modules (e.g., `ModbusTcpClient`, `StartAsyncTcpServer`). All I/O operations must be `await`ed.","message":"Pymodbus v3.0.0 introduced a significant shift from synchronous (blocking) to asynchronous (asyncio-based) operations. Code using `pymodbus.client.sync` or `pymodbus.server.sync` from v2.x will be incompatible with v3.x and require complete refactoring to use the new `asyncio` patterns.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"For new projects, consider adopting `SimData` and `SimDevice` introduced in v3.12.0. For existing projects, be aware that a refactor will be necessary upon upgrading to v4.0.0 or later.","message":"The traditional datastore classes (e.g., `ModbusSlaveContext`, `ModbusSequentialDataBlock`) are slated for deprecation and eventual removal in Pymodbus v4.0.0. New `SimData` and `SimDevice` classes have been introduced as the preferred modern approach for defining server data storage.","severity":"breaking","affected_versions":">=3.12.0"},{"fix":"Users running `3.10.0` should immediately upgrade to `3.11.0` or later to avoid critical issues. Version `3.11.0` specifically addressed the problems in `3.10.0`.","message":"Pymodbus version `3.10.0` was officially marked as 'DO NOT USE THIS RELEASE it is broken' by the maintainers. It contained critical bugs that caused unexpected behavior.","severity":"gotcha","affected_versions":"3.10.0"},{"fix":"Explicitly specify `byteorder` and `wordorder` parameters when reading/writing registers, or when converting between registers and other data types, to match the device's expectations. Common options are `Endian.Big` and `Endian.Little`.","message":"Incorrect byte or word order is a common issue when communicating with Modbus devices from different manufacturers. Pymodbus defaults to certain orders, but devices may expect others (e.g., Big-Endian vs. Little-Endian, or swapped words).","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}