ModBus笔记
Modbus TCP 数据帧结构
一个完整的 Modbus TCP 数据帧(PDU, Protocol Data Unit)由以下几部分组成:
- 事务标识符(Transaction Identifier):2 字节
- 用于同步请求和响应消息。客户端生成一个唯一的事务标识符,并在响应中由服务器回传,以便客户端可以将响应与对应的请求匹配。
- 协议标识符(Protocol Identifier):2 字节
- 固定为
0x0000,表示使用的是 Modbus 协议。
- 固定为
- 长度字段(Length):2 字节
- 表示后续字节的数量,包括单元标识符和实际的 Modbus 请求或响应数据。最大值为 260(0x0104),因为整个 Modbus PDU 的最大长度是 260 字节。
- 单元标识符(Unit Identifier):1 字节
- 在传统的串行链路 Modbus 中用来标识从站地址。在 Modbus TCP 中,这个字段通常被设置为
0xFF或者某个固定的值,因为它不是必需的。然而,在某些情况下,它可以用来区分不同的设备或服务实例。
- 在传统的串行链路 Modbus 中用来标识从站地址。在 Modbus TCP 中,这个字段通常被设置为
- Modbus 功能码及数据(MBAP + PDU):
- 这部分包含了具体的 Modbus 请求或响应数据,包括功能码和对应的数据域。这部分遵循标准的 Modbus 协议格式。
示例数据帧结构
假设我们要发送一个读取保持寄存器的请求,起始地址为 0x0001,读取数量为 0x0002:
| 事务标识符 (2 字节) | 协议标识符 (2 字节) | 长度 (2 字节) | 单元标识符 (1 字节) | 功能码 (1 字节) | |
|---|---|---|---|---|---|
- 事务标识符:
0x0001(假设这是第一个请求) - 协议标识符:
0x0000 - 长度:
0x0006(表示接下来的 6 字节属于 Modbus PDU) - 单元标识符:
0xFF - 功能码:
0x03(读取保持寄存器) - 起始地址:
0x0001 - 寄存器数量:
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),值为0或1,分别表示 OFF 或 ON 状态。
2. I - 输入寄存器
- Modbus 功能码:
0x02(Read Discrete Inputs) - 描述:
I表示输入位(Discrete Input),只能被读取。- 每个
I地址也对应一个二进制位(bit),值为0或1。
- 例子:
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 功能码 | 描述 |
|---|---|---|---|
Q | Coils | 0x01 / 0x05 | 输出寄存器,可读写 |
I | Discrete Inputs | 0x02 | 输入寄存器,只读 |
V | Holding/Input Registers | 0x03 / 0x04 / 0x06 / 0x10 | 变量存储区,用于存储中间变量 |