寻求指导:如何用Python模拟正泰DDSU666电能表(Modbus RTU)
模拟正泰DDSU666电能表(Modbus RTU从站)方案与调试指导
问题背景
我已经能用Python读取正泰DDSU666电能表的数据,该电表通过Modbus RTU和光伏逆变器通信。现在想模拟这个电表,通过自定义寄存器数据来调节逆变器的充放电功率。
读取电表的代码如下:
import minimalmodbus import struct # The minimalmodbus library uses RTU mode for Modbus communication. # You will need to set up the serial port and baud rate to match your energy meter's settings. instrument1 = minimalmodbus.Instrument('com4',1) instrument1.serial.baudrate = 9600 instrument1.close_port_after_each_call = True # Set the slave address of your energy meter instrument1.address = 0x01 while True: try: ##Chint Meter DDSU666 spanning = instrument1.read_float(0x2000, 3, 2) freq = instrument1.read_float(0x200e, 3, 2) stroom = instrument1.read_float(0x2002, 3, 2) vermogen = instrument1.read_float(0x2004, 3, 2) baurdrate = instrument1.read_register(0x000c, 0) print(spanning) print(freq) print(stroom) print(vermogen) print(baurdrate) except: print('communication lost')
我尝试搭建Modbus RTU从站让逆变器读取自定义寄存器,先用pymodbus3.1没成功,换modbus_tk写了代码但无法正常运行:
import minimalmodbus import struct import pymodbus import asyncio import modbus_tk import modbus_tk.defines as cst from modbus_tk import modbus_rtu import serial import time modbusserver = modbus_rtu.RtuServer(serial.Serial('com4'),baudrate = 9600, bytesize=8,parity = 'N', stopbits=1, xonxoff=0) print('start') modbusserver.start() slave1 = modbusserver.add_slave(1) slave1.add_block('spanning',cst.HOLDING_REGISTERS,0x2000,250) slave1.add_block('freq',cst.HOLDING_REGISTERS,0x200e,50) slave1.add_block('vermogen',cst.HOLDING_REGISTERS,0x2002,3000)
需要指导:实现模拟该电能表的合适方案是什么?如何调试当前代码?
一、合适的实现方案
方案1:基于modbus_tk完善实现
modbus_tk是成熟的Modbus RTU从站实现库,适配你的场景,核心要注意两点:
- 严格匹配DDSU666的寄存器规格:电压(0x2000)、电流(0x2002)、功率(0x2004)、频率(0x200E)都是32位浮点型,对应Modbus的2个连续保持寄存器(单寄存器为16位)。
- 确保串口资源独占,且服务器持续运行不退出。
方案2:改用pymodbus同步RTU服务器
如果modbus_tk调试受阻,pymodbus的同步RTU从站是可靠替代选项,它的文档更完善,社区支持更广泛,适合快速搭建稳定的Modbus从站。
二、当前modbus_tk代码的调试与修复
你的代码存在几个关键问题,修复后即可正常运行:
1. 串口资源与参数修正
- 确认
com4未被其他程序(比如之前的电表读取代码)占用,否则会导致串口打开失败。 - 初始化
serial.Serial时添加超时参数,避免无响应阻塞:ser = serial.Serial( port='com4', baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0, timeout=1 # 添加超时防止阻塞 ) modbusserver = modbus_rtu.RtuServer(ser)
2. 寄存器块定义错误
add_block的第四个参数是寄存器数量,不是初始值:
- 32位浮点寄存器需要占用2个连续的16位寄存器,所以所有浮点型参数的寄存器数量都应设为2,而非你写的250、50等数值。
- 正确的块定义:
slave1 = modbusserver.add_slave(1) # 电压:起始地址0x2000,2个寄存器(对应32位浮点) slave1.add_block('spanning', cst.HOLDING_REGISTERS, 0x2000, 2) # 频率:起始地址0x200E,2个寄存器 slave1.add_block('freq', cst.HOLDING_REGISTERS, 0x200E, 2) # 电流:起始地址0x2002,2个寄存器 slave1.add_block('stroom', cst.HOLDING_REGISTERS, 0x2002, 2) # 功率:起始地址0x2004,2个寄存器 slave1.add_block('vermogen', cst.HOLDING_REGISTERS, 0x2004, 2) # 波特率寄存器:0x000C,1个寄存器 slave1.add_block('baudrate', cst.HOLDING_REGISTERS, 0x000C, 1)
3. 设置寄存器初始值
添加块后,需要将浮点值转换为Modbus兼容的16位寄存器值(大端模式,和DDSU666一致),并设置初始值:
import struct def float_to_registers(value): # 大端模式打包32位浮点,拆分为两个16位寄存器 packed = struct.pack('>f', value) reg1 = struct.unpack('>H', packed[:2])[0] reg2 = struct.unpack('>H', packed[2:])[0] return [reg1, reg2] # 设置初始模拟值 slave1.set_values('spanning', 0x2000, float_to_registers(220.0)) slave1.set_values('freq', 0x200E, float_to_registers(50.0)) slave1.set_values('stroom', 0x2002, float_to_registers(5.0)) slave1.set_values('vermogen', 0x2004, float_to_registers(1100.0)) slave1.set_values('baudrate', 0x000C, [9600])
4. 保持服务器持续运行
原代码启动服务器后直接退出,需要添加循环维持运行:
try: print('Modbus RTU Slave started on com4') while True: time.sleep(1) except KeyboardInterrupt: print('Stopping server...') modbusserver.stop() ser.close()
5. 分步调试步骤
- 串口连通性测试:用串口助手监听
com4,手动发送Modbus读取指令(比如读取0x2000寄存器),检查是否有响应。 - 本地读取测试:关闭逆变器,运行原电表读取代码,验证是否能读取到你设置的初始值,确认从站功能正常。
- 逆变器连接测试:确保逆变器的Modbus参数(从站地址1、波特率9600、奇偶校验N、停止位1)与从站完全一致,再连接串口测试充放电调节。
三、完整修复后的modbus_tk代码示例
import modbus_tk import modbus_tk.defines as cst from modbus_tk import modbus_rtu import serial import time import struct def float_to_registers(value): # 大端模式打包32位浮点,拆分两个16位寄存器 packed = struct.pack('>f', value) reg1 = struct.unpack('>H', packed[:2])[0] reg2 = struct.unpack('>H', packed[2:])[0] return [reg1, reg2] # 初始化串口 ser = serial.Serial( port='com4', baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0, timeout=1 ) # 启动Modbus RTU从站 modbusserver = modbus_rtu.RtuServer(ser) modbusserver.start() # 添加从站(地址1,和DDSU666一致) slave1 = modbusserver.add_slave(1) # 定义寄存器块:地址、寄存器数量 slave1.add_block('spanning', cst.HOLDING_REGISTERS, 0x2000, 2) slave1.add_block('freq', cst.HOLDING_REGISTERS, 0x200E, 2) slave1.add_block('stroom', cst.HOLDING_REGISTERS, 0x2002, 2) slave1.add_block('vermogen', cst.HOLDING_REGISTERS, 0x2004, 2) slave1.add_block('baudrate', cst.HOLDING_REGISTERS, 0x000C, 1) # 设置初始值 slave1.set_values('spanning', 0x2000, float_to_registers(220.0)) slave1.set_values('freq', 0x200E, float_to_registers(50.0)) slave1.set_values('stroom', 0x2002, float_to_registers(5.0)) slave1.set_values('vermogen', 0x2004, float_to_registers(1100.0)) slave1.set_values('baudrate', 0x000C, [9600]) print('Modbus RTU Slave running... Press Ctrl+C to stop') try: while True: time.sleep(1) # 可选:动态修改寄存器值,比如每隔5秒调整功率 # current_power = struct.unpack('>f', struct.pack('>HH', *slave1.get_values('vermogen', 0x2004, 2)))[0] # slave1.set_values('vermogen', 0x2004, float_to_registers(current_power + 100)) except KeyboardInterrupt: print('Stopping server...') modbusserver.stop() ser.close()
内容的提问来源于stack exchange,提问作者Baptist Vermeulen
相关产品推荐
相关产品推荐

