Modbus 简介
modbus 报文解析
1 简介
1.1 Modbus 存储区
存储的数据类型分为 :布尔量 和 16位寄存器
- 布尔量 比如IO口的电平高低,灯的开关状态等。
- 16位寄存器 比如 传感器的温度数据,存储的密码等。
Modbus协议规定了4个存储区 分别是0 1 3 4区 其中0区和4区是可读可写,1区和3区是只读。
区号 | 名称 | 读写 | 地址范围 |
0区 | 输出线圈 | 可读可写布尔量 | 00001-09999 |
1区 | 输入线圈 | 只读布尔量 | 10001-19999 |
4区 | 保持寄存器 | 可读可写寄存器 | 40001-49999 |
3区 | 输入寄存器 | 只读寄存器 | 30001-39999 |
1.2 Modbus 功能码
Modbus协议同时规定了二十几种功能码,但是常用的只有8种,用于对存储区的读写,如下表所示:
功能码 | 功能说明 |
01H | 读取输出线圈 |
02H | 读取输入线圈 |
03H | 读取保持寄存器 |
04H | 读取输入寄存器 |
05H | 写入单线圈 |
06H | 写入单寄存器 |
0FH | 写入多线圈 |
10H | 写入多寄存器 |
1.3 CRC校验
错误校验(CRC)域占用两个字节包含了一个16位的二进制值。CRC值由传输设备计算出来,然后附加到数据帧上,接收设备在接收数据时重新计算CRC值,然后与接收到的CRC域中的值进行比较,如果这两个值不相等,就发生了错误。
比如主机发出01 06 00 01 00 17 98 04, 98 04 两个字节是校验位,那么从机接收到后要根据01 06 00 01 00 17 再计算CRC校验值,从机判断自己计算出来的CRC校验是否与接收的CRC校验(98 04主机计算的)相等,如果不相等那么说明数据传输有错误,这些数据就不能要。
CRC校验流程:
- 预置一个16位寄存器为0FFFFH(全1),称之为CRC寄存器。
- 把数据帧中的第一个字节的8位与CRC寄存器中的低字节进行异或运算,结果存回CRC寄存器。
- 将CRC寄存器向右移一位,最高位填以0,最低位移出并检测。
- 如果最低位为0:重复第三步(下一次移位);如果最低位为1:将CRC寄存器与一个预设的固定值(0A001H)进行异或运算。
- 重复第三步和第四步直到8次移位。这样处理完了一个完整的八位。
- 重复第2步到第5步来处理下一个八位,直到所有的字节处理结束。
- 最终CRC寄存器的值就是CRC的值。
2 Modbus-RTU协议
2.1 报文帧结构
从站地址 | 功能码 | 数据 | CRC/LRC |
1 byte | 1 byte | N byte | 2 byte |
帧结构 = 从机地址 + 功能吗 + 数据 + 校验
- 从机地址 每个从机都有唯一地址,占用一个字节,范围0-255,其中有效范围是1-247,其中255是广播地址(广播就是对所有从机发送应答)
- 功能码 占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改从机的数据,所以不同功能码对应不同功能.
- 数据 根据功能码不同,有不同功能,比方说功能码是查询从机的数据,这里就是查询数据的地址和查询字节数等。
- 校验 在数据传输过程中可能数据会发生错误,CRC检验检测接收的数据是否正确
2.2 读数据 主机->从机
从站地址 | 功能码 | 起始(高) | 起始(低) | 数量(高) | 数量(低) | 校验 |
0x01 | 0x03 | 0x00 | 0x01 | 0x00 | 0x01 | 0xD5 0xCA |
- 0x01 从机的地址
- 0x03 查询功能,读取从机寄存器的数据
- 0x00 0x01 代表读取的起始寄存器地址.说明从0x0001开始读取.
- 0x00 0x01 查询的寄存器数量为0x0001个, Modbus把数据存放在寄存器中,通过查询寄存器来得到不同变量的值,一个寄存器地址对应2字节数据; 寄存器地址对应着从机实际的存储地址
- 0xD5 0xCA 循环冗余校验 CRC
从机回复报文
从站地址 | 功能码 | 字节计数 | 字节1 | 字节2 | 校验 |
0x01 | 0x03 | 0x02 | 0x00 | 0x17 | 0xF8 0x4A |
- 0x01 从机的地址
- 0x03 查询功能,读取从机寄存器的数据
- 0x02 返回字节数为2个 一个寄存器2个字节
- 0x00 0x17 寄存器的值是0017
- 0xF8 0x4A 循环冗余校验 CRC
2.3 写数据 主机->从机
从站地址 | 功能码 | 数据地(高) | 数据地址(低) | 数据(高) | 数据(低) | 校验 |
0x01 | 0x06 | 0x00 | 0x01 | 0x00 | 0x62 | 0x59 0xE3 |
- 0x01 从机的地址
- 0x06 修改功能,修改从机寄存器的数据
- 0x00 0x01 代表修改的起始寄存器地址.说明修改0x0000-0x0001的存储内容
- 0x00 0x62 要修改的数据值为98
- 0x59 0xE3 循环冗余校验 CRC
从机回复报文
从站地址 | 功能码 | 数据地址(高) | 数据地址(低) | 数据(高) | 数据(低) | 校验 |
0x01 | 0x06 | 0x00 | 0x01 | 0x00 | 0x62 | 0x59 0xE3 |
- 0x01 从机的地址
- 0x06 修改功能,修改从机寄存器的数据
- 0x00 0x01 代表修改的起始寄存器地址.说明是0x0000
- 0x00 0x62 修改的值为98
- 0x59 0xE3 循环冗余校验 CRC
3 Modbus-TCP协议
3.1 报文帧结构
帧结构 = MBAP(报文头)+ PDU(帧结构)
MBAP(报文头)
事务标志符(2字节) | 协议标识(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 |
- 事务标识符 可以解释为报文的序列号,由于我们测试使用的Modbus Poll客户端是一直发送数据,所以每发送一次数据标识符就加一。服务器接收时会把这个数据原封返回。
- 协议标识符 0x00 0x00代表Modbus TCP协议。
- 长度 表示从单元标识符开始后面数据的长度。如:0x00 0x06表示后面有6个字节长度的数据。
- 单元标识符 相当于设备的地址。一般为01
PDU(帧结构)
功能码 | 数据 |
0x01 | 视功能而定 |
3.2 读输出线圈
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 数据地址(高) | 数据地址(低) | 数量(高) | 数量(低) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x01 | 0x00 | 0x01 | 0x00 | 0x0A |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x06 表示从单元标识符开始后面数据的长度有6位
- 0x01 从站地址:1
- 0x01 功能码:读线圈
- 0x00 0x00 数据地址:读取数据的开始地址索引为 0x0001 = 1,对应的实际地址为00002
- 0x00 0x0A 读取数据数量,0x0A = 10
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 字节计数 | 字节(N) |
0x00 0x00 | 0x00 0x00 | 0x00 0x05 | 0x01 | 0x01 | 0x02 | 0xB7 0x03 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x07 表示从单元标识符开始后面数据的长度有7位
- 0x01 从站地址:1
- 0x01 功能码:读线圈
- 0x02 返回字节计数:2
- 0xB7 返回字节:0xB7 = 1011 0111 对应 00009-00002
- 0x03 返回字节:0x03 = 11 对应 00011-00010
3.3 读输入线圈
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 数据地址(高) | 数据地址(低) | 数量(高) | 数量(低) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x02 | 0x00 | 0x01 | 0x00 | 0x0A |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x06 表示从单元标识符开始后面数据的长度有6位
- 0x01 从站地址:1
- 0x02 功能码:读取输入线圈
- 0x00 0x01 数据地址:读取数据的开始地址索引为 0x0001 =1,对应的实际地址为10002
- 0x00 0x0A 读取数据数量,0x000A = 10
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 字节计数 | 字节(N) |
0x00 0x00 | 0x00 0x00 | 0x00 0x05 | 0x01 | 0x02 | 0x02 | 0x0F 0x01 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x05 表示从单元标识符开始后面数据的长度有5位
- 0x01 从站地址:1
- 0x02 功能码:读取输入线圈
- 0x02 返回字节计数:2
- 0x0F 返回字节:0x0F = 0000 1111 对应 10009-10001
- 0x01 返回字节:0x01 = 01 对应 10011-10010
3.4 读取保持寄存器
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 数据地址(高) | 数据地址(低) | 数量(高) | 数量(低) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x03 | 0x00 | 0x01 | 0x00 | 0x02 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x06 表示从单元标识符开始后面数据的长度有6位
- 0x01 从站地址:1
- 0x03 功能码:读取保持寄存器
- 0x00 0x01 数据地址:读取数据的开始地址索引为 0x0001 =1,对应的实际地址为40002
- 0x00 0x02 读取数据数量,0x0002 = 2
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 字节计数 | 字节1(高) | 字节1(低) | 字节2(高) | 字节2(低) |
0x00 0x00 | 0x00 0x00 | 0x00 0x07 | 0x01 | 0x03 | 0x04 | 0x00 | 0x3C | 0x01 | 0x00 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x07 表示从单元标识符开始后面数据的长度有7位
- 0x01 从站地址:1
- 0x03 功能码:读取保持寄存器
- 0x04 返回字节计数:4
- 0x00 0x3C 返回字节:地址40002对应数据 0x003C = 60
- 0x01 0x00 返回字节:地址40003对应数据 0x0100 = 256
3.5 读取输入寄存器
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 数据地址(高) | 数据地址(低) | 数量(高) | 数量(低) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x04 | 0x00 | 0x01 | 0x00 | 0x02 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x06 表示从单元标识符开始后面数据的长度有6位
- 0x01 从站地址:1
- 0x04 功能码:读取输入寄存器
- 0x00 0x01 数据地址:读取数据的开始地址索引为 0x0001 =1,对应的实际地址为30002
- 0x00 0x02 读取数据数量,0x0002 = 2
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 字节计数 | 字节1(高) | 字节1(低) | 字节2(高) | 字节2(低) |
0x00 0x00 | 0x00 0x00 | 0x00 0x07 | 0x01 | 0x04 | 0x04 | 0x02 | 0x00 | 0x00 | 0x6D |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x07 表示从单元标识符开始后面数据的长度有7位
- 0x01 从站地址:1
- 0x04 功能码:读取输入寄存器
- 0x04 返回字节计数:4
- 0x02 0x00 返回字节:地址30002对应数据 0x0200 = 512
- 0x00 0x6D 返回字节:地址30003对应数据 0x006D = 109
3.6 写入单输出线圈
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 线圈地址(高) | 线圈地址(低) | 断通标识(2字节) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x05 | 0x00 | 0x01 | 0xFF 0x00 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x06 表示从单元标识符开始后面数据的长度有6位
- 0x01 从站地址:1
- 0x05 功能码:写入单输出线圈
- 0x00 0x01 线圈地址:写入数据的开始地址索引为 0x0001 =1,对应的实际地址为00002
- 0xFF 0x00 写入数据值:0xFF00 - 表示置ON/1状态, 0x0000 - 表示置OFF/0状态
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 线圈地址(高) | 线圈地址(低) | 断通标识(2字节) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x05 | 0x00 | 0x01 | 0xFF 0x00 |
写入成功返回原报文
3.7 写入多输出线圈
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 线圈地址(2字节) | 数量(2字节) | 字节数 | 字节 |
0x00 0x00 | 0x00 0x00 | 0x00 0x09 | 0x01 | 0x0F | 0x00 0x01 | 0x00 0x0A | 0x02 | 0x0F 0x01 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x09 表示从单元标识符开始后面数据的长度有9位
- 0x01 从站地址:1
- 0x0F 功能码:写入多输出线圈
- 0x00 0x01 线圈地址:写入数据的开始地址索引为 0x0001 =1,对应的实际地址为00002
- 0x00 0x0A 写入线圈数:0x000A = 10
- 0x02 字节数:0x02 = 2
- 0x0F 0x01 字节:0x0F=0000 1111 写入到 00009-00001,0x01 = 01 写入到 00011-00010
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 线圈地址(2字节) | 数量(2字节) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x0F | 0x00 0x01 | 0x00 0x0A |
写入成功返回,在原报文的基础上移除字节数、字节
3.8 写入单保持寄存器
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 寄存器地址(高) | 寄存器地址(低) | 写入值 |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x06 | 0x00 | 0x01 | 0x15 0xB3 |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x06 表示从单元标识符开始后面数据的长度有6位
- 0x01 从站地址:1
- 0x06 功能码:写入单保持寄存器
- 0x00 0x01 寄存器地址:写入数据的开始地址索引为 0x0001 =1,对应的实际地址为40002
- 0x15 0xB3 写入数据值:0x15B3 = 5555
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 寄存器地址(高) | 寄存器地址(低) | 写入值 |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x06 | 0x00 | 0x01 | 0x15 0xB3 |
写入成功返回原报文
3.9 写入多保持寄存器
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 寄存器地址(2字节) | 数量 (2字节) | 字节数 | 字节 |
0x00 0x00 | 0x00 0x00 | 0x00 0x0B | 0x01 | 0x10 | 0x00 0x01 | 0x00 0x02 | 0x04 | 0x03 0xE8 0x04 0x4C |
- 0x00 0x00 事务标识符,报文的序列号
- 0x00 0x00 协议标识符,Modbus TCP协议
- 0x00 0x0B 表示从单元标识符开始后面数据的长度有11位
- 0x01 从站地址:1
- 0x10 功能码:写入多保持寄存器
- 0x00 0x01 寄存器地址:写入数据的开始地址索引为 0x0001 =1,对应的实际地址为40002
- 0x00 0x02 写入寄存器数:0x0002 = 2
- 0x04 字节数:0x04 = 4
- 0x03 0xE8 字节:0x03E8 = 1000 写入到 40002
- 0x04 0x4C 字节:0x044C = 1100 写入到 40003
从机回复报文
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 线圈地址(2字节) | 数量(2字节) |
0x00 0x00 | 0x00 0x00 | 0x00 0x06 | 0x01 | 0x10 | 0x00 0x01 | 0x00 0x02 |
写入成功返回,在原报文的基础上移除字节数、字节
4 错误响应
当发生通讯异常时,响应前7位仍然为modbus正常协议格式,第八位响应功能码(请求功能码+0x80),第九位错误码。
事务标志符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(从站地址 1字节) | 功能码 | 错误码 |
0x00 0x00 | 0x00 0x00 | 0x00 0x03 | 0x01 | 0x90 | 0x02 |
- 0x90 功能码 = 请求功能码+0x80
错误码 | 简称 | 说明 |
0x01 | 非法功能 | 对于服务器(或从站)来说,询问中接收到的功能码是不可允许的操作,可能是因为功能码仅适用于新设备而被选单元中不可实现同时,还指出服务器(或从站)在错误状态中处理这种请求,例如:它是未配置的,且要求返回寄存器值。 |
0x02 | 地址非法 | 对于服务器(或从站)来说,询问中接收的数据地址是不可允许的地址,特别是参考号和传输长度的组合是无效的。对于带有100个寄存器的控制器来说,偏移量96和长度4的请求会成功,而偏移量96和长度5的请求将产生异常码02。 |
0x03 | 数据非法 | 对于服务器(或从站)来说,询问中包括的值是不可允许的值。该值指示了组合请求剩余结构中的故障。例如:隐含长度是不正确的。modbus协议不知道任何特殊寄存器的任何特殊值的重要意义,寄存器中被提交存储的数据项有一个应用程序期望之外的值。 |
0x04 | 从站设备故障 | 当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。 |
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)