使用pymodbus读取Modbus RTU从站保持寄存器失败求助
解决pymodbus RTU读取保持寄存器失败问题(对比minimalmodbus)
核心问题分析
你遇到的问题主要源于两个库在串口处理、寄存器读取逻辑上的细节差异:
- float读取的寄存器数量差异:minimalmodbus的
read_float默认读取2个连续16位寄存器(拼成32位float),但你的pymodbus代码只读取了1个寄存器,这会导致数据不完整或错误。 - 串口缓冲区与连接管理差异:minimalmodbus自动处理了端口关闭和缓冲区清空,pymodbus需要手动配置这些逻辑。
- 连接状态检查缺失:你的pymodbus代码没有验证
connect()的返回结果,直接执行读取操作,容易触发连接异常。
修复步骤与代码示例
以下是修正后的pymodbus代码,完全对齐minimalmodbus的行为:
from pymodbus.client import ModbusSerialClient from pymodbus.transaction import ModbusRtuFramer as ModbusFramer import struct PORT='COM5' A_REGISTER = 40320 B_REGISTER = 40321 OFFSET = 38001 # 计算实际寄存器地址(和minimalmodbus保持一致) A_REG_ADDR = A_REGISTER - OFFSET B_REG_ADDR = B_REGISTER - OFFSET client = ModbusSerialClient( framer=ModbusFramer, method='rtu', port=PORT, baudrate=19200, bytesize=8, parity="E", stopbits=1, retries=5, timeout=1 ) client.strict = False try: # 检查连接状态 if not client.connect(): raise Exception("Failed to connect to serial port") # 清空缓冲区(对齐minimalmodbus的clear_buffers_before_each_transaction) client.clear_buffers() # 读取float:需要读取2个连续寄存器(对应minimalmodbus的read_float) # 读取A寄存器(float) a_res = client.read_holding_registers(address=A_REG_ADDR, count=2, unit=1) if a_res.isError(): raise Exception(f"Failed to read A register: {a_res}") # 将两个16位寄存器转换为32位float(默认使用大端模式,和minimalmodbus一致) a_reg = struct.unpack('!f', struct.pack('!HH', a_res.registers[0], a_res.registers[1]))[0] # 读取B寄存器(float) b_res = client.read_holding_registers(address=B_REG_ADDR, count=2, unit=1) if b_res.isError(): raise Exception(f"Failed to read B register: {b_res}") b_reg = struct.unpack('!f', struct.pack('!HH', b_res.registers[0], b_res.registers[1]))[0] # 打印结果 print(f'The A register value is : {a_reg:.1f} deg C') print(f'The B register value is: {b_reg:.1f} percent') finally: # 每次操作后关闭端口(对齐minimalmodbus的close_port_after_each_call) client.close()
关键修改说明
- 寄存器数量调整:将
count=1改为count=2,因为float类型需要32位数据,对应两个16位保持寄存器,和minimalmodbus的read_float逻辑一致。 - 连接状态验证:增加
if not client.connect()的判断,避免在未连接成功时执行读取操作。 - 缓冲区清空:调用
client.clear_buffers(),清除串口缓冲区残留数据,对齐minimalmodbus的clear_buffers_before_each_transaction设置。 - 端口自动关闭:使用
try...finally确保操作完成后关闭串口,对齐minimalmodbus的close_port_after_each_call设置。 - float转换:使用
struct模块将两个16位寄存器按大端模式(!)打包后解包为float,和minimalmodbus默认的字节序一致。
额外排查建议
如果问题仍存在,可以尝试:
- 调整
timeout值为2秒,确保设备有足够时间响应。 - 检查串口设备是否被其他程序占用(比如minimalmodbus测试后是否释放了COM5)。
- 验证设备的Modbus字节序是否为大端,如果是小端,将
struct.unpack('!f'改为struct.unpack('<f'(小端)或'>f'(大端,和!等价)。
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

