3. Modbus TCP客户端通信

Modbus TCP客户端通信 学习卡片

任务目标

在网关设备上实现一个Modbus TCP客户端,能够主动连接远程Modbus TCP服务器,完成保持寄存器(Holding Register)的读取与写入操作,验证网关与远程服务器之间的数据交换链路。

环境与依赖

  • 操作系统:Linux(网关标准环境,如Buildroot/Yocto)
  • Python版本:3.6+
  • 依赖库:pymodbus(安装命令:pip install pymodbus)
  • 网络:网关与远程服务器之间TCP 502端口可达

实现方案

使用Python的pymodbus库实现客户端。核心逻辑包含三个部分:建立TCP连接、读取保持寄存器、写入保持寄存器。代码采用同步客户端模式,便于在网关启动脚本或定时任务中直接调用。服务器地址与端口通过命令行参数传入,便于部署时灵活配置。

关键完整代码


#!/usr/bin/env python3
"""Modbus TCP客户端 - 读取/写入保持寄存器"""
import argparse
import logging
from pymodbus.client import ModbusTcpClient

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
log = logging.getLogger("modbus_client")

def read_holding_registers(client, address, count):
    """读取保持寄存器"""
    result = client.read_holding_registers(address, count, slave=1)
    if result.isError():
        raise RuntimeError(f"读取失败: {result}")
    return result.registers

def write_holding_register(client, address, value):
    """写入单个保持寄存器"""
    result = client.write_register(address, value, slave=1)
    if result.isError():
        raise RuntimeError(f"写入失败: {result}")
    log.info("写入成功: 地址=%d, 值=%d", address, value)

def main():
    parser = argparse.ArgumentParser(description="Modbus TCP客户端")
    parser.add_argument("--host", required=True, help="Modbus服务器IP")
    parser.add_argument("--port", type=int, default=502, help="Modbus端口")
    parser.add_argument("--read-addr", type=int, default=0, help="读取起始地址")
    parser.add_argument("--read-count", type=int, default=2, help="读取寄存器数量")
    parser.add_argument("--write-addr", type=int, help="写入地址")
    parser.add_argument("--write-value", type=int, help="写入值")
    args = parser.parse_args()

    client = ModbusTcpClient(args.host, port=args.port, timeout=5)
    if not client.connect():
        log.error("无法连接服务器 %s:%d", args.host, args.port)
        return 1

    try:
        # 执行写入操作(如果指定了写入参数)
        if args.write_addr is not None and args.write_value is not None:
            write_holding_register(client, args.write_addr, args.write_value)

        # 执行读取操作
        registers = read_holding_registers(client, args.read_addr, args.read_count)
        log.info("读取寄存器[%d-%d]: %s", args.read_addr, args.read_addr + args.read_count - 1, registers)
        print(f"READ_RESULT: {registers}")
    except Exception as e:
        log.error("操作异常: %s", e)
        return 1
    finally:
        client.close()
    return 0

if __name__ == "__main__":
    exit(main())

运行方式


# 1. 安装依赖
pip install pymodbus

# 2. 读取操作(读取服务器地址0x00开始的2个寄存器)
python3 modbus_client.py --host 192.168.1.100 --read-addr 0 --read-count 2

# 3. 写入操作(向地址0x10写入值1234)
python3 modbus_client.py --host 192.168.1.100 --write-addr 16 --write-value 1234

# 4. 组合操作(先写入再读取验证)
python3 modbus_client.py --host 192.168.1.100 --write-addr 16 --write-value 1234 --read-addr 16 --read-count 1

验证方法

  • 连通性验证:若服务器不可达,程序输出无法连接服务器并返回退出码1;连接成功则继续执行后续操作。
  • 读取验证:正常执行时输出READ_RESULT: [值1, 值2, ...],寄存器数量与--read-count一致。
  • 写入验证:日志显示写入成功: 地址=16, 值=1234。若服务器端有监控工具(如Modbus Poll),可观察到寄存器值已更新。
  • 闭环验证:使用组合操作命令,先写入已知值再读回,若输出值与写入值一致,则证明数据交换链路完整可用。

常见问题

  • 连接超时:确认服务器IP/端口正确,且网关到服务器的TCP 502端口未被防火墙拦截。
  • 从站地址不匹配:代码中固定使用slave=1,若服务器配置的从站地址不同,需修改代码中的slave参数。
  • 寄存器地址越界:读取或写入地址超出服务器映射范围时,pymodbus会返回异常错误,程序将打印错误信息并退出。