1、MODBUS协议的基本概念
Modbus协议是一种用于工业控制的网络通讯协议
请求信息的设备被称为 Modbus 客户端,提供信息的设备是 Modbus 服务器。Modbus 支持单主机,多个从机,在标准 Modbus 网络中,可以有一个客户端和多达 247个服务器,每个服务器都有从 1 到 247 的唯一服务器地址(注意:0通常是保留的,不能用于设备)。

特点
- 标准、开放、免费
- 支持多种电器接口,如串行接口RS-232、RS-485等,还可以在各种介质上传递,如:光纤、无线等
- Modbus的帧格式简单、紧凑、通俗易懂。用户使用简单,厂商开发简单。
2、传输模式
Modbus 协议主要有三种标准传输形式:Modbus RTU、Modbus ASCII 和 Modbus TCP/IP。
Modbus ASCII 和 Modbus RTU 是基于串行通信的协议, Modbus TCP/IP 是基于以太网的协议。
Modbus RTU 是一种二进制协议,使用二进制码表示数据。它采用起始字符、从站地址、功能码、数据等字段来定义通信内容,并使用 CRC 校验位来保证数据的完整性。Modbus RTU 通常通过 RS-232、RS-485 或 RS-422 等串行通信接口进行传输。
Modbus ASCII 是一种文本协议,使用 ASCII 码表示数据。它使用起始字符(“:”)、从站地址、功能码、数据、结尾字符(换行符 CR/LF)等字段来定义通信内容,并采用的是 LRC 校验算法。数据以 ASCII 码的形式传输,通常是通过 RS-232 或 RS-485 等串行通信接口进行传输。
Modbus TCP/IP 是一种基于以太网的协议,使用 TCP/IP 协议栈进行通信。它使用以太网帧作为数据传输的封装,通过 IP 地址和端口号来标识设备。其占用的是 502 端口,数据帧主要包括两部分:MBAP(报文头)+PDU(帧结构),数据块与串行链路是一致的。Modbus TCP/IP 可以通过以太网、无线局域网等网络介质实现设备之间的远程通信。
3、Modbus数据帧格式
无论是三种传输模式中的哪一种,Modbus 帧格式都是一样的:

帧结构 = 地址域 + 功能码+ 数据 + 差错校验
- 地址域:1 字节,即主机要访问的从机设备地址,通常 1-247 为有效地址,0 为广播地址。
- 功能码:1 字节,表明主机请求从机数据的类型。不同的功能码对应操作不同类型的寄存器。比如:0x01读线圈、0x03读保持寄存器、0x06写单个寄存器、0x10写多个寄存器等。
- 数据:N 字节,包含寄存器地址、寄存器数量、数据内容等。根据功能的不同,以及传输的数据为请求数据还是响应数据的不同,会有不同的内容。
- 差错校验:为保障传输数据的准确性,modbus会进行差错校验,如Modbus CRC16校验等。
4、功能码
最常用的两个功能码(03 06 10)
01 (0x01) 读线圈
在一个远程设备中,使用该功能码读取线圈的 1 至 2000 连续状态。请求 PDU 详细说明了起始地址,即指定的第一个线圈地址和线圈编号。从零开始寻址线圈。因此寻址线圈 1-16 为 0-15。
根据数据域的每个比特将响应报文中的线圈分成为一个线圈。指示状态为 1= ON 和 0= OFF。
第一个数据字节的 LSB(最低有效位)包括在询问中寻址的输出。其它线圈依次类推,一直到这个字节的高位端为止,并在后续字节中从低位到高位的顺序。
如果返回的输出数量不是八的倍数,将用零填充最后数据字节中的剩余比特(一直到字节的高位端)。字节数量域说明了数据的完整字节数。
请求
响应PDU
*N=输出数量/8,如果余数不等于 0,那么N = N+1
错误
举例1
这是一个请求读离散量输出 20-38 的实例:
将输出 27-20 的状态表示为十六进制字节值 CD,或二进制 1100 1101。输出 27 是这个字节的MSB,输出 20 是 LSB。
通常,将一个字节内的比特表示为 MSB 位于左侧,LSB 位于右侧。第一字节的输出从左至右为 27 至 20。下一个字节的输出从左到右为 35 至 28。当串行发射比特时,从 LSB 向 MSB 传输:20 . . .27、28 . . . 35 等等。
在最后的数据字节中,将输出状态 38-36 表示为十六进制字节值 05,或二进制 0000 0101。输出 38 是左侧第六个比特位置,输出 36 是这个字节的 LSB。用零填充五个剩余高位比特。
注:用零填充五个剩余比特(一直到高位端)。

