构建Modbus TCP写多个寄存器指令详解
在Modbus TCP通信中,构建正确的指令对于实现设备间的数据交换至关重要。本文将详细解释如何构建一个Modbus TCP指令,用于向设备地址为1的从站,从地址200(0xC8)开始,连续写入8个寄存器,其中前4个寄存器写入值1,后4个寄存器写入值2。
一、Modbus TCP帧结构
Modbus TCP帧由两部分组成:
- MBAP头(Modbus Application Protocol Header):7个字节
- PDU(Protocol Data Unit):功能码及其相关数据
1.1 MBAP头结构
字节序号 | 字段名称 | 长度 | 描述 |
---|---|---|---|
0-1 | 事务标识符 | 2 | 匹配请求与响应的唯一标识符 |
2-3 | 协议标识符 | 2 | 固定为0x0000,表示使用Modbus协议 |
4-5 | 长度字段 | 2 | 后续PDU部分的字节数,包括单元标识符 |
6 | 单元标识符 | 1 | 从设备的地址(这里为1) |
1.2 PDU结构
字节序号 | 字段名称 | 长度 | 描述 |
---|---|---|---|
0 | 功能码 | 1 | 指示操作类型(写多个寄存器为0x10) |
1-2 | 起始地址 | 2 | 要写入的第一个寄存器地址(0x00C8) |
3-4 | 寄存器数量 | 2 | 要写入的寄存器数量(8个) |
5 | 字节计数 | 1 | 后续数据部分的字节数(8个寄存器 × 2 = 16) |
6-21 | 寄存器值 | 16 | 要写入的寄存器值,每个寄存器2字节 |
二、构建指令步骤
2.1 确定各字段值
- 事务标识符(Transaction Identifier):任意选择一个唯一值,例如0x0001
- 协议标识符(Protocol Identifier):固定为0x0000
- 长度字段(Length Field):
- PDU部分包括:单元标识符(1) + 功能码(1) + 起始地址(2) + 寄存器数量(2) + 字节计数(1) + 寄存器值(16) = 23字节
- 因此,长度字段为0x0017(23)
- 单元标识符(Unit Identifier):1(目标从设备地址)
- 功能码(Function Code):0x10(写多个寄存器)
- 起始地址(Starting Address):0x00C8(200)
- 寄存器数量(Quantity of Registers):0x0008(8)
- 字节计数(Byte Count):0x10(16)
- 寄存器值(Register Values):
- 前4个寄存器值:0x0001, 0x0001, 0x0001, 0x0001
- 后4个寄存器值:0x0002, 0x0002, 0x0002, 0x0002
2.2 构建完整指令
将上述字段按顺序排列,得到完整的Modbus TCP指令:
[0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x01,0x10, 0x00, 0xC8, 0x00, 0x08, 0x10,0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02]
2.3 字节分解
字节序号 | 字节值 | 描述 |
---|---|---|
0-1 | 0x00 0x01 | 事务标识符(1) |
2-3 | 0x00 0x00 | 协议标识符(0) |
4-5 | 0x00 0x17 | 长度字段(23) |
6 | 0x01 | 单元标识符(从设备地址1) |
7 | 0x10 | 功能码(写多个寄存器) |
8-9 | 0x00 0xC8 | 起始地址(200) |
10-11 | 0x00 0x08 | 寄存器数量(8) |
12 | 0x10 | 字节计数(16) |
13-28 | 0x00 0x01, 0x00 0x01, 0x00 0x01, 0x00 0x01, 0x00 0x02, 0x00 0x02, 0x00 0x02, 0x00 0x02 | 寄存器值(前4个为1,后4个为2) |
三、示例代码实现
以下是使用Python和pymodbus
库构建并发送上述Modbus TCP指令的示例代码:
3.1 安装pymodbus
库
首先,确保已安装pymodbus
库:
pip install pymodbus
3.2 实现Modbus TCP客户端
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.constants import Endian# 创建Modbus TCP客户端并连接到服务器
client = ModbusTcpClient('192.168.1.100', port=502) # 替换为实际服务器IP和端口
connection = client.connect()
if connection:print("连接成功")# 构建寄存器值:前4个寄存器为1,后4个寄存器为2builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)for _ in range(4):builder.add_16bit_uint(1)for _ in range(4):builder.add_16bit_uint(2)payload = builder.to_registers()# 执行写多个寄存器操作starting_address = 200 # 起始地址register_values = payload # 寄存器值列表result = client.write_registers(address=starting_address, values=register_values, unit=1)if not result.isError():print("写入成功")else:print("写入失败:", result)# 关闭连接client.close()
else:print("连接失败")
代码解析:
- 连接服务器:使用
ModbusTcpClient
连接到目标Modbus TCP服务器。 - 构建寄存器值:使用
BinaryPayloadBuilder
按照大端字节序(Endian.Big)依次添加寄存器值。 - 写入寄存器:调用
write_registers
方法,指定起始地址、寄存器值列表和从设备地址(1)。 - 处理响应:检查写入操作是否成功。
- 关闭连接。
3.3 构建手动指令
如果需要手动构建上述Modbus TCP指令,可以参考以下Python代码:
def build_modbus_tcp_write_multiple_registers(transaction_id, unit_id, start_address, register_values):protocol_id = 0x0000function_code = 0x10quantity = len(register_values)byte_count = quantity * 2# MBAP头mbap = [(transaction_id >> 8) & 0xFF,transaction_id & 0xFF,(protocol_id >> 8) & 0xFF,protocol_id & 0xFF,((6 + byte_count) >> 8) & 0xFF, # Length high byte(6 + byte_count) & 0xFF, # Length low byteunit_id]# PDUpdu = [function_code,(start_address >> 8) & 0xFF,start_address & 0xFF,(quantity >> 8) & 0xFF,quantity & 0xFF,byte_count]# 寄存器值for value in register_values:pdu.append((value >> 8) & 0xFF)pdu.append(value & 0xFF)# 合并MBAP头和PDUmessage = mbap + pdureturn message# 示例参数
transaction_id = 1
unit_id = 1
start_address = 200
register_values = [1, 1, 1, 1, 2, 2, 2, 2]# 构建指令
instruction = build_modbus_tcp_write_multiple_registers(transaction_id, unit_id, start_address, register_values)
print("Modbus TCP指令:", [hex(byte) for byte in instruction])
输出结果:
Modbus TCP指令: ['0x0', '0x1', '0x0', '0x0', '0x0', '0x17', '0x1', '0x10', '0xc8', '0x0', '0x8', '0x10', '0x0', '0x1', '0x0', '0x1', '0x0', '0x1', '0x0', '0x1', '0x0', '0x2', '0x0', '0x2', '0x0', '0x2', '0x0', '0x2']
这与前述手动构建的指令一致。
四、总结
通过上述步骤,我们成功构建了一个Modbus TCP指令,用于向从设备地址为1的从站,从地址200开始,连续写入8个寄存器,其中前4个寄存器写入值1,后4个寄存器写入值2。确保每个字段的正确性,特别是MBAP头中的长度字段,是实现成功通信的关键。
在实际应用中,推荐使用现有的Modbus库(如pymodbus
)来简化指令的构建和发送过程,减少手动错误。同时,使用网络抓包工具(如Wireshark)可以帮助调试和验证Modbus TCP通信的正确性。