2. Modbus TCP从站模拟器开发

任务目标

开发一个基于Python的Modbus TCP从站模拟器,模拟PLC的保持寄存器与线圈区域,供Modbus主站(如SCADA、上位机或调试工具)进行读写测试。从站启动后监听指定端口,响应主站的读写请求,并维护可动态修改的寄存器数据。

环境依赖

需要Python 3.8+,并安装pymodbus库(版本≥3.0):

pip install pymodbus

关键完整代码

以下代码实现了一个完整的Modbus TCP从站,模拟4个保持寄存器(地址0~3)和8个线圈(地址0~7)。从站启动后可通过主站工具(如Modbus Poll)或pymodbus客户端进行读写验证。

# modbus_slave.py
import logging
from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.datastore import ModbusSequentialDataBlock

# 配置日志(可选,便于观察请求)
logging.basicConfig(level=logging.INFO)

# 初始化数据存储:保持寄存器(3x)与线圈(0x)
# 保持寄存器地址0~3,初始值分别为 10, 20, 30, 40
# 线圈地址0~7,初始值分别为 1,0,1,0,1,0,1,0
store = ModbusSlaveContext(
    di=ModbusSequentialDataBlock(0, [0]*8),      # 离散输入(本案例不使用)
    co=ModbusSequentialDataBlock(0, [1,0,1,0,1,0,1,0]),  # 线圈
    hr=ModbusSequentialDataBlock(0, [10,20,30,40]),      # 保持寄存器
    ir=ModbusSequentialDataBlock(0, [0]*8)       # 输入寄存器(本案例不使用)
)
context = ModbusServerContext(slaves=store, single=True)

if __name__ == "__main__":
    # 启动TCP从站,监听502端口(需管理员权限)或改用5020等非特权端口
    print("Modbus TCP Slave 启动,监听端口 5020...")
    StartTcpServer(context=context, address=("0.0.0.0", 5020))

运行与验证

启动从站:

python modbus_slave.py

从站将监听本机5020端口(若需使用标准502端口,在Linux/macOS下用sudo运行,Windows下以管理员身份运行)。

验证方法(使用pymodbus客户端脚本):

# test_client.py
from pymodbus.client import ModbusTcpClient

client = ModbusTcpClient("127.0.0.1", port=5020)
assert client.connect()

# 读保持寄存器(地址0,读4个)
rr = client.read_holding_registers(0, 4)
print("保持寄存器:", rr.registers)  # 预期输出: [10, 20, 30, 40]

# 写保持寄存器(地址2,写值99)
client.write_register(2, 99)
rr = client.read_holding_registers(0, 4)
print("写后保持寄存器:", rr.registers)  # 预期输出: [10, 20, 99, 40]

# 读线圈(地址0,读8个)
cr = client.read_coils(0, 8)
print("线圈:", cr.bits)  # 预期输出: [True, False, True, False, True, False, True, False]

# 写单个线圈(地址1,写True)
client.write_coil(1, True)
cr = client.read_coils(0, 8)
print("写后线圈:", cr.bits)  # 预期输出: [True, True, True, False, True, False, True, False]

client.close()
print("验证通过")

运行客户端:

python test_client.py

通过条件:客户端脚本输出的寄存器与线圈值符合上述注释中的预期值,且无异常抛出。也可使用Modbus Poll等图形工具连接127.0.0.1:5020进行手动读写验证。

常见问题

  • 端口占用:若5020被占用,修改代码中address的端口号即可。
  • 权限问题:使用标准502端口时需管理员/root权限,否则监听失败。
  • 数据持久性:从站数据仅保存在内存中,重启后恢复初始值,适合测试场景。