ModBus-TCP学习
"这段代码是一个 Modbus TCP..."点击查看元宝的回答
https://yuanbao.tencent.com/bot/app/share/chat/iCeAQyDSNe0G
1. Modbus TCP 报文头部格式(共 6 字节)
(1) 为什么是 6 字节?
Modbus TCP 的报文头部 固定为 6 字节,结构如下:
字段 | 长度 | 说明 |
---|---|---|
Transaction ID | 2 字节 | 事务标识符(由客户端生成,服务器原样返回) |
Protocol ID | 2 字节 | Modbus TCP 固定为 |
Length | 2 字节 | 后续数据的字节数(从 Unit ID 开始计算) |
Unit ID | 1 字节 | 设备地址(Slave ID) |
Function Code | 1 字节 | 功能码(如 |
总长度 = 2 + 2 + 2 = 6 字节(Length 之后的数据属于“应用数据单元”,不计入头部)。
(2) 头部是固定的吗?
-
前 6 字节(头部)格式固定,所有 Modbus TCP 报文必须包含这 6 字节。
-
后续数据(应用数据单元) 根据功能码变化(如读/写寄存器的数据不同)。
2. 为什么读寄存器响应的 Length = 寄存器数量 × 2 + 3?
(1) 读寄存器响应的结构
以 读保持寄存器(0x03) 为例,服务器响应报文格式如下:
字段 | 长度 | 说明 |
---|---|---|
Transaction ID | 2 字节 | 原样返回客户端的值 |
Protocol ID | 2 字节 |
|
Length | 2 字节 | 后续数据的字节数(计算如下) |
Unit ID | 1 字节 | 设备地址 |
Function Code | 1 字节 |
|
Byte Count | 1 字节 | 寄存器数据的总字节数(= 寄存器数量 × 2) |
Register Data | N × 2 字节 | 实际寄存器值(每个值占 2 字节) |
(2) Length 的计算
-
Length 字段的值 = 从 Unit ID 开始到报文末尾的字节数。
-
对于读寄存器响应,这部分包括:
-
Unit ID(1 字节)
-
Function Code(1 字节)
-
Byte Count(1 字节)
-
Register Data(寄存器数量 × 2 字节)
-
因此:
Length = 1 (Unit ID) + 1 (Function Code) + 1 (Byte Count) + N × 2 (Register Data)= 3 + N × 2
即:Length = 寄存器数量 × 2 + 3
(3) 示例
假设客户端请求读取 5 个保持寄存器,服务器响应如下:
Transaction ID: 0x0001 (2 字节)
Protocol ID: 0x0000 (2 字节)
Length: 0x000D (13 字节,计算:5 × 2 + 3 = 13)
Unit ID: 0x01 (1 字节)
Function Code: 0x03 (1 字节)
Byte Count: 0x0A (10 字节,5 寄存器 × 2)
Register Data: [0x1234, 0x5678, 0x9ABC, 0xDEF0, 0x1111] (10 字节)
实际报文(十六进制):
00 01 00 00 00 0D 01 03 0A 12 34 56 78 9A BC DE F0 11 11
3. 关键总结
问题 | 答案 |
---|---|
Modbus TCP 头部为什么是 6 字节? | 固定包含 Transaction ID(2)、Protocol ID(2)、Length(2)。 |
Length 为什么是 寄存器数量 × 2 + 3? | 3 = Unit ID + Function Code + Byte Count;N × 2 = 寄存器数据。 |
字节序规则 | Modbus TCP 规定所有多字节数据按大端序传输(高字节在前)。 |
4. 常见疑问
(1) 为什么 Length 不包含头部?
-
Modbus TCP 的 Length 字段仅统计应用数据单元(从 Unit ID 开始),这是协议标准定义。
-
头部(前 6 字节)用于协议控制,不计入 Length。
(2) 其他功能码的 Length 计算
-
写单个寄存器(0x06):Length = 6(固定,包含 Unit ID、Function Code、寄存器地址和值)。
-
写多个寄存器(0x10):Length = 6 + 字节数(类似读寄存器,但结构不同)。
5. 参考
-
Modbus TCP 协议标准:见 Modbus.org的 MODBUS Messaging on TCP/IP Implementation Guide。
-
调试工具:使用 Modbus 调试工具(如 Modbus Poll)抓包验证报文格式。
1. Modbus TCP 写单个寄存器(功能码 0x06
)的响应结构
当服务器成功处理写单个寄存器请求后,其响应报文的结构如下:
字段 | 长度 | 说明 |
---|---|---|
Transaction ID | 2 字节 | 由客户端生成,服务器原样返回 |
Protocol ID | 2 字节 | 固定为 |
Length | 2 字节 | 后续数据的字节数(固定为 |
Unit ID | 1 字节 | 设备地址(Slave ID) |
Function Code | 1 字节 |
|
Register Address | 2 字节 | 写入的寄存器地址(原样返回) |
Register Value | 2 字节 | 写入的寄存器值(原样返回) |
写单个寄存器(0x06):Length = 6(固定,包含 Unit ID、Function Code、寄存器地址和值)。为什么为6
2. 为什么 Length = 6?
(1) Length 的计算范围
Length 字段表示 从 Unit ID 开始到报文末尾的字节数。对于写单个寄存器响应,这部分包括:
-
Unit ID(1 字节)
-
Function Code(1 字节)
-
Register Address(2 字节)
-
Register Value(2 字节)
因此:
Length = 1 (Unit ID) + 1 (Function Code) + 2 (Register Address) + 2 (Register Value) = 6 字节
(2) 固定长度的原因
-
写单个寄存器的响应 是 对请求的确认,需原样返回客户端发送的寄存器地址和值。
-
协议规定该功能码的响应长度固定为 6 字节(无需动态计算)。
3. 示例分析
假设客户端发送以下写单个寄存器请求:
写入地址:0x0002(寄存器地址 2)
写入值:0x1234
服务器响应报文如下(十六进制):
Transaction ID: 0x0001 // 客户端生成,服务器返回
Protocol ID: 0x0000 // 固定
Length: 0x0006 // 固定为 6
Unit ID: 0x01 // 设备地址
Function Code: 0x06 // 写单个寄存器
Register Addr: 0x0002 // 原样返回地址
Register Value: 0x1234 // 原样返回值
实际报文(十六进制):
00 01 00 00 00 06 01 06 00 02 12 34
4. 对比其他功能码
功能码 | Length 计算 | 说明 |
---|---|---|
读寄存器(0x03/0x04) |
| 动态长度(N=寄存器数量) |
写单个寄存器(0x06) |
| 原样返回地址和值 |
写多个寄存器(0x10) |
| 返回起始地址和寄存器数量 |
5. 关键总结
问题 | 答案 |
---|---|
为什么 Length = 6? | 包含 Unit ID(1)、Function Code(1)、Register Address(2)、Register Value(2)。 |
是否可变? | 固定为 6,与寄存器数量无关(与读寄存器不同)。 |
协议规定 | 见 Modbus TCP 标准文档(Section 6.1.2)。 |
6. 调试建议
-
使用 Modbus Poll 或 Wireshark 抓包,观察写单个寄存器的请求和响应报文。
-
验证 Length 字段是否为
0x0006
,并检查返回的地址和值是否与请求一致。