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校验流程:

  1. 预置一个16位寄存器为0FFFFH(全1),称之为CRC寄存器。
  2. 把数据帧中的第一个字节的8位与CRC寄存器中的低字节进行异或运算,结果存回CRC寄存器。
  3. 将CRC寄存器向右移一位,最高位填以0,最低位移出并检测。
  4. 如果最低位为0:重复第三步(下一次移位);如果最低位为1:将CRC寄存器与一个预设的固定值(0A001H)进行异或运算。
  5. 重复第三步和第四步直到8次移位。这样处理完了一个完整的八位。
  6. 重复第2步到第5步来处理下一个八位,直到所有的字节处理结束。
  7. 最终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

从站设备故障

当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