02 (0x02) 读离散量输入
在一个远程设备中,使用该功能码读取离散量输入的 1 至 2000 连续状态。请求 PDU 详细说明了起始地址,即指定的第一个输入地址和输入编号。从零开始寻址输入。因此寻址输入 1-16 为 0-15。
根据数据域的每个比特将响应报文中的离散量输入分成为一个输入。指示状态为 1= ON 和 0= OFF。第一个数据字节的 LSB(最低有效位)包括在询问中寻址的输入。其它输入依次类推,一直到这个字节的高位端为止,并在后续字节中从低位到高位的顺序。
如果返回的输入数量不是八的倍数,将用零填充最后数据字节中的剩余比特(一直到字节的高位端)。字节数量域说明了数据的完整字节数。
请求
响应PDU
错误
举例1
这是一个请求读取离散量输入 197-218 的实例:
将离散量输入状态 204-197 表示为十六进制字节值 AC,或二进制 1010 1100。输入 204 是这个字节的 MSB,输入 197 是这个字节的 LSB。
将离散量输入状态 218-213 表示为十六进制字节值 35,或二进制 0011 0101。输入 218 位于左侧第 3 比特,输入 213 是 LSB。

03 (0x03)读保持寄存器
在一个远程设备中,使用该功能码读取保持寄存器连续块的内容。请求 PDU 说明了起始寄存器地址和寄存器数量。从零开始寻址寄存器。因此,寻址寄存器 1-16 为 0-15。
将响应报文中的寄存器数据分成每个寄存器有两字节,在每个字节中直接地调整二进制内容。
对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。
请求
响应PDU
错误
举例1
请求读寄存器 108-110 的实例:
将寄存器 108 的内容表示为两个十六进制字节值 02 2B,或十进制 555。将寄存器 109-110 的内容分别表示为十六进制 00 00 和 00 64,或十进制 0 和 100。
举例2
主机发送: 01 03 00 00 00 01 84 0A
从机回复: 01 03 02 19 98 B2 7E
主机向01地址的从机发送请求
01地址的从机响应主机请求
读保持寄存器的状态图

04 (0x04)读输入寄存器
在一个远程设备中,使用该功能码读取 1 至大约 125 的连续输入寄存器。请求 PDU 说明了起始地址和寄存器数量。从零开始寻址寄存器。因此,寻址输入寄存器 1-16 为 0-15。
将响应报文中的寄存器数据分成每个寄存器为两字节,在每个字节中直接地调整二进制内容。
对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。
请求PDU
响应PDU
错误
举例1
这是一个请求读输入寄存器 9 的实例:
将输入寄存器 9 的内容表示为两个十六进制字节值 00 0A,或十进制 10。

05 (0x05)写单个线圈
在一个远程设备上,使用该功能码写单个输出为 ON 或 OFF。
请求数据域中的常量说明请求的 ON/OFF 状态。十六进制值 FF 00 请求输出为 ON。十六进制值00 00 请求输出为 OFF。其它所有值均是非法的,并且对输出不起作用。
请求 PDU 说明了强制的线圈地址。从零开始寻址线圈。因此,寻址线圈 1 为 0。线圈值域的常量说明请求的 ON/OFF 状态。十六进制值 0XFF00 请求线圈为 ON。十六进制值 0X0000 请求线圈为OFF。其它所有值均为非法的,并且对线圈不起作用。
正常响应是请求的应答,在写入线圈状态之后返回这个正常响应。
请求PDU
响应PDU
错误
举例1
这是一个请求写线圈 173 为 ON 的实例:

