modbus协议介绍
2025-09-03
简要的介绍MODBUS协议,包含RTU、ASCII、TCP三种模式的基本结构和示例。
MODBUS 介绍
- 定义: Modbus是一种工业通信协议,由Modicon公司于1979年推出,用于控制器之间的数据通信。
- 主从架构:单主设备(Master)发起请求,多从设备(Slave)响应。
- 多传输模式:支持RTU(二进制)、ASCII(文本)、TCP/IP(以太网)。
协议对比
| 特性 | RTU模式 | ASCII模式 | TCP模式 |
|---|---|---|---|
| 传输格式 | 二进制(十六进制) | ASCII字符(可读文本) | TCP/IP数据包(二进制) |
| 开始/结束标记 | 无(依赖3.5字符静默时间) | 冒号:开始,CRLF结束 | MBAP头(7字节)+PDU数据 |
| 校验方式 | CRC-16 | LRC(纵向冗余校验) | 无(依赖TCP/IP层校验) |
| 传输效率 | 高(1字节传输1数据) | 低(1字节需2字符表示) | 最高(直接二进制传输) |
| 调试便利性 | 较差(需工具解析二进制) | 极佳(直接可读字符) | 中等(需网络抓包工具) |
| 适用场景 | 工业现场(大数据量) | 调试/小数据量(如HMI) | 以太网设备(如PLC网关) |
常用功能码分类
| 功能码 | 名称 | 操作对象 | 数据类型 |
|---|---|---|---|
| 0x01 | 读线圈状态 | 线圈寄存器 | 位 |
| 0x02 | 读离散输入 | 离散输入寄存器 | 位 |
| 0x05 | 写单个线圈 | 线圈寄存器 | 位 |
| 0x0F | 写多个线圈 | 线圈寄存器 | 位 |
| 0x03 | 读保持寄存器 | 保持寄存器 | 16位 |
| 0x04 | 读输入寄存器 | 输入寄存器 | 16位 |
| 0x06 | 写单个保持寄存器 | 保持寄存器 | 16位 |
| 0x10 | 写多个保持寄存器 | 保持寄存器 | 16位 |
MODBUS RTU
数据帧结构
- 地址域(1字节):指定从设备地址(1-247)。
- 功能码(1字节):指定操作类型(读/写)。
- 地址域(2字节):指定寄存器地址。
- 寄存器数量(2字节):指定要读写的寄存器数量。
- 数据长度(1字节):指定数据长度。
- 数据域(N字节):包含具体操作数据。
- CRC校验(2字节):用于错误检测。
CRC 校验
CRC(循环冗余校验)是一种基于多项式除法的错误检测算法,用于验证数据传输或存储的完整性。
static uint16_t crc16(const uint8_t *data, size_t length)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < length; i++)
{
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++)
{
if (crc & 0x0001)
crc = (crc >> 1) ^ 0xA001;
else
crc >>= 1;
}
}
return crc;
}举例说明
写入单个线圈(功能码0x05)
- 请求:
01 05 00 03 FF 00 7C 3A - 响应:
01 05 00 03 FF 00 7C 3A
| 方向 | 参数 | 描述 |
|---|---|---|
| 请求 | 01 | 从站地址(设备ID=1) |
| 05 | 功能码(强制单个线圈) | |
| 00 03 | 线圈地址(0x0003,十进制3,对应地址0004) | |
| FF 00 | 写入值(FF00=置位,0000=复位) | |
| 7C 3A | CRC校验码 | |
| 响应 | 01 | 从站地址(设备ID=1) |
| 05 | 功能码(强制单个线圈) | |
| 00 03 | 线圈地址(0x0003,十进制3,对应地址0004) | |
| FF 00 | 写入结果 | |
| 7C 3A | CRC校验码 |
读取保持寄存器(功能码0x03)
- 请求:
01 03 00 19 00 02 15 CC - 响应:
01 03 04 2E 63 6F 6D EF 18
| 方向 | 参数 | 描述 |
|---|---|---|
| 请求 | 01 | 从站地址(设备ID=1) |
| 03 | 功能码(读取保持寄存器) | |
| 00 19 | 起始寄存器地址(0x0019,十进制25) | |
| 00 02 | 读取数量(2个寄存器) | |
| 15 CC | CRC校验码 | |
| 响应 | 01 | 从站地址(设备ID=1) |
| 03 | 功能码(读取保持寄存器) | |
| 04 | 返回字节数(4字节,即2个寄存器的值) | |
| 2E 63 | 第一个寄存器值(0x2E63) | |
| 6F 6D | 第二个寄存器值(0x6F6D) | |
| EF 18 | CRC校验码 |
MODBUS ASCII
数据帧结构
- 起始符(1字节):固定为“:” (0x3a)。
- 地址域(2字节):2个ASCII字符(1字节十六进制)表示从站地址(00-FF)。
- 功能码(2个ASCII字符(1字节十六进制)表示操作类型
- 数据域(可变长度,每个字节用2个ASCII字符表示(十六进制))
- LRC校验(2个ASCII字符(1字节校验值),纵向冗余校验(LRC))
- 结束符(固定为回车换行 CRLF(0x0D 0x0A))
LRC 校验
LRC(纵向冗余校验)是一种简单的错误检测机制,通过对数据帧中的所有字节求和,然后取反得到校验值。
static uint8_t calc_lrc(const uint8_t *data, size_t length)
{
uint8_t lrc = 0;
for (size_t i = 0; i < length; i++)
lrc += data[i];
return (~lrc + 1) & 0xff;
}举例说明
写入单个线圈(功能码0x05)
- 请求:
3a 30 31 30 35 30 30 30 33 46 46 30 30 46 38 0d 0a':01050003FF00F8\r\n' - 响应:
3a 30 31 30 35 30 30 30 33 46 46 30 30 46 38 0d 0a':01050003FF00F8\r\n'
| 方向 | 参数(HEX) | 字符串 | 描述 |
|---|---|---|---|
| 请求 | 3a | ':' | 起始符 |
| 30 31 | '01' | 从站地址(设备ID=1) | |
| 30 35 | '05' | 功能码(强制单个线圈) | |
| 30 30 30 33 | '0003' | 线圈地址(0x0003,十进制3,对应地址0004) | |
| 46 46 30 30 | 'FF00' | 写入值(FF00=置位,0000=复位) | |
| 46 38 | 'F8' | LRC校验码 | |
| 0D 0A | '\r\n' | 结束符 | |
| 响应 | 3a | ':' | 起始符 |
| 30 31 | '01' | 从站地址(设备ID=1) | |
| 30 35 | '05' | 功能码(强制单个线圈) | |
| 30 30 30 33 | '0003' | 线圈地址(0x0003,十进制3,对应地址0004) | |
| 46 46 30 30 | 'FF00' | 写入值(FF00=置位,0000=复位) | |
| 46 38 | 'F8' | LRC校验码 | |
| 0D 0A | '\r\n' | 结束符 |
读取保持寄存器(功能码0x03)
- 请求:
3a 30 31 30 33 30 30 31 39 30 30 30 32 45 31 0d 0a':010300190002E1\r\n' - 响应:
3a 30 31 30 33 30 34 32 45 36 33 36 46 36 44 38 42 0d 0a':0103042E636F6D8B\r\n'
| 方向 | 参数(HEX) | 字符串 | 描述 |
|---|---|---|---|
| 请求 | 3a | ':' | 起始符 |
| 30 31 | '01' | 从站地址(设备ID=1) | |
| 30 33 | '03' | 功能码(读取保持寄存器) | |
| 30 30 31 39 | '0019' | 起始寄存器地址(0x0019,十进制25) | |
| 30 30 30 32 | '0002' | 读取数量(2个寄存器) | |
| 45 31 | 'E1' | LRC校验码 | |
| 响应 | 3a | ':' | 起始符 |
| 30 31 | '01' | 从站地址(设备ID=1) | |
| 30 33 | '03' | 功能码(读取保持寄存器) | |
| 30 34 | '04' | 返回字节数(4字节,即2个寄存器的值) | |
| 32 45 36 33 | '2E63' | 第一个寄存器值(0x2E63) | |
| 36 46 36 44 | '6F6D' | 第二个寄存器值(0x6F6D) | |
| 38 42 | '8B' | LRC校验码 | |
| 0D 0A | '\r\n' | 结束符 |
MODBUS TCP
同样适用UDP
数据帧结构
- 流水号(2字节):唯一标识。
- 协议类型(2字节):固定0。
- 数据长度(2字节):指定数据长度。
- 单元标识符(1字节):类似从站地址。
- 功能码(1字节):指定操作类型(读/写)。
- 地址域(2字节):指定寄存器地址。
- 寄存器数量(2字节):指定要读写的寄存器数量。
举例说明
写入单个线圈(功能码0x05)
- 请求:
00 45 00 00 00 06 01 05 00 03 FF 00 - 响应:
00 45 00 00 00 06 01 05 00 03 FF 00
| 方向 | 参数 | 描述 |
|---|---|---|
| 请求 | 00 45 | 唯一序号 |
| 00 00 | 固定0 | |
| 00 06 | 数据长度 | |
| 01 | 设备地址 | |
| 05 | 功能码 | |
| 00 03 | 线圈地址(0x0003,十进制3,对应地址0004) | |
| FF 00 | 写入值(FF00=置位,0000=复位) | |
| 响应 | 00 45 | 与请求相同 |
| 00 00 | 固定0 | |
| 00 06 | 数据长度 | |
| 01 | 设备地址 | |
| 05 | 功能码 | |
| 00 03 | 线圈地址(0x0003,十进制3,对应地址0004) | |
| FF 00 | 写入值(FF00=置位,0000=复位) |
读取保持寄存器(功能码0x03)
- 请求:
01 CE 00 00 00 06 01 03 00 19 00 02 - 响应:
01 CE 00 00 00 07 01 03 04 2E 63 6F 6D
| 方向 | 参数 | 描述 |
|---|---|---|
| 请求 | 01 CE | 唯一序号 |
| 00 00 | 固定0 | |
| 00 06 | 数据长度 | |
| 01 | 从站地址(设备ID=1) | |
| 03 | 功能码(读取保持寄存器) | |
| 00 19 | 起始寄存器地址(0x0019,十进制25) | |
| 00 02 | 读取数量(2个寄存器) | |
| 响应 | 01 CE | 唯一序号 |
| 00 00 | 固定0 | |
| 00 07 | 数据长度 | |
| 01 | 从站地址(设备ID=1) | |
| 03 | 功能码(读取保持寄存器) | |
| 04 | 返回字节数(4字节,即2个寄存器的值) | |
| 2E 63 | 第一个寄存器值(0x2E63) | |
| 6F 6D | 第二个寄存器值(0x6F6D) |
相关软件及工具
Modbus Poll
Modbus Poll是一个Modbus主站模拟器,主要用于帮助Modbus从站设备的开发人员或其他想要测试和模拟Modbus协议的人。通过多文档界面,您可以同时监控多个Modbus从站和/或数据区。对于每个窗口,您只需指定Modbus从站ID、功能、地址、大小和轮询速率。您可以从任何窗口读取和写入寄存器和线圈。如果您想更改单个寄存器,只需双击该值。或者您可以更换多个寄存器/线圈。 多种数据格式,如float,double和long,以及词序交换。
- 常见激活序列号
5A5742575C5D10
Modbus Slave
Modbus Slave用于在100个窗口中模拟多达100个从设备!使用此模拟工具加快您的PLC编程。在从供应商处收到从设备之前开始编程和测试。主应用程序可以访问任何打开的文档中包含的数据。与Modbus Poll相同的用户界面。 监控串行通信。
- 常见激活序列号
5455415451475662
在线串口助手
通过上传到本地浏览器的JS文件,实现自定义协议开发,预设协议包含MODBUS RTU/ASCII。