自学内容网 自学内容网

解读Modbus TCP指令

解读Modbus TCP指令:[0x01, 0x00, 0x00, 0x00, 0x04, 0x06, 0x01, 0x10, 0x00, 0xC8, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x01]

在Modbus TCP通信中,数据以字节流的形式传输。理解和解析这些字节对于调试和开发至关重要。本文将详细解析给定的Modbus TCP指令,解释每个字节的含义以及整个指令的作用。

一、指令整体结构

给定的指令为17个字节:

[0x01, 0x00, 0x00, 0x00, 0x04, 0x06, 0x01, 0x10, 0x00, 0xC8, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x01]

按照Modbus TCP协议,数据帧通常由两个主要部分组成:

  1. MBAP头(Modbus Application Protocol Header):7个字节
  2. PDU(Protocol Data Unit):功能码及其相关数据

因此,我们可以将上述17个字节分为:

  • MBAP头:前7个字节 [0x01, 0x00, 0x00, 0x00, 0x04, 0x06, 0x01]
  • PDU:后10个字节 [0x10, 0x00, 0xC8, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x01]

二、MBAP头解析

MBAP头由7个字节组成,其结构如下:

字节序号字段名称长度描述
0-1事务标识符2用于匹配请求与响应的标识符
2-3协议标识符2标识使用的协议,Modbus TCP为0x0000
4-5长度字段2后续PDU部分的字节数,包括单元标识符
6单元标识符1标识从设备的地址(类似于Modbus RTU的从地址)

1. 事务标识符(Transaction Identifier)

  • 字节0x01, 0x00

  • 解释:将两个字节组合成一个16位的整数,采用大端字节序。

    0x01 << 8 + 0x00 = 256
    
  • 作用:客户端发送请求时生成一个唯一的事务标识符,服务器在响应中返回相同的标识符,用于匹配请求与响应。

2. 协议标识符(Protocol Identifier)

  • 字节0x00, 0x00

  • 解释:固定为0x0000,表示使用的是Modbus协议。

    0x00 << 8 + 0x00 = 0
    
  • 作用:用于区分不同的协议,Modbus TCP始终为0。

3. 长度字段(Length Field)

  • 字节0x04, 0x06

  • 解释:表示后续PDU部分的字节数,包括单元标识符。

    长度 = 0x04 << 8 + 0x06 = 1030
    
  • 注意:按照标准Modbus TCP协议,长度字段应该表示后续PDU部分的实际字节数。然而,本例中长度字段为1030,而实际PDU部分仅为10个字节,存在不一致性。这可能是由于数据错误或协议扩展导致的。

4. 单元标识符(Unit Identifier)

  • 字节0x01

  • 解释:标识目标从设备的地址,类似于Modbus RTU中的从地址。

  • 作用:在多从设备环境中,服务器根据单元标识符区分不同的从设备。

三、PDU解析

PDU部分包含功能码及其相关数据。本例中的PDU为10个字节:

[0x10, 0x00, 0xC8, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x01]

1. 功能码(Function Code)

  • 字节0x10

  • 解释:功能码0x10对应于写多个寄存器(Write Multiple Registers)

  • 作用:指示服务器执行写入多个保持寄存器的操作。

2. 数据部分解析

根据功能码0x10的定义,数据部分结构如下:

字节序号字段名称长度描述
1-2起始地址(Starting Address)2要写入的第一个寄存器地址
3-4寄存器数量(Quantity of Registers)2要写入的寄存器数量
5字节计数(Byte Count)1后续数据部分的字节数(每个寄存器2字节)
6-…寄存器值(Register Values)N*2要写入的寄存器值,每个寄存器2字节

具体解析如下:

  • 起始地址

    • 字节0x00, 0xC8
    • 计算
      0x00 << 8 + 0xC8 = 200
      
    • 解释:从地址200开始写入寄存器。
  • 寄存器数量

    • 字节0x00, 0x02
    • 计算
      0x00 << 8 + 0x02 = 2
      
    • 解释:写入2个寄存器。
  • 字节计数

    • 字节0x04
    • 计算
      4字节
      
    • 解释:后续数据部分包含4个字节,表示2个寄存器的值(每个寄存器2字节)。
  • 寄存器值

    • 字节0x00, 0x01, 0x00, 0x01
    • 解析
      • 第一个寄存器值:0x00, 0x01 → 1
      • 第二个寄存器值:0x00, 0x01 → 1

3. 总结PDU内容

