PLC通信中内存位与数据寄存器传递及pymodbus读写故障排查
Modbus RTU 通信失败排查与正确配置方法
核心配置错误修正
首先逐一修正现有配置和代码里的硬错误:
- 超时参数错误:原配置里超时时间设为0是完全错误的,Modbus RTU为请求-响应式通信,客户端必须预留等待PLC返回数据的时间,将超时设置为
timeout=1(单位秒,可根据总线长度微调,0.5~3s均为合理值)即可。 - 寄存器地址越界+映射错误:你代码里读写的地址
0x1017(十进制4119)远超出你声明的D0~D100寄存器范围,必然无法读写。pymodbus中传入的address参数是Modbus协议层从0开始的偏移地址,和PLC原生的D、M区地址的对应关系由PLC厂商的Modbus映射规则决定,常规通用映射规则为:- 保持寄存器(功能码03/06/16)对应D数据寄存器:D0对应
address=0,D1对应address=1,以此类推D100对应address=100 - 线圈(功能码01/05/15)对应M内存位:M0对应
address=0,M1对应address=1,以此类推M100对应address=100
注意:如果是特定品牌PLC(如三菱Q系列、欧姆龙CP系列),请以PLC编程手册里标注的Modbus映射地址为准,不要直接套用通用规则
- 保持寄存器(功能码03/06/16)对应D数据寄存器:D0对应
- 串口参数双向核对:不要只在Python端配置参数,必须进入PLC的编程配置页面,确认PLC侧串口参数和代码完全一致:RTU模式、8数据位、无校验、1停止位、9600波特率、从站地址1,任意参数不匹配都会导致通信完全失败。另外要注意COM口不能被PLC编程软件、串口调试助手等其他程序占用,否则
client.connect()会直接返回失败。 - API方法调用错误:写单个寄存器请用
write_register()方法,原代码用的write_registers()是批量写多个寄存器的方法,部分旧版本pymodbus对单值传入的兼容性有问题。 - 缺少异常判断:原代码直接访问返回值的
registers属性,若通信失败返回的是异常对象,会直接触发属性错误,必须先判断响应是否正常。
修正后的可运行参考代码
from pymodbus.client.sync import ModbusSerialClient from pymodbus.exceptions import ModbusIOException # 初始化客户端,补全超时参数,所有串口参数和PLC侧严格对齐 client = ModbusSerialClient( method="rtu", port="COM5", stopbits=1, bytesize=8, parity="N", baudrate=9600, timeout=1 # 必须设置非0超时 ) # 检查串口连接状态 conn_status = client.connect() if not conn_status: print("串口连接失败,请检查串口号、驱动、端口占用情况") exit() # -------------------------- # D寄存器(保持寄存器)读写示例:读写D0、D1两个寄存器 # -------------------------- # 读D0开始的2个寄存器(对应D0、D1) read_d_res = client.read_holding_registers(address=0, count=2, unit=1) if not isinstance(read_d_res, ModbusIOException): print("D0、D1寄存器值:", read_d_res.registers) else: print("读D寄存器失败:", read_d_res) # 写单个寄存器D0,写入值445 write_d_res = client.write_register(address=0, value=445, unit=1) if not isinstance(write_d_res, ModbusIOException): print("写D0寄存器成功") else: print("写D寄存器失败:", write_d_res) # -------------------------- # M内存位(线圈)读写示例:读写M1、M2两个位 # -------------------------- # 读M1开始的2个位(对应M1、M2) read_m_res = client.read_coils(address=1, count=2, unit=1) if not isinstance(read_m_res, ModbusIOException): print("M1、M2位状态:", read_m_res.bits) else: print("读M位失败:", read_m_res) # 写单个位M1,写入值True(导通) write_m_res = client.write_coil(address=1, value=True, unit=1) if not isinstance(write_m_res, ModbusIOException): print("写M1位成功") else: print("写M位失败:", write_m_res) # 通信结束关闭连接 client.close()
额外排查点
如果按上述配置后仍无法通信,可先用串口调试助手接在485总线上抓包,确认Python端是否正常发送请求报文、PLC是否返回响应:
- 正常读D0的请求报文为
01 03 00 00 00 02 C4 0B - 若PLC无任何报文返回,优先检查485的A/B线是否接反、终端电阻是否匹配、PLC站号是否配置正确
- 若PLC返回异常码(比如02非法地址),说明传入的地址和PLC实际映射不匹配,重新核对PLC手册里的Modbus地址表即可。
内容的提问来源于stack exchange,提问作者Pandurang Fatangade
相关产品推荐
相关产品推荐

