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

如何通过pymodbus的ModbusSerialClient批量读取多个保持寄存器?

解决Pymodbus批量读取保持寄存器的Illegal Address错误

你的问题我很熟悉——用Pymodbus的ModbusSerialClient逐个读寄存器没问题,但批量读就触发Illegal Address错误,核心问题出在两个地方:对read_holding_registers参数的误解,以及尝试访问设备无效的寄存器地址。

先分析你之前代码的问题

  1. 参数混淆:read_holding_registers的第二个参数是要读取的寄存器总数,不是地址跨度。你用add_max - add_min计算的是地址差,不是实际需要的寄存器数量。比如从3860到3926,地址差是66,但如果要覆盖到3926开始的2个寄存器(到3927),实际需要读68个寄存器(3927-3860+1)。
  2. 无效地址访问:你的寄存器列表是分散的(3900,3902,3904...),直接读取从最小到最大地址的连续范围,会包含很多设备未定义的寄存器地址(比如3862到3899、3901、3903等),设备检测到无效地址就会返回Illegal Address错误。
  3. 可能的单次读取超限:很多Modbus设备限制单次读取的寄存器数量(比如最多125个,有些设备更少),如果你的请求超出这个上限也会报错。

解决方案:分块读取连续有效寄存器

既然你的目标是提升性能,最合理的做法是把需要的寄存器整理成连续的有效块,每个块单独读取——这样既能减少通信次数(比逐个读快),又不会访问无效地址。

比如你的需求是读取这些起始地址的2个寄存器:3860、3900、3902、3904、3906、3926。可以把它们合并成3个连续块:

  • 3860-3861(起始3860,读2个)
  • 3900-3907(起始3900,读8个,覆盖3900、3902、3904、3906各自的2个寄存器)
  • 3926-3927(起始3926,读2个)

对应的代码实现:

from pymodbus.client import ModbusSerialClient

# 定义要读取的连续寄存器块(起始地址,读取数量)
register_blocks = [
    (3860, 2),
    (3900, 8),
    (3926, 2)
]

# 初始化客户端
client = ModbusSerialClient(
    method='rtu',
    port='/dev/ttyUSB0',
    timeout=1,
    parity='N',
    baudrate=19200,
    unit=1
)
client.connect()

# 存储所有寄存器结果,键为寄存器地址,值为对应值
all_registers = {}

for start_addr, count in register_blocks:
    response = client.read_holding_registers(start_addr, count, unit=0x01)
    if not response.isError():
        # 遍历读取到的寄存器,存入字典
        for offset in range(count):
            reg_addr = start_addr + offset
            all_registers[reg_addr] = response.registers[offset]
    else:
        print(f"读取块 [{start_addr}, {start_addr+count-1}] 失败: {response}")

client.close()

# 提取你需要的起始地址对应的寄存器值
target_starts = [3900, 3902, 3904, 3906, 3926, 3860]
for start in target_starts:
    val1 = all_registers.get(start)
    val2 = all_registers.get(start + 1)
    print(f"起始地址 {start} 的两个寄存器值: {val1}, {val2}")

如果设备允许读取全范围的替代方案

如果你确认设备支持读取从3860到3927的所有寄存器,也可以尝试一次性读取,但要计算正确的寄存器数量:

client = ModbusSerialClient(method='rtu', port='/dev/ttyUSB0', timeout=1, parity='N', baudrate=19200, unit=1)
client.connect()

min_start = 3860
max_end = 3926 + 1  # 最后一个起始地址读2个,结束地址是3927
total_regs = max_end - min_start + 1  # 计算总寄存器数量

response = client.read_holding_registers(min_start, total_regs, unit=0x01)
if not response.isError():
    target_starts = [3900, 3902, 3904, 3906, 3926, 3860]
    for start in target_starts:
        # 计算当前起始地址在结果中的索引
        idx = start - min_start
        print(f"起始地址 {start} 的两个寄存器值: {response.registers[idx]}, {response.registers[idx+1]}")
else:
    print(f"全范围读取失败: {response}")

注意:这个方案只有在设备所有中间地址都是有效寄存器时才会成功,否则还是会触发Illegal Address错误。

额外建议

  1. 一定要查看设备的Modbus手册,确认:
    • 有效的保持寄存器地址范围
    • 设备允许的单次最大读取寄存器数量
  2. 尽量优先选择分块读取的方式,既安全又能保证性能提升。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:30:08