You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pymodbus请求Arduino从站保持寄存器值时遇ModbusIOException的解决方法

解决Pymodbus无法读取Arduino Modbus RTU从站数据的问题

我来帮你排查这个问题~你的Arduino从站能用QModMaster正常读取,说明硬件和从站逻辑没问题,问题大概率出在Pymodbus主站的配置或者代码细节上。下面是一步步的排查和解决方法:

1. 补全串口参数,确保和从站完全匹配

你的Arduino从站配置的是SERIAL_8N1(8数据位、无校验、1停止位),但原Python代码初始化ModbusSerialClient时,没有显式传入stopbits和bytesize参数。虽然Pymodbus默认值可能符合要求,但显式指定能避免环境差异导致的参数不匹配:

修改ModbusSerialClient初始化部分:

self.client = ModbusSerialClient(
    method='rtu',
    port=port,
    baudrate=baudrate,
    parity=parity,
    stopbits=stopbits,  # 新增:传递停止位参数
    bytesize=bytesize,  # 新增:传递数据位参数
    timeout=timeout
)

2. 添加串口初始化延迟

Arduino通过USB连接电脑时,上电后会有短暂的重启和串口初始化过程。如果在__init__里立刻执行读寄存器操作,可能串口还没准备好接收命令。可以在连接成功后加1秒左右的延迟:

import time  # 记得导入time模块

def __init__(self, graph_name, port, baudrate=9600, stopbits=1, bytesize=8, parity='N', timeout=1):
    # ... 其他代码 ...
    self.connection = self.client.connect()
    if self.connection:
        time.sleep(1)  # 等待Arduino串口稳定
        # 后续读寄存器操作

3. 完善错误处理逻辑

原代码直接访问result.registers,但如果读取失败(触发ModbusIOException),会直接抛出属性错误。一定要先判断结果是否正常:

result = self.client.read_holding_registers(address=0, count=2, unit=1)
if result.isError():
    print(f"读取失败: {result}")
else:
    print(result.registers)

4. 检查串口权限(仅Linux/macOS)

如果是Linux或macOS系统,/dev/ttyACM0可能需要用户加入特定组才能访问。执行以下命令(重启后生效):

sudo usermod -aG dialout $USER

5. 调整超时时间

如果Arduino从站响应较慢,可以尝试把timeout参数从1秒增加到2秒,确保有足够时间接收响应。

修改后的完整Python代码

import time
from pymodbus.client import ModbusSerialClient

class ModbusRTU:
    def __init__(self, graph_name, port, baudrate=9600, stopbits=1, bytesize=8, parity='N', timeout=2):
        self.graph_name = graph_name
        self.client = ModbusSerialClient(
            method='rtu',
            port=port,
            baudrate=baudrate,
            parity=parity,
            stopbits=stopbits,
            bytesize=bytesize,
            timeout=timeout
        )
        self.connection = self.client.connect()
        if self.connection:
            time.sleep(1)
            result = self.client.read_holding_registers(address=0, count=2, unit=1)
            if not result.isError():
                print(result.registers)
            else:
                print(f"读取失败: {result}")
        else:
            print("串口连接失败")
    
    def check_connection(self):
        return self.connection

if __name__ == '__main__':
    modbus = ModbusRTU(
        graph_name='/dev/ttyACM0', 
        port='/dev/ttyACM0', 
        baudrate=9600, 
        stopbits=1, 
        bytesize=8, 
        parity='N', 
        timeout=2
    )
    print(modbus.check_connection())

额外验证步骤

  • 用串口助手监听/dev/ttyACM0,查看Pymodbus发送的命令和Arduino的响应是否正常,确认物理层没有问题。
  • 确保Arduino的串口没有被其他程序占用(比如Arduino IDE的串口监视器),否则Pymodbus无法正常读写。

内容的提问来源于stack exchange,提问作者kebaranas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:58:38