该PDU指令的具体含义为:

  • 功能:写入多个保持寄存器
  • 起始地址:200
  • 寄存器数量:2
  • 寄存器值
    • 地址200:写入值1
    • 地址201:写入值1

四、指令作用总结

综合上述解析,该Modbus TCP指令的作用如下:

  1. 目标设备:单元标识符为1的从设备
  2. 操作类型:写入保持寄存器
  3. 写入内容
    • 从寄存器地址200开始,连续写入2个寄存器
    • 写入的值均为1

注意事项

  • 长度字段不匹配:根据解析,MBAP头中的长度字段为1030,而实际PDU部分仅为10个字节。这可能导致服务器在解析时出现问题。应确认数据的正确性,确保长度字段与实际PDU长度一致。

    • 正确的长度字段应为PDU部分的字节数,包括单元标识符:
      PDU字节数 = 功能码(1) + 起始地址(2) + 寄存器数量(2) + 字节计数(1) + 寄存器值(4) = 10字节
      Length Field = 10
      
      因此,长度字段应为0x000A(10),而不是0x0406(1030)。
  • 字节序:Modbus TCP使用大端字节序(高位字节在前)。确保在编码和解码时保持一致。

五、实际应用示例

假设我们使用Python的pymodbus库来构建和解析上述指令,可以参考以下示例代码:

1. 构建Modbus TCP请求

from pymodbus.client.sync import ModbusTcpClient

# 创建客户端并连接到服务器
client = ModbusTcpClient('192.168.1.100', port=502)
connection = client.connect()
if connection:
    print("连接成功")

    # 构建写多个寄存器的请求
    starting_address = 200
    register_values = [1, 1]
    result = client.write_registers(address=starting_address, values=register_values, unit=1)
    
    if not result.isError():
        print("写入成功")
    else:
        print("写入失败:", result)

    # 关闭连接
    client.close()
else:
    print("连接失败")

2. 解析接收到的字节流

假设我们接收到上述字节流,可以使用以下代码进行解析:

def parse_modbus_tcp_message(message):
    if len(message) < 7:
        print("消息长度不足,无法解析MBAP头")
        return

    # 解析MBAP头
    transaction_id = (message[0] << 8) + message[1]
    protocol_id = (message[2] << 8) + message[3]
    length = (message[4] << 8) + message[5]
    unit_id = message[6]

    print(f"事务标识符: {transaction_id}")
    print(f"协议标识符: {protocol_id}")
    print(f"长度字段: {length}")
    print(f"单元标识符: {unit_id}")

    # 解析PDU
    if len(message) < 7 + (length -1):
        print("PDU部分长度不足")
        return

    pdu = message[7:7 + (length -1)]
    function_code = pdu[0]
    print(f"功能码: {function_code}")

    if function_code == 0x10:
        starting_address = (pdu[1] << 8) + pdu[2]
        quantity = (pdu[3] << 8) + pdu[4]
        byte_count = pdu[5]
        register_values = []
        for i in range(quantity):
            value = (pdu[6 + i*2] << 8) + pdu[7 + i*2]
            register_values.append(value)
        print(f"起始地址: {starting_address}")
        print(f"寄存器数量: {quantity}")
        print(f"字节计数: {byte_count}")
        print(f"寄存器值: {register_values}")
    else:
        print("未处理的功能码")

# 示例消息
message = [
    0x01, 0x00, 0x00, 0x00, 0x04, 0x06, 0x01,
    0x10, 0x00, 0xC8, 0x00, 0x02, 0x04,
    0x00, 0x01, 0x00, 0x01
]

parse_modbus_tcp_message(message)

输出

事务标识符: 256
协议标识符: 0
长度字段: 1030
单元标识符: 1
功能码: 16
起始地址: 200
寄存器数量: 2
字节计数: 4
寄存器值: [1, 1]

注意:由于长度字段不匹配,实际解析过程中可能会导致错误或忽略部分数据。在实际应用中,应确保长度字段的正确性。

六、结语

通过以上解析,我们详细了解了给定的Modbus TCP指令的各个组成部分及其含义。理解Modbus TCP的帧结构对于开发和调试工业通信应用至关重要。在实际应用中,务必确保每个字段的正确性,特别是长度字段,以避免通信故障和数据错误。

如果在解析或构建Modbus TCP指令时遇到问题,建议使用网络抓包工具(如Wireshark)进行分析,或者参考相关协议文档以确保实现的准确性。


原文地址:https://blog.csdn.net/weixin_37647148/article/details/144402681

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!