06 (0x06)写单个寄存器
在一个远程设备中,使用该功能码写单个保持寄存器。
请求 PDU 说明了被写入寄存器的地址。从零开始寻址寄存器。因此,寻址寄存器 1 为 0。
正常响应是请求的应答,在写入寄存器内容之后返回这个正常响应。
请求PDU
响应PDU
错误
举例1
这是一个请求将十六进制 00 03 写入寄存器 2 的实例:
举例2
主机发送: 01 06 00 00 00 01 48 0A
从机回复: 01 06 00 00 00 01 48 0A
主机向01地址的从机发送请求
01地址的从机响应主机请求
写单个寄存器状态图

16 (0x10) 写多个寄存器
在一个远程设备中,使用该功能码写连续寄存器块(1 至约 120 个寄存器)。
在请求数据域中说明了请求写入的值。每个寄存器将数据分成两字节。
正常响应返回功能码、起始地址和被写入寄存器的数量。
请求PDU
响应PDU
错误
举例
请求将十六进制 00 0A 和 01 02 写入以 2 开始的两个寄存器的实例:
写多个寄存器状态图

15 (0x0F) 写多个线圈
16 (0x10) 写多个寄存器
。。。。。
5、Modbus异常码
从设备使用异常来指示各种不良状况,比如错误请求或不正确输入。但是,异常也可以作为对无效请求的应用程序级响应。从设备不会响应发出异常的请求,而是忽略不完整或损坏的请求,并开始等待新的消息传入。
根据标准,四个最常见的异常码是 01、02、03 和 04。
代码 | 名称 | 含义 |
01 |
非法功能
|
对于服务器(或从站)来说,询问中接收到的功能码是不可允许的操作。这也许是因为功能码仅仅适用于新设备而在被选单元中是不可实现的。同时,还指出服务器(或从站)在错误状态中处理这种请求,例如:因为它是未配置的,并且要求返回寄存器值。
|
02 |
非法数据地址
|
对于服务器(或从站)来说,询问中接收到的数据地址是不可允许的地址。特别是,参考号和传输长度的组合是无效的。对于带有 100 个寄存器的控制器来说,带有偏移量 96 和长度 4 的请求会成功,带有偏移量 96 和长度 5 的请求将产生异常码 02。
|
03 |
非法数据值
|
对于服务器(或从站)来说,询问中包括的值是不可允许的值。这个值指示了组合请求剩余结构中的故障,例如:隐含长度是不正确的。并不意味着,因为MODBUS 协议不知道任何特殊寄存器的任何特殊值的重要意义,寄存器中被提交存储的数据项有一个应用程序期望之外的值。
|
04 |
从站设备故障
|
当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。
|
异常响应报文有两个与正常响应不同的域:
功能码域:在正常响应中,服务器利用响应功能码域来应答最初请求的功能码。所有功能码的最高有效位(MSB)都为 0(它们的值都低于十六进制 80)。在异常响应中,服务器设置功能码的MSB 为 1。这使异常响应中的功能码值比正常响应中的功能码值高十六进制 80。
通过设置功能码的 MSB,客户机的应用程序能够识别异常响应,并能检测异常码的数据域。
数据域:在正常响应中,服务器可以返回数据域中数据或统计表(请求中要求的任何报文)。在异常响应中,服务器返回数据域中的异常码。这就定义了产生异常的服务器状态。
举例
在这个实例中,客户机对服务器设备寻址请求。功能码(01)用于读输出状态操作,它将请求地址1245(十六进制 04A1)的输出状态。输出域(0001)号码说明只读出一个输出。如果在服务器设备中不存在输出地址,那么服务器将返回异常码(02)的异常响应。说明请求从站的非法数据地址。