![深入解析Modbus RTU命令与应答机制插图1 深入解析Modbus RTU命令与应答机制插图1](https://www.modbus.cn/wp-content/themes/module2/themer/assets/images/lazy.png)
Modbus RTU协议描述
Modbus是基于主从结构的通信协议。它使用RS-485, RS-422, RS-232接口,以及Ethernet TCP/IP网络(Modbus TCP协议)进行数据传输。
Modbus RTU消息包括:
- SlaveID设备的地址
- 功能码
- 依赖于功能码的特殊数据
- CRC-16(Modbus)
结构如下:
SlaveID 功能码 特殊数据 CRC16
如果你去掉SlaveID地址和CRC-16(Modbus),你将得到PDU(协议数据单元)。
SlaveID是设备的地址,可以从0到247之间选择值。值得注意的是,从248到255的地址是保留的。
模块中的数据存储在四个表中,其中两个是只读的,另外两个是读写的。每个表可以容纳9999个值。
以下是表的总结:
- 离散输出线圈 (DO):读写;寄存器号1-9999;HEX地址范围0000到270E
- 离散输入触点 (DI):只读;寄存器号10001-19999;HEX地址范围0000到270E
- 模拟输入寄存器 (AI):只读;寄存器号30001-39999;HEX地址范围0000到270E
- 模拟输出保持寄存器 (AO):读写;寄存器号40001-49999;HEX地址范围0000到270E
每个表都有一个寄存器号(例如AO的40001),它对应于一个地址(例如0000)。这种差异被称为”偏移”。表的偏移量分别为1, 10001, 30001和40001。
以一个例子说明:从40108到40110寄存器为设备地址17获取AI值的Modbus RTU请求如下:
11 03 006B 0003 7687
这个分解如下:
11
:SlaveID设备地址(十进制中的17或HEX中的11)03
:功能码006B
:第一个寄存器的地址(计算为40108-40001 = 107,这是HEX中的6B)0003
:所需的寄存器数量(从40108到40110获取三个寄存器)7687
:CRC校验和
Modbus RTU从设备会回复:
11 03 06 AE41 5652 4340 49AD
这转化为:
- 设备地址:
11
(或十进制中的17) - 功能码:
03
- 字节计数(表示后面跟随6字节):
06
- 注册值AO0、AO1和AO2作为两字节序列
- CRC值由
49AD
给出
模拟输出寄存器的值可以通过多种方式解释,例如:
- 作为16位无符号整数(范围0至65535)
- 作为16位有符号整数(范围-32768至32767)
- 作为两个字符的ASCII字符串
- 作为离散开/关值(0或1)
- 作为32位无符号整数(范围0到4,294,967,295)
- 作为32位有符号整数(范围-2,147,483,648至2,147,483,647)
- 作为32位单精度IEEE浮点数(范围1.2×10^−38至3.4×10^+38)
- 作为四字符ASCII字符串
![深入解析Modbus RTU命令与应答机制插图3 深入解析Modbus RTU命令与应答机制插图3](https://www.modbus.cn/wp-content/themes/module2/themer/assets/images/lazy.png)
Modbus RTU命令是什么?
以下是用于读取和写入Modbus RTU寄存器的代码表。
功能代码 | 功能操作 | 数据类型 | 访问类型 |
---|---|---|---|
01 (0x01) | 读取DO | 离散输出线圈状态 | 读取 |
02 (0x02) | 读取DI | 离散输入状态 | 读取 |
03 (0x03) | 读取AO | 保持寄存器 | 16位 |
04 (0x04) | 读取AI | 输入寄存器 | 16位 |
05 (0x05) | 写入一个DO | 强制单线圈 | 离散 |
06 (0x06) | 写入一个AO | 预设单寄存器 | 16位 |
15 (0x0F) | 多DO写入 | 强制多线圈 | 离散 |
16 (0x10) | 多AO写入 | 预设多寄存器 | 16位 |
如何发送Modbus RTU命令来读取离散输出?命令0x01
此命令用于读取DO数字输出的值。
PDU请求指定第一个DO寄存器的起始地址和后续所需的DO值数量。在PDU中,DO值从零开始寻址。
响应中的DO值为一个字节,对应位的值。
位值定义为1 = ON,0 = OFF。
第一个数据字节的低位包含在请求中指定地址的DO值。其余的DO值按照增加的值到字节的最高值。即从右到左。
如果请求的DO值少于八个,则响应中的其余位将填充为零(从低字节到高字节的方向)。字节计数进一步表示响应中数据的完整字节数。
一个从20到56的DO查询示例,针对设备的SlaveID地址17。第一个寄存器的地址将是0013十六进制=19,因为帐户是从0地址开始的(0014十六进制=20, -1零偏移=我们得到0013十六进制=19)。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
01 | 功能代码 | 01 | 功能代码 |
00 | 第一个寄存器地址高字节 | 05 | 更多的字节数 |
13 | 第一个寄存器地址低字节 | CD | DO 27-20的寄存器值 (1100 1101) |
00 | 寄存器数量高字节 | 6B | DO 35-28的寄存器值 (0110 1011) |
25 | 寄存器数量低字节 | B2 | DO 43-36的寄存器值 (1011 0010) |
0E | CRC-16(Modbus) | 0E | DO 51-44的寄存器值 (0000 1110) |
84 | CRC-16(Modbus) | 1B | DO 56-52的寄存器值 (0001 1011) |
45 | CRC-16(Modbus) | ||
E6 | CRC-16(Modbus) | ||
DO 27-20的输出状态显示为字节CD十六进制值,或在二进制系统中为1100 1101。 |
在寄存器DO 56-52中,右边请求了5位,其余的位填充为零以得到完整的字节(0001 1011)。
通道 | – | – | – | DO 56 | DO 55 | DO 54 | DO 53 | DO 52 |
---|---|---|---|---|---|---|---|---|
位 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
十六进制 | 1B |
如何发送Modbus RTU命令来读取数字输入?命令0x02
此命令用于读取数字输入DI的值。
从寄存器# 10197到10218的DI请求示例,针对设备的SlaveID地址17。第一个寄存器的地址将是00C4十六进制=196,因为帐户是从0地址开始的。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
02 | 功能代码 | 02 | 功能代码 |
00 | 第一个寄存器地址高字节 | 03 | 更多的字节数 |
C4 | 第一个寄存器地址低字节 | AC | DI 10204-10197的寄存器值 (1010 1100) |
00 | 寄存器数量高字节 | DB | DI 10212-10205的寄存器值 (1101 1011) |
16 | 寄存器数量低字节 | 35 | DI 10218-10213的寄存器值 (0011 0101) |
BA | CRC-16(Modbus) | 20 | CRC-16(Modbus) |
A9 | CRC-16(Modbus) | 18 | CRC-16(Modbus) |
如何发送Modbus RTU命令来读取模拟输出?命令0x03
此命令用于读取模拟输出AO的值。
从寄存器# 40108到40110的AO请求示例,针对设备的SlaveID地址17。第一个寄存器的地址将是006B十六进制=107,因为帐户是从0地址开始的。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | ( |
Hex) | 字段名
11 | 设备地址 | 11 | 设备地址
03 | 功能代码 | 03 | 功能代码
00 | 第一个寄存器地址高字节 | 06 | 更多的字节数
6B | 第一个寄存器地址低字节 | AE | #40108的寄存器值高位
00 | 寄存器数量高字节 | 41 | #40108的寄存器值低位
03 | 寄存器数量低字节 | 56 | #40109的寄存器值高位
76 | CRC校验和 | 52 | #40109的寄存器值低位
87 | CRC校验和 | 43 | #40110的寄存器值高位
40 | #40110的寄存器值低位
49 | CRC-16(Modbus)
AD | CRC-16(Modbus)
如何发送Modbus RTU命令来读取模拟输入?命令0x04
此命令用于读取模拟输入AI的值。
从寄存器# 30009的AI请求示例,针对设备的SlaveID地址17。第一个寄存器的地址是0008十六进制=8,因为帐户是从0地址开始的。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
04 | 功能代码 | 04 | 功能代码 |
00 | 第一个寄存器地址高字节 | 02 | 更多的字节数 |
08 | 第一个寄存器地址低字节 | 00 | #30009的寄存器值高位 |
00 | 寄存器数量高字节 | 0A | #30009的寄存器值低位 |
01 | 寄存器数量低字节 | F8 | CRC-16(Modbus) |
B2 | CRC-16(Modbus) | F4 | CRC-16(Modbus) |
98 | CRC-16(Modbus) |
如何发送Modbus RTU命令来写入离散输出?命令0x05
此命令用于记录DO数字输出的一个值。
值FF 00十六进制将输出设置为ON。
值00 00十六进制将输出设置为OFF。
所有其他值都无效,且不会影响输出值。
对这样的请求的正常响应是回声(响应中的重复请求),在DO状态已更改后返回。
示例,使用设备的SlaveID地址17的寄存器# 173的DO记录。寄存器地址为00AC十六进制=172,因为从0地址开始记录。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
05 | 功能代码 | 05 | 功能代码 |
00 | 第一个寄存器地址高字节 | 00 | 第一个寄存器地址高字节 |
AC | 第一个寄存器地址低字节 | AC | 第一个寄存器地址低字节 |
FF | 高字节值 | FF | 高字节值 |
00 | 低字节值 | 00 | 低字节值 |
4E | CRC-16(Modbus) | 4E | CRC-16(Modbus) |
8B | CRC-16(Modbus) | 8B | CRC-16(Modbus) |
DO173输出状态已从OFF更改为ON。 |
如何发送Modbus RTU命令来记录模拟输出?命令0x06
此命令用于记录模拟输出AO的一个值。
示例,使用设备的SlaveID地址17的寄存器# 40002的AO记录。第一个寄存器的地址为0001十六进制=1,因为从0地址开始记录。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
06 | 功能代码 | 06 | 功能代码 |
00 | 第一个寄存器地址高字节 | 00 | 第一个寄存器地址高字节 |
01 | 第一个寄存器地址低字节 | 01 | 第一个寄存器地址低字节 |
00 | 高字节值 | 00 | 高字节值 |
03 | 低字节值 | 03 | 低字节值 |
9A | CRC-16(Modbus) | 9A | CRC-16(Modbus) |
9B | CRC-16(Modbus) | 9B | CRC-16(Modbus) |
如何发送Modbus RTU命令来写入多个离散引脚?命令0x0F
此命令用于记录DO数字输出的多个值。
示例,写入多个DO,从设备的SlaveID地址17的寄存器# 20到# 29。寄存器地址为0013十六进制=19,因为从0地址开始记录。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
0F | 功能代码 | 0F | 功能代码 |
00 | 第一个寄存器地址高字节 | 00 | 第一个寄存器地址高字节 |
13 | 第一个寄存器地址低字节 | 13 | 第一个寄存器地址低字节 |
00 | 寄存器数量高字节 | 00 | 记录的寄存器数量高字节 |
0A | 寄存器数量低字节 | 0A | 记录的寄存器数量低字节 |
02 | 更多的字节数 | 26 | CRC-16(Modbus) |
CD | DO 27-20的字节值 (1100 1101) | 99 | CRC-16(Modbus) |
01 | DO 29-28的字节值 (0000 0001) | ||
BF | CRC-16(Modbus) | ||
0B | CRC-16(Modbus) |
答案返回已记录的寄存器数量。
如何发送Modbus RTU命令来记录多个模拟输出?命令0x10
此命令用于记录模拟输出AO的多个值。
示例,记录多个AO,从设备的SlaveID地址17的寄存器# 40002和# 40003。第一个寄存器的地址为0001十六进制=1,因为从0地址开始记录。
字节 | 请求 | 字节 | 回复 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
11 | 设备地址 | 11 | 设备地址 |
10 | 功能代码 | 10 | 功能代码 |
00 | 第一个寄存器地址高字节 | 00 | 第一个寄存器地址高字节 |
01 | 第一个寄存器地址低字节 | 01 | 第一个寄存器地址低字节 |
00 | 寄存器数量高字节 | 00 | 记录的寄存器数量高字节 |
02 | 寄存器数量低字节 | 02 | 记录的寄存器数量低字节 |
04 | 更多的字节数 | 12 | CRC-16(Modbus) |
00 | 40002高位值 | 98 | CRC-16(Modbus) |
0A | 40002低位值 | ||
01 | 40003高位值 | ||
02 | 40003低位值 | ||
C6 | CRC-16(Modbus) | ||
F0 | CRC-16(Modbus) |
![深入解析Modbus RTU命令与应答机制插图5 深入解析Modbus RTU命令与应答机制插图5](https://www.modbus.cn/wp-content/themes/module2/themer/assets/images/lazy.png)
Modbus请求的错误
如果设备接收到一个请求,但无法处理该请求,该设备将以错误代码进行响应。
响应将包含修改后的功能代码,高位字节为1。
示例:
原始值 | 结果值 |
---|---|
请求中的功能代码 | 响应中的功能错误代码 |
01 (01 hex) 0000 0001 | 129 (81 hex) 1000 0001 |
02 (02 hex) 0000 0010 | 130 (82 hex) 1000 0010 |
03 (03 hex) 0000 0011 | 131 (83 hex) 1000 0011 |
04 (04 hex) 0000 0100 | 132 (84 hex) 1000 0100 |
05 (05 hex) 0000 0101 | 133 (85 hex) 1000 0101 |
06 (06 hex) 0000 0110 | 134 (86 hex) 1000 0110 |
15 (0F hex) 0000 1111 | 143 (8F hex) 1000 1111 |
16 (10 hex) 0001 0000 | 144 (90 hex) 1001 0000 |
带有错误的请求和响应示例:
字节 | 请求 | 字节 | 响应 |
---|---|---|---|
(Hex) | 字段名 | (Hex) | 字段名 |
0A | 设备地址 | 0A | 设备地址 |
01 | 功能代码 | 81 | 已更改的功能代码 |
04 | 第一个寄存器地址高字节 | 02 | 错误代码 |
A1 | 第一个寄存器地址低字节 | B0 | CRC-16(Modbus) |
00 | 寄存器数量高字节 | 53 | CRC-16(Modbus) |
01 | 寄存器数量低字节 | ||
AC | CRC-16(Modbus) | ||
63 | CRC-16(Modbus) |
错误代码说明
01 | 不能处理已接受的功能代码。
02 | 请求中指定的数据地址不可用。
03 | 查询数据字段中包含的值是无效值。
04 | 从站尝试执行请求的操作时发生不可恢复的错误。
05 | 从站已接受请求并正在处理,但需要较长时间。此响应防止主机生成超时错误。
06 | 从站正忙于处理命令。主机在从站空闲时必须重复消息。
07 | 从站无法执行请求中指定的程序功能。使用功能编号13或14的不成功的程序请求返回此代码。主机必须从从站请求诊断信息或错误信息。
08 | 从站在读取扩展内存时检测到奇偶校验错误。主机可以重复请求,但通常在这种情况下需要维修。