Modbus协议是一种请求/应答方式的交互过程,主机主动发起通讯请求,从机响应主机的请求,从机在没有收到主机的请求时,不会主动发送数据,从机之间不会进行通讯。
一、Modbus 4种数据类型/寄存器分类
寄存器种类 | 读写状态 | 数据类型 | 功能码 | PLC地址 |
线圈寄存器 | 读/写 | 位(bit) | 01H(读单/多个位);05H(写单个位);0FH(写多个位); | 00001-09999 |
离散输入寄存器 | 只读 | 位(bit) | 02H(读单/多个位) | 10001-19999 |
保持寄存器 | 读/写 | 字(byte) | 03H(读);06H(写单个字节);0FH(写多个字节) | 30001-39999 |
输入寄存器 | 只读 | 字(byte) | 04H(读单/多个字) | 40001-49999 |
二、Modbus数据帧格式
RTU帧
起始位 | 设备地址 | 功能码 | 数据 | CRC校验 | 结束符 |
T1-T2-T3-T4 | 8Bit | 8Bit | n个8Bit | 16Bit | T1-T2-T3-T4 |
不超过256字节。
三、Modbus功能码/异常码/错误码
Modbus功能码
功能码 | 说明 |
01 | 读取线圈状态 |
02 | 读取输入状态 |
03 | 读取保持寄存器 |
04 | 读取输入寄存器 |
05 | 强置单线圈 |
06 | 预置单寄存器 |
07 | 读取异常状态 |
08 | 回送诊断校验 |
09 | 编程(只用于 484) |
10 | 控询 |
11 | 读取事件计数 |
12 | 读取通信事件记录 |
13 | 编程(184/384/484/584 等) |
14 | 探寻 |
15 | 强置多线圈 |
16 | 预置多线圈 |
17 | 报告多寄存器 |
18 | 可使主机模拟编程功能 |
19 | 重置通信链路 |
20 | 读取通用参数 |
21 | 写入通用参数 |
22 | 屏蔽写寄存器 |
23 | 读 / 写多个寄存器 |
43 | 读设备别识码 |
22-42,44-64 | 保留作为扩展功能 |
65-72 | 保留以备用功能所用 |
73-119 | 非法功能 |
120-127 | 保留,留作内部作用 |
128-255 | 保留,用于异常应答 |
Modbus异常码
(ExceptionCode)
ExceptionCode: Function Code 的最左边 Bit 设定为 1
举例:86 01, 功能码 06 最左边 Bit 设定为 1,即为 86
参照下表
Modbus 错误码(10 进制)
(ErrorCode)
功能码 | 说明 |
01 | 非法功能。对于服务器(或从站)来说,询问中接收到的功能码是不可允许的操作,可能是因为功能码仅适用于新设备而被选单元中不可实现同时,还指出服务器(或从站)在错误状态中处理这种请求,例如:它是未配置的,且要求返回寄存器值。 |
02 | 非法数据地址。对于服务器(或从站)来说,询问中接收的数据地址是不可允许的地址,特别是参考号和传输长度的组合是无效的。对于带有 100 个寄存器的控制器来说,偏移量 96 和长度 4 的请求会成功,而偏移量 96 和长度 5 的请求将产生异常码 02。 |
03 | 非法数据值。对于服务器(或从站)来说,询问中包括的值是不可允许的值。该值指示了组合请求剩余结构中的故障。例如:隐含长度是不正确的。modbus 协议不知道任何特殊寄存器的任何特殊值的重要意义,寄存器中被提交存储的数据项有一个应用程序期望之外的值。 |
04 | 从站设备故障。当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。 |
05 | 确认。与编程命令一起使用,服务器(或从站)已经接受请求,并且正在处理这个请求,但是需要长持续时间进行这些操作,返回这个响应防止在客户机(或主站)中发生超时错误,客户机(或主机)可以继续发送轮询程序完成报文来确认是否完成处理。 |
06 | 从属设备忙。与编程命令一起使用。服务器 (或从站) 正在处理长持续时间的程序命令。张服务器 (或从站) 空闲时,用户 (或主站) 应该稍后重新传输报文。 |
08 | 存储奇偶差错。与功能码 20 和 21 以及参考类型 6 一起使用,指示扩展文件区不能通过一致性校验。服务器 (或从站) 设法读取记录文件,但是在存储器中发现一个奇偶校验错误。客户机 (或主方) 可以重新发送请求,但可以在服务器 (或从站) 设备上要求服务。 |
10 | 不可用网关路径。与网关一起使用,指示网关不能为处理请求分配输入端口至输出端口的内部通信路径。通常意味着网关是错误配置的或过载的。 |
11 | 网关目标设备响应失败。与网关一起使用,指示没有从目标设备中获得响应。通常意味着设备未在网络中。 |
四、Modbus常用功能码应用举例
MODBUS协议相当复杂,但是常用的命令也就简单的几个,01,02,03,04,05,06,15,16号命令。
各个命令的功能和报文如下:
01 命令 读取线圈状态 MODBUS地址 00001~
MODBUS 请求
功能码 | 1 BYTE | 0X01 |
起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
读取数量 | 2 BYTE | 1 TO 2000(0X7D0) |
MODBUS 响应
功能码 | 1 BYTE | 0X01 |
字节计数 | 1 BYTE | N |
线圈状态 | n BYTE | n =N or N+1 |
N =读取数量/8 如果余数不为0 则N=N+1
错误 响应
功能码 | 1 BYTE | 0X01+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 01 | 功能码 | 01 |
起始地址高(字节) | 00 | 字节计数 | 03 |
起始地址低(字节) | 13 | 27(h)~20状态 | CD |
读取数量高(字节) | 00 | 35(h)~28状态 | 6B |
读取数量低(字节) | 13 | 38(h)~36状态 | 05 |
02 命令 读取输入状态 MODBUS地址 10001~
MODBUS 请求
功能码 | 1 BYTE | 0X02 |
起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
读取数量 | 2 BYTE | 1 TO 2000(0X7D0) |
MODBUS 响应
功能码 | 1 BYTE | 0X02 |
字节计数 | 1 BYTE | N |
输入状态 | n BYTE | n =N or N+1 |
N =读取数量/8 如果余数不为0 则N=N+1
错误 响应
功能码 | 1 BYTE | 0X02+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 02 | 功能码 | 02 |
起始地址高(字节) | 00 | 字节计数 | 03 |
起始地址低(字节) | C4 | 204(h)~197状态 | AC |
读取数量高(字节) | 00 | 212(h)~205状态 | DB |
读取数量低(字节) | 16 | 218(h)~213状态 | 35 |
03 读保持寄存器 MODBUS地址 40001~
MODBUS 请求
功能码 | 1 BYTE | 0X03 |
起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
读取数量 | 2 BYTE | 1 TO 125(0X7D) |
MODBUS 响应
功能码 | 1 BYTE | 0X03 |
字节计数 | 1 BYTE | N*2 |
输入状态 | N*2 BYTE |
|
错误 响应
功能码 | 1 BYTE | 0X03+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 03 | 功能码 | 03 |
起始地址高(字节) | 00 | 字节计数 | 06 |
起始地址低(字节) | 6B | 寄存器高(108) | 02 |
读取数量高(字节) | 00 | 寄存器低(108) | 2B |
读取数量低(字节) | 03 | 寄存器高(109) | 00 |
| 寄存器低(109) | 00 | |
寄存器高(110) | 00 | ||
寄存器低(110) | 64 |
04 输入寄存器 MODBUS地址 30001~
MODBUS 请求
功能码 | 1 BYTE | 0X04 |
起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
读取数量 | 2 BYTE | 1 TO 125(0X7D) |
MODBUS 响应
功能码 | 1 BYTE | 0X04 |
字节计数 | 1 BYTE | N*2 |
输入状态 | N*2 BYTE |
|
错误 响应
功能码 | 1 BYTE | 0X04+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 04 | 功能码 | 04 |
起始地址高(字节) | 00 | 字节计数 | 02 |
起始地址低(字节) | 08 | 输入寄存器高(9) | 00 |
读取数量高(字节) | 00 | 输入寄存器低(9) | 0A |
读取数量低(字节) | 01 | ||
05 设置单个继电器状态
MODBUS 请求
功能码 | 1 BYTE | 0X05 |
设置地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置内容 | 2 BYTE | 0x0000 OR 0XFF00 0x0000 释放继电器 0xff00 吸合继电器 |
MODBUS 响应
功能码 | 1 BYTE | 0X05 |
设置地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置内容 | 2 BYTE | 0x0000 OR 0XFF00 |
错误 响应
功能码 | 1 BYTE | 0X05+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例(吸合6号继电器)
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 05 | 功能码 | 05 |
设置地址高(字节) | 00 | 设置地址高(字节) | 00 |
设置地址低(字节) | 05 | 设置地址低(字节) | 05 |
设置内容高(字节) | FF | 设置内容高(字节) | FF |
设置内容低(字节) | 00 | 设置内容低(字节) | FF |
06 设置单个保持寄存器
MODBUS 请求
功能码 | 1 BYTE | 0X06 |
设置地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置内容 | 2 BYTE | 0x0000 to 0XFF00 |
MODBUS 响应
功能码 | 1 BYTE | 0X06 |
设置地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置内容 | 2 BYTE | 0x0000 to 0XFF00 |
错误 响应
功能码 | 1 BYTE | 0X06+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
设置9号保持寄存器内容为25
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 06 | 功能码 | 06 |
设置地址高(字节) | 00 | 设置地址高(字节) | 00 |
设置地址低(字节) | 08 | 设置地址低(字节) | 08 |
设置内容高(字节) | 00 | 设置内容高(字节) | 00 |
设置内容低(字节) | 19 | 设置内容低(字节) | 19 |
15 设置多个继电器状态
MODBUS 请求
功能码 | 1 BYTE | 0X0F |
设置起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置长度 | 2 BYTE | 0X0000 TO 0X7B0 |
字节计数 | 1 BYTE | N |
设置内容 | N BYTE |
|
MODBUS 响应
功能码 | 1 BYTE | 0X0F |
设置起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置长度 | 2 BYTE | 0X0000 TO 0X7B0 |
错误 响应
功能码 | 1 BYTE | 0X0F+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
设置继电器
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 0F | 功能码 | 0F |
设置地址高(字节) | 00 | 设置地址高(字节) | 00 |
设置地址低(字节) | 13 | 设置地址低(字节) | 13 |
设置数量高(字节) | 00 | 设置数量高(字节) | 00 |
设置数量低(字节) | 0A | 设置数量低(字节) | 0A |
字节计数 | 02 | ||
设置内容高(字节) | CD | ||
设置内容低(字节) | 01 | ||
16 设置多个保持寄存器
MODBUS 请求
功能码 | 1 BYTE | 0X10 |
设置起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置长度 | 2 BYTE | 0X0000 TO 0X7B0 |
字节计数 | 1 BYTE | N*2 |
设置内容 | N*2 BYTE |
|
MODBUS 响应
功能码 | 1 BYTE | 0X10 |
设置起始地址 | 2 BYTE | 0X0000 TO 0XFFFF |
设置长度 | 2 BYTE | 0X0000 TO 0X7B0 |
错误 响应
功能码 | 1 BYTE | 0X10+ 0X80 |
错误代码 | 1 BYTE | 0x1 or 0x2 or 0x3 or 0x4 |
举例
设置多个保持寄存器
请求 | 响应 | ||
域名称 | 数据(hex) | 域名称 | 数据(hex) |
功能码 | 10 | 功能码 | 0F |
设置地址高(字节) | 00 | 设置地址高(字节) | 00 |
设置地址低(字节) | 01 | 设置地址低(字节) | 01 |
设置数量高(字节) | 00 | 设置数量高(字节) | 00 |
设置数量低(字节) | 02 | 设置数量低(字节) | 02 |
字节计数 | 04 | ||
设置内容高(字节) | 00 | ||
设置内容低(字节) | 0A | ||
设置内容高(字节) | 01 | ||
设置内容低(字节) | 02 | ||
MODBUS协议在智能设备中的应用
上面讲述了MODBUS协议的报文以及命令,那么在智能设备中如何使用这个协议呢?
如果智能设备有开关量输入输出,模拟量输入输出,有计数器等。很明显开关量输入可以映射到 10001地址,第一路开关量输入为10001,第二路为10002,………
开关量输出映射到 00001地址,第一路为00001,第二路为00002,…….
模拟量输入映射到 30001地址,第一路为 30001,第二路为30002,……
模拟量输出和计数器输入映射到40001地址,第一路为 40001,第二路为40002,……
当然也可以把所有的数据都放在保持寄存器中,这样对于MODBUS主设备访问时要简单,访问效率能提高,但是处理起来略显繁琐。