Skip to content

ModBus笔记

Modbus TCP 数据帧结构

一个完整的 Modbus TCP 数据帧(PDU, Protocol Data Unit)由以下几部分组成:

  1. 事务标识符(Transaction Identifier):2 字节
    • 用于同步请求和响应消息。客户端生成一个唯一的事务标识符,并在响应中由服务器回传,以便客户端可以将响应与对应的请求匹配。
  2. 协议标识符(Protocol Identifier):2 字节
    • 固定为 0x0000,表示使用的是 Modbus 协议。
  3. 长度字段(Length):2 字节
    • 表示后续字节的数量,包括单元标识符和实际的 Modbus 请求或响应数据。最大值为 260(0x0104),因为整个 Modbus PDU 的最大长度是 260 字节。
  4. 单元标识符(Unit Identifier):1 字节
    • 在传统的串行链路 Modbus 中用来标识从站地址。在 Modbus TCP 中,这个字段通常被设置为 0xFF 或者某个固定的值,因为它不是必需的。然而,在某些情况下,它可以用来区分不同的设备或服务实例。
  5. Modbus 功能码及数据(MBAP + PDU)
    • 这部分包含了具体的 Modbus 请求或响应数据,包括功能码和对应的数据域。这部分遵循标准的 Modbus 协议格式。

示例数据帧结构

假设我们要发送一个读取保持寄存器的请求,起始地址为 0x0001,读取数量为 0x0002

事务标识符 (2 字节)协议标识符 (2 字节)长度 (2 字节)单元标识符 (1 字节)功能码 (1 字节)
  1. 事务标识符: 0x0001 (假设这是第一个请求)
  2. 协议标识符: 0x0000
  3. 长度: 0x0006 (表示接下来的 6 字节属于 Modbus PDU)
  4. 单元标识符: 0xFF
  5. 功能码: 0x03 (读取保持寄存器)
  6. 起始地址: 0x0001
  7. 寄存器数量: 0x0002

Modbus 数据类型

在 Modbus 协议中,有几种主要的数据类型:

  • 线圈(Coils):单个位(bit),可读写,通常用于控制开关或指示状态。
  • 离散输入(Discrete Inputs):单个位(bit),只读,类似于线圈,但仅用于输入。
  • 保持寄存器(Holding Registers):16 位字(word),可读写,常用于存储模拟量或配置参数。
  • 输入寄存器(Input Registers):16 位字(word),只读,类似于保持寄存器,但仅用于输入。

在 Modbus 协议中的对应关系:

1. Q - 输出寄存器

  • Modbus 功能码: 0x01 (Read Coils) 或 0x05 (Write Single Coil)
  • 描述:
    • Q 表示输出线圈(Coil),可以被读取或写入。
    • 每个 Q 地址对应一个二进制位(bit),值为 01,分别表示 OFF 或 ON 状态。

2. I - 输入寄存器

  • Modbus 功能码: 0x02 (Read Discrete Inputs)
  • 描述:
    • I 表示输入位(Discrete Input),只能被读取。
    • 每个 I 地址也对应一个二进制位(bit),值为 01
  • 例子:
    • I0.0 对应 Modbus 离散输入地址 0x0000
    • 使用 read_discrete_inputs(0, 1) 来读取 I0.0 的状态

3. V - 变量存储区

  • Modbus 功能码: 0x03 (Read Holding Registers) 或 0x06 (Write Single Register)
  • 描述:
    • V 通常用于存储中间变量或需要长期保存的数据,可以是整数、浮点数等。
    • 在 Modbus 中,这些数据通常存储在保持寄存器(Holding Registers)或输入寄存器(Input Registers)中。
  • 例子:
    • 如果你有一个 VW10(假设是一个 16 位无符号整数),它可能映射到 Modbus 保持寄存器地址 0x000A
    • 使用 read_holding_registers(10, 1) 来读取 VW10 的值
    • 使用 write_register(10, value) 来写入 VW10

ModbusTcpClient 常用 API 及其对应的数据类型

1. 读取线圈

client.read_coils(address, count, unit=1)
  • 功能码:0x01
  • 描述:从指定地址开始读取指定数量的线圈状态。
  • 返回:包含线圈状态的响应对象,.bits 属性表示线圈状态列表(True 表示 ON/1,False 表示 OFF/0)。

2. 读取离散输入

client.read_discrete_inputs(address, count, unit=1)
  • 功能码:0x02
  • 描述:从指定地址开始读取指定数量的离散输入状态。
  • 返回:包含离散输入状态的响应对象,.bits 属性表示离散输入状态列表(True 表示 ON/1,False 表示 OFF/0)。

3. 读取保持寄存器

client.read_holding_registers(address, count, unit=1)
  • 功能码:0x03
  • 描述:从指定地址开始读取指定数量的保持寄存器值。
  • 返回:包含保持寄存器值的响应对象,.registers 属性表示寄存器值列表(每个值为 16 位整数)。

4. 读取输入寄存器

client.read_input_registers(address, count, unit=1)
  • 功能码:0x04
  • 描述:从指定地址开始读取指定数量的输入寄存器值。
  • 返回:包含输入寄存器值的响应对象,.registers 属性表示寄存器值列表(每个值为 16 位整数)。

5. 写入单个线圈

client.write_coil(address, value, unit=1)
  • 功能码:0x05
  • 描述:向指定地址写入单个线圈状态(ON/1 或 OFF/0)。
  • 参数:
    • address: 线圈起始地址。
    • value: 写入的值(True 或 False)。
  • 返回:确认是否写入成功的响应对象。

6. 写入多个线圈

client.write_coils(address, values, unit=1)
  • 功能码:0x0F
  • 描述:向指定地址开始的一组线圈写入多个状态值。
  • 参数:
    • address: 线圈起始地址。
    • values: 要写入的状态值列表(True 或 False)。
  • 返回:确认是否写入成功的响应对象。

7. 写入单个保持寄存器

client.write_register(address, value, unit=1)
  • 功能码:0x06
  • 描述:向指定地址写入单个保持寄存器值。
  • 参数:
    • address: 寄存器地址。
    • value: 写入的值(16 位整数)。
  • 返回:确认是否写入成功的响应对象。

8. 写入多个保持寄存器

client.write_registers(address, values, unit=1)
  • 功能码:0x10
  • 描述:向指定地址开始的一组保持寄存器写入多个值。
  • 参数:
    • address: 寄存器起始地址。
    • values: 要写入的值列表(16 位整数列表)。
  • 返回:确认是否写入成功的响应对象。

注意事项

  • 单位号(unit):这是 Modbus 设备的从站 ID,默认是 1。如果网络中有多个 Modbus 设备,则需要根据设备的实际 ID 设置此参数。
  • 错误处理:如果请求失败,isError() 方法将返回 True,可以通过检查响应对象来获取具体的错误信息。

PLC 标识Modbus 类型Modbus 功能码描述
QCoils0x01 / 0x05输出寄存器,可读写
IDiscrete Inputs0x02输入寄存器,只读
VHolding/Input Registers0x03 / 0x04 / 0x06 / 0x10变量存储区,用于存储中间变量
最近更新