Modbus简述
Modbus是一种应用层的报文传输协议:
-
RTU
-
ASCII
-
TCP
| 方面 | Modbus RTU | Modbus TCP |
|---|---|---|
| 物理层/传输介质 | RS-232 / RS-485(串口) | 以太网(TCP/IP) |
| 编码方式 | 二进制(紧凑) | 二进制(与RTU相同,但封装不同) |
| 帧结构 | Slave ID + PDU + CRC | MBAP Header (7字节) + PDU |
| 错误校验 | CRC-16 | 无(依赖TCP校验和) |
| 最大从站数 | 247 | 受IP网络限制,几乎无限 |
| 并发性 | 单主多从(主站轮询) | 多主多从(多客户端并发) |
| 速度 | 通常9600~115200 bps | 10/100/1000 Mbps,延迟更低 |
| 距离 | RS-485最远1200m | 以太网标准距离,或通过光纤/路由器无限扩展 |
| 抗干扰 | 需终端电阻、光电隔离 | 依赖网络质量,工业以太网需交换机优化 |
| 典型应用场景 | 现场设备密集、成本敏感、低速 | 集中监控、远程访问、与IT系统集成 |
| 安全性 | 物理隔离,基本无加密 | 暴露在网络中,易受攻击(建议加VPN/防火墙) |
Modbus RTU通信协议:
Modbus RTU(Remote Terminal Unit)是一种在工业领域广泛使用的串行通信协议。它支持通过串行连接进行远程设备间的通讯,以实现数据交换。
1.1 数据格式
| 从站地址(设备编号) | 功能码 | 数据 | 校验 |
|---|---|---|---|
| 1 byte | 1 byte | N byte | 2 byte |
1.2 存储区
- 输出线圈 0
- 00001-09999
- 输入线圈 1
- 10001-19999
- 输出寄存器 4
- 40001-49999
- 输入寄存器 3
- 30001-39999
存储区范围5位和6位 标准地址和扩展地址
1.3 功能码
| 读和写 | 功能码 |
|---|---|
| 读输出线圈(读线圈) | 01 |
| 读输入线圈(读离散输入) | 02 |
| 读输出寄存器(读保持寄存器) | 03 |
| 读输入寄存器 | 04 |
| 写单个输出线圈(写单个线圈) | 05 |
| 写单个输出寄存器(写单个保持寄存器) | 06 |
| 写多个输出线圈(写多个线圈) | 15 |
| 写多个输出寄存器(写多个保持寄存器) | 16 |
1.4 报文收发
| 功能码 | 请求与响应 | 数据格式 | ||||||
|---|---|---|---|---|---|---|---|---|
| 01读线圈 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 线圈起始地址(2byte) | 线圈数量(2byte) | CRC校验码(2byte) | ||
| 01读线圈 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 字节数(1byte) | 线圈值(nbyte) | CRC校验码(2byte) | ||
| 02读离散输入 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 离散输入起始地址(2byte) | 离散输入数量(2byte) | CRC校验码(2byte) | ||
| 02读离散输入 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 字节数(1byte) | 离散输入值(nbyte) | CRC校验码(2byte) | ||
| 03读保持寄存器 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 保持寄存器起始地址(2byte) | 保持寄存器数量(2byte) | CRC校验码(2byte) | ||
| 03读保持寄存器 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 字节数(1byte) | 保持寄存器值(nbyte) | CRC校验码(2byte) | ||
| 04读输入寄存器 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 输入寄存器起始地址(2byte) | 输入寄存器数量(2byte) | CRC校验码(2byte) | ||
| 04读输入寄存器 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 字节数(1byte) | 输入寄存器值(nbyte) | CRC校验码(2byte) | ||
| 05写单个线圈 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 线圈地址(2byte) | 线圈写入值(2byte) | CRC校验码(2byte) | ||
| 05写单个线圈 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 线圈地址(2byte) | 线圈写入值(2byte) | CRC校验码(2byte) | ||
| 06写单个保持寄存器 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 寄存器地址(2byte) | 寄存器写入值(2byte) | CRC校验码(2byte) | ||
| 06写单个保持寄存器 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 寄存器地址(2byte) | 寄存器写入值(2byte) | CRC校验码(2byte) | ||
| 15写多个线圈 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 线圈起始地址(2byte) | 线圈数量(2byte) | 字节数(1byte) | 线圈写入值(nbyte) | CRC校验码(2byte) |
| 15写多个线圈 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 线圈起始地址(2byte) | 线圈数量(2byte) | CRC校验码(2byte) | ||
| 16写多个保持寄存器 | 请求(主站-从站) | 从站地址(1byte) | 功能码(1byte) | 保持寄存器起始地址(2byte) | 保持寄存器数量(2byte) | 字节数(1byte) | 保持寄存器写入值(nbyte) | CRC校验码(2byte) |
| 16写多个保持寄存器 | 响应(从站-主站) | 从站地址(1byte) | 功能码(1byte) | 保持寄存器起始地址(2byte) | 保持寄存器数量(2byte) | CRC校验码(2byte) |
1.5 代码示例
发送:01 03 00 00 00 02 C4 0B
接收:01 03 04 01 46 01 3B 5A 59
| 发送报文 | 解析 | 接收报文 | 解析 |
|---|---|---|---|
| 01 | 站地址 | 01 | 站地址 |
| 03 | 读输出寄存器 | 03 | 读输出寄存器 |
| 00 00 | 起始寄存器 | 04 | 字节计数 |
| 00 02 | 寄存器长度 | 01 46 01 3B | 具体四个字节 |
| C4 0B | CRC校验 | 5A 59 | CRC校验 |
Modbus TCP通信协议:
2.1 数据格式
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据 |
|---|---|---|---|---|---|---|
| 2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 2字节 | N字节 |
2.2 Modbus 地址映射到 CPU 地址
| Modbus地址 | CPU地址 |
|---|---|
| 00001 | Q0.0 |
| 00002 | Q0.1 |
| ...... | ...... |
| 10001 | I0.0 |
| 10002 | I0.1 |
| ...... | ...... |
| 30001 | AIW0 |
| 30002 | AIW2 |
| ...... | ...... |
| 40001 | Vx(保持寄存器起始地址)例:VW0 |
| 40002 | Vx+2 =(保持寄存器起始地址+2)例:VW2 |
| ...... | ...... |
2.3 报文头(MBAP头)及功能码
其中,事务处理标识、协议标识符、长度和单元标识符统称为报文头(MBAP头)。
| 内容 | 解释 |
|---|---|
| 事务处理标识 | 可以理解为报文序列号,一般每次通信后就要加1,以区分不同的通信数据报文。 |
| 协议标识符 | 00 00 表示Modbus TCP 协议 |
| 长度 | 接下来数据长度,单位字节 |
| 单元标识符 | 设备地址,一般为01 00 FF |
| 功能码 | 描述 | 说明 | MOUBUS地址 |
|---|---|---|---|
| 01(0x01) | 读离散输出寄存器(读线圈(Coils)) | 位操作 | 00001 ~ 09999 |
| 02(0x02) | 读离散输入寄存器 | 位操作 | 10001 ~ 19999 |
| 03(0x03) | 读保持寄存器(最常用) | 字操作 | 40001 ~ 49999 |
| 04(0x04) | 读输入寄存器 | 字操作 | 30001 ~ 39999 |
| 05(0x05) | 写离散输出寄存器(写单个线圈) | 位操作 | 00001 ~ 09999 |
| 06(0x06) | 写保持寄存器(写单个寄存器) | 字操作 | 40001 ~ 49999 |
| 15(0x0F) | 写多线圈寄存器(写多个线圈) | 位操作 | 00001 ~ 09999 |
| 16(0x10) | 写多个保持寄存器(写多个寄存器) | 字操作 | 40001 ~ 49999 |
2.4 报文收发
| 功能码 | 请求与响应 | 数据格式 | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 01读线圈 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 线圈起始地址(2byte) | 线圈数量(2byte) | ||
| 01读线圈 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 字节数(1byte) | 线圈值(nbyte) | ||
| 02读离散输入 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 离散输入起始地址(2byte) | 离散输入数量(2byte) | ||
| 02读离散输入 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 字节数(1byte) | 离散输入值(nbyte) | ||
| 03读保持寄存器 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 保持寄存器起始地址(2byte) | 保持寄存器数量(2byte) | ||
| 03读保持寄存器 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 字节数(1byte) | 保持寄存器值(nbyte) | ||
| 04读输入寄存器 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 输入寄存器起始地址(2byte) | 输入寄存器数量(2byte) | ||
| 04读输入寄存器 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 字节数(1byte) | 输入寄存器值(nbyte) | ||
| 05写单个线圈 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 线圈地址(2byte) | 线圈写入值(2byte) | ||
| 05写单个线圈 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 线圈地址(2byte) | 线圈写入值(2byte) | ||
| 06写单个保持寄存器 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 寄存器地址(2byte) | 寄存器写入值(2byte) | ||
| 06写单个保持寄存器 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 寄存器地址(2byte) | 寄存器写入值(2byte) | ||
| 15写多个线圈 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 线圈起始地址(2byte) | 线圈数量(2byte) | 字节数(1byte) | 线圈写入值(nbyte) |
| 15写多个线圈 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 线圈起始地址(2byte) | 线圈数量(2byte) | ||
| 16写多个保持寄存器 | 请求(主站-从站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 保持寄存器起始地址(2byte) | 保持寄存器数量(2byte) | 字节数(1byte) | 保持寄存器写入值(nbyte) |
| 16写多个保持寄存器 | 响应(从站-主站) | 事务处理标识符(2byte) | 协议标识符(2byte) | 长度(2byte) | 单位标识符(1byte) | 功能码(1byte) | 保持寄存器起始地址(2byte) | 保持寄存器数量(2byte) |
2.5 代码示例
示例1: 读取保持寄存器,功能码0x03
result = client.read_holding_registers(0, count=2, slave=1)
address: int,寄存器地址,开始读取的地址,依据从机设置的地址配置
功能码:03已默认
count: 要读取的寄存器数量
slave: 与从机匹配
发送:00 01 00 00 00 06 01 03 00 00 00 02
接收:00 01 00 00 00 07 01 03 04 00 0A 00 64
| 发送报文 | 解析 | 接收报文 | 解析 |
|---|---|---|---|
| 00 01 | 事务ID | 00 01 | 事务ID |
| 00 00 | 协议ID | 00 00 | 协议ID |
| 00 06 | 后续6字节 | 00 07 | 后续7字节 |
| 01 | 单位标识符 | 01 | 单位标识符 |
| 03 | 功能码:读输出寄存器 | 03 | 功能码:读输出寄存器 |
| 00 00 | 保持寄存器起始地址 | 04 | 字节计数 |
| 00 02 | 保持寄存器长度 | 00 0A 00 64 | 具体四个字节 |
Modbus TCP 通讯数据示例
3.1 功能码01 读离散输出线圈
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 00 11 | 00 00 | 00 06 | 01 | 01 | 00 00 | 00 08 |
注释:读取Q0.0地址开始的8个连续输出点(即Q0.0-Q0.7)。
3.2 功能码02 读离散输入线圈
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 00 15 | 00 00 | 00 06 | 01 | 02 | 00 00 | 00 08 |
注释:读取I0.0地址开始的8个连续输出点(即I0.0-I0.7)。
3.3 功能码03 读保持寄存器
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 00 66 | 00 00 | 00 06 | 01 | 03 | 00 00 | 00 02 |
注释:读取VW0地址开始的2个连续字空间(即vw0,vw2)。
3.4 功能码04 读输入寄存器
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 00 97 | 00 00 | 00 06 | 01 | 04 | 00 00 | 00 02 |
注释:读取AIW0地址开始的2个连续字空间(即AIW0,AIW2)。
3.5 功能码05 写单个离散输出寄存器
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 00 C2 | 00 00 | 00 06 | 01 | 05 | 00 00 | FF 00 |
注释:将地址00000的输出离散线圈置一(即让Q0.0置一)。
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 01 0A | 00 00 | 00 06 | 01 | 05 | 00 01 | 00 00 |
注释:将地址00001的输出离散线圈置一(即让Q0.1复位)。
3.6 功能码06 写单个保持寄存器
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度 |
|---|---|---|---|---|---|---|
| 01 5e | 00 00 | 00 06 | 01 | 06 | 00 02 | 00 7b |
注释:往地址VW4寄存器中写入数值0x007B(即十进制数123)。
3.7 功能码15 写多个线圈寄存器
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度(位) | 字节数 | 数据 |
|---|---|---|---|---|---|---|---|---|
| 01 5e | 00 00 | 00 09 | 01 | 0F | 00 00 | 00 10 | 02 | aa cb |
注释:往地址QB0寄存器中写入数值0xaa,往地址QB1寄存器中写入数值0xcb。
数据长度位不满8的N倍,字节数也算N个。
3.8 功能码16 写多个保持寄存器
| 事务处理标识 | 协议标识 | 长度 | 单元标识 | 功能码 | 起始地址 | 数据长度(位) | 字节数 | 数据 |
|---|---|---|---|---|---|---|---|---|
| 02 68 | 00 00 | 00 0B | 01 | 10 | 00 00 | 00 02 | 04 | 00 7B 00 EA |
注释:往地址VW0寄存器中写入数值0x007B,往地址VW2寄存器中写入数值0x00EA。
配置ModBus TCP连接通常涉及以下几个步骤:
- 确定网络参数 :获取目标设备的IP地址和端口号。
- 安装ModBus TCP客户端软件 :选择合适的ModBus客户端软件,如Modscan、MBConnectLine等。
- 配置连接参数 :在客户端软件中设置目标设备的IP地址和端口,确认连接的超时时间。
- 选择通信模式 :选择TCP作为通信模式。
- 连接测试 :尝试连接到设备,如果连接成功,配置完成。
- 启动MBConnectLine
- 点击"File" -> "New Project"
- 选择"Modbus TCP"
- 输入设备的IP地址和端口(例如:192.168.1.101:502)
- 点击"Connect"测试连接
- 查看状态栏,显示"Connected"表示配置成功。