必须使用 asyncmodbustcpclient 等异步客户端类;旧版 pymodbus 同步 client 类不支持 asyncio,需升级至 pymodbus 3.0+ 并采用对应异步接口。

asyncio 里用 pymodbus 必须换 client 类
旧版 pymodbus(ModbusClient 会阻塞整个 event loop。3.0+ 把同步和异步 client 彻底拆开,异步必须用 AsyncModbusTcpClient、AsyncModbusSerialClient 这类带 Async 前缀的类,否则一跑就报 RuntimeWarning: coroutine 'xxx' was never awaited。
常见错误现象:写了个 await client.read_holding_registers(...),但 client 是 ModbusTcpClient 实例 —— 这个方法根本不是协程,只是普通函数,await 它等于白等,还可能静默失败。
- TCP 场景用
AsyncModbusTcpClient,串口用AsyncModbusSerialClient(注意串口异步依赖pyserial-asyncio) - 初始化 client 后必须
await client.connect(),不能只实例化就发请求 -
connect()和所有读写方法(如read_coils())都得 await,漏一个就会卡住或报错
串口异步 client 需要额外装依赖且路径写法不同
AsyncModbusSerialClient 不是纯 Python 实现,底层靠 pyserial-asyncio 桥接,不装它会直接 ImportError: No module named 'serial_asyncio'。而且 Windows 和 Linux 下串口路径习惯不同,硬编码 /dev/ttyUSB0 或 COM3 容易在部署时崩。
- 执行
pip install pyserial-asyncio(注意不是pyserial本身) - Windows 下串口名是
COMx(如COM4),Linux/macOS 是/dev/tty*(如/dev/ttyACM0) - 建议用
async def find_serial_port()扫描可用端口,别写死
timeout 参数在异步 client 里行为更敏感
同步 client 的 timeout 是阻塞等待上限,异步 client 的 timeout 本质是 asyncio.wait_for() 的超时,一旦触发就是 asyncio.TimeoutError,不会退化成重试或静默忽略。而且这个 timeout 是针对单次 I/O 操作(比如一次 read),不是整个 session。
立即学习“Python免费学习笔记(深入)”;
- 默认
timeout=3太短,工业现场 Modbus 响应常在 100–500ms,建议设为timeout=2.0起步 - 如果设备偶尔响应慢,别指望 client 自动重试 —— 得自己包一层
asyncio.sleep()+ 循环逻辑 - 串口 client 的
timeout还受系统串口缓冲区影响,Linux 下可能需调stty -F /dev/ttyX raw
并发读多个地址时别直接 await 多个请求
Modbus 协议本身不支持多地址并行读(不像 HTTP/2),物理上是一问一答。用 asyncio.gather() 并发发多个 read_holding_registers() 请求,实际还是串行走线缆,但会显著增加异常概率:超时、CRC 错、从站忙返回异常码 0x04(Slave Device Failure)。
- 真要读分散地址,优先合并成连续区间,用一次
read_holding_registers(address=100, count=50) - 非得并发时,加
asyncio.Semaphore(1)强制串行化 Modbus 请求 - 别用
asyncio.create_task()随意启后台任务 —— 从站通常无状态,乱序响应会导致数据错位
Modbus 异步真正的难点不在语法,而在协议层的串行约束和工业现场的不可靠性。async/await 写得再漂亮,遇到噪声干扰或从站固件 bug,照样丢帧、超时、数据错位。留足 timeout、做地址合并、加简单重试逻辑,比追求“高并发”实在得多。










