??本文的主要目的是写一篇简单的文章MODBUS-TCP记录在服务器-客户端程序中的知识点包含了编程所需的必要背景知识和协议分析流程图。
MODBUS基本数据类型
??MODBUS基本数据有四种类型: ??:客户端只能读取,由服务器提供,占1个比例,可以传输实际的开关量输入,如接近开关的通断信息。 ??:客户端可以写入和读取,服务器可以根据客户端的设置改变其值,占1个比例,可以控制实际继电器的吸合和断开。 ??:客户端只能读取它,最小单位是16个比特,它还可以传输8位数据。当传输超过16个比特数据时,需要多个输入寄存器,可以通过输入寄存器传输实际温度、电压等数据。 ??:客户端可以写入或读取,最小单位为16比特字,可以设置一些参数,以及实际电压等物理量。
MODBUS-TCP数据格式
一个正常的MODBUS-TCP数据帧包括以下三部分: ??这三部分合称ADU,即应用数据单元,其中功能码和数据合称为PDU,也就是协议数据单元。ADU中的MBAP(MODBUS Application Protocol)是MODBUS-TCP独特的内容。PDU在所有MODBUS中格式完全相同。 ??MODBUS-TCP数据帧由端口502发送,端口502由互联网组织专门发送MODBUS-TCP协议保留的端口号。
MBAP报文头格式:
??MBAP报文头(MODBUS共7个字节,其含义如下: ??事务元标识符:2字节,由于客户端可以同时发送多个请求,为了区分服务器响应的请求,客户端在请求帧中使用计数器值填充该区域,服务器可以在响应帧中返回相同的计数值,以区分请求。 ??协议标识符:2字节,保持0,表示MODBUS协议 长度:2字节,后续字节数 ??单元标识符:1字节,需要转发给串行链路MODBUS设备有意义,一般不需要考虑,写0。
PDU格式
??由于不同的功能代码对应不同的数据格式,因此需要解释功能代码和数据部分。我只介绍我的程序将涉及的功能代码3和16。之所以只选择这两个功能码,是因为这两个功能码基本上可以涵盖所有与数据采集相关的功能,写多个寄存器可以设置采集参数DA设置开关量输出……阅读多个寄存器可以阅读各种收集结果。
03 (0x读保持寄存器
??由于是大端模式,每个寄存器的宽度为16位和2个字节(Big-endian),因此,高字节在前,低字节在后,具体数据格式如下: 请求:
功能码 | 1个字节 | 3(0x03) |
---|---|---|
起始地址 | 2个字节 | 0~65535(0xFFFFF) |
寄存器数量 | 2个字节 | N=1~125(0x7D) |
响应:
功能码 | 1个字节 | 3(0x03) |
---|---|---|
字节数 | 1个字节 | N*2 |
寄存器值 | N*2个字节 |
举例: 以下是要求读取地址偏移0的寄存器值,寄存器值为0 字节序号(10进制):00 01 02 03 04 05 06 07 08 09 10 11 发送字符(16进制):00 00 00 00 00 06 00 03 00 00 00 01 接受字符(16进制):00 00 00 00 00 05 00 03 02 00 00
寄存器数量有限MODBUS-RTU协议数据帧(PDU)长度不得超过252,PDU因此(252-2)/2=125
16 (0x10)写保持寄存器
??写维持寄存器是多个寄存器的反向操作。它可以是同一功能项的读写操作,也可以是不同功能项的单独操作。DA(数模转换)中,使用写多个寄存器设置DA输出值,然后读取多个寄存器返回之前设定的值。也可以返回相同的地址AD(模数转换)的采集值,这取决于实际应用。其具体格式如下:
功能码 | 1个字节 | 16(0x10) |
---|---|---|
起始地址 | 2个字节 | 0~65535(0xFFFFF) |
寄存器数量 | 2个字节 | N=1~123(0x7B) |
字节数 | 1个字节 | 2*N |
寄存器值 | N*2个字节 | 大端排列值 |
PDU最大字节数为252,减去功能码起始地址、寄存器数量、字节数占用的6个字节为246,因此最大字节为123。
功能码 | 1个字节 | 16(0x10) |
---|---|---|
起始地址 | 2个字节 | 与请求的起始地址相同 |
寄存器数量 | 2个字节 | N |
举例: 以下是要求写地址偏移0的寄存器值,寄存器值为0 字节序号(10进制):00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 发送字符(16进制):00 00 00 00 00 09 00 10 00 00 00 01 02 00 00 接受字符(16进制):00 00 00 00 00 06 00 10 00 00 00 01
异常响应
??当请求失败时,服务器将返回异常响应,异常响应帧将原始功能码放置在功能码位置 0x80.后面的数据是异常码,具体格式如下:
功能码 | 1个字节 | 0x80 请求功能码 |
---|---|---|
异常码 ?? | 1个字节?? | 常用异常码:01:不支持的功能码02:地址错误,起始地址 如果寄存器数量超出范围,也是地址错误03:数据错误,这里的数据也包括在内PDU例如,数据本身要求的寄存器数量超过了PDU允许的最大长度。04:服务器处理请求过程中从站故障出现错误。 |
MODBUS-TCP请求响应流程
??接到客户端请求后,服务器首先判断协议标识符。MODBUS协议可以继续处理,然后根据MBAP报文头中的后续字节数用于拆卸完整的数据帧。如果客户端使用请求响应发送请求,服务器收到的每个包数据应该是,只有一个完整的数据包。最后,根据功能码将请求交给每个功能码程序。流程图如下:
接下来,我们将真正写程序!