modbus TCP,最详细的modbus TCP协议攻略.modbus,Modbus Slave/Poll,如何使用modbus TCP协议进行通信?网络高级部分1【网络高级】
本篇网络编程modbus的详细解释,重点放在modbus TCP部分~
如果对你有帮助,请点个免费的赞吧,谢谢汪。(点个关注也可以!)
如果以下内容需要补充和修改,请大家在评论区多多交流~。
最下面有总结的关于本篇相关题目(会包括面试题和考试题),如果大家有新题目欢迎一起交流呀
目录
1. Modbus
1.1. modbus起源
1.1.1. Modbus RTU
1.1.2. Modbus ASCII
1.1.3. Modbus TCP
1.1.4. Modbus协议特点
1.1.5. 应用
1.2. 分类:
1.2.1.1. Modbus RTU
1.2.1.2. 数据格式
1.2.1.3. 串行通信
1.2.1.4. 波特率
1.2.1.5. CRC校验
1.2.1.6. 帧结构
1.2.1.7. 应用
1.2.1.8. 限制
1.2.1. Modbus ASCII协议概述:
1.2.2. 关键特点:
1. 网络层和传输层
2. Modbus TCP 帧
3. 通信过程
1.3. 优势:
1.4. 应用场景:
2. ModbusTCP的协议格式
2.1. Modbus TCP 的协议格式:
2.1.1. Modbus TCP 数据帧结构:
2.1.1.1. MBAP:Modbus Application Protocol (modbus报文头) - 固定7个字节
2.1.1.2. PDU:Protocol Data Unit(协议数据单元)
2.1.1.3. Modbus TCP/IP协议最大数据帧长度为260字节
2.1.1.4. 例子:
2.2. 报文头
2.3. 寄存器
2.3.1. 线圈寄存器(Coil Registers)
2.3.2. 离散输入寄存器(Discrete Input Registers)
2.3.3. 保持寄存器(Holding Registers)
2.3.4. 输入寄存器(Input Registers)
2.4. 功能码
2.5. Modbus Slave/Poll
2.5.1. Modbus Slave 功能
2.5.2. Modbus Master(Poller)功能
2.6. 在虚拟机写程序实现主机功能,编写客户端实现和Slave通信。
2.6.1. 如何封装函数实现03,05功能码的作用。大家可以尝试一下。
1. Modbus
1.1. modbus起源
Modbus由Modicon公司于1979年开发,是一种工业现场总线协议标准。
Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,其中最著名的是Modbus RTU、Modbus ASCII和Modbus TCP三种
其中Modbus TCP是在施耐德收购Modicon后1997年发布的。
1.1.1. Modbus RTU
采用串行通信方式,数据以二进制形式传输,适用于短距离、低速率的通信场景。
1.1.2. Modbus ASCII
同样采用串行通信方式,数据以ASCII码形式传输,便于调试和阅读,但传输效率低于Modbus RTU。
1.1.3. Modbus TCP
基于以太网通信,数据传输速率较高,适用于长距离、高速率的通信场景。Modbus TCP是在施耐德收购Modicon后1997年发布的。
1.1.4. Modbus协议特点
简单性:协议相对简单,易于实现和维护,降低了设备的通信成本。
开放性:作为一种标准协议,Modbus是开放的,不同厂商的设备可以轻松实现互联互通。
广泛性:广泛应用于工业控制领域,如PLC、变频器、智能仪表等。
可靠性:具有较好的抗干扰能力,适用于各种工业环境。
1.1.5. 应用
Modbus协议广泛应用于以下场景:
工业自动化:PLC、变频器、伺服驱动器等设备之间的通信。
能源管理:智能电表、水表、气表等的数据采集。
环境监测:温湿度、压力、流量等传感器的数据传输。
楼宇自动化:照明、空调、安防等系统的集成控制。
1.2. 分类:
1)Modbus RTU
1.2.1.1. Modbus RTU
Modbus RTU(Remote Terminal Unit)是Modbus协议的一种变体,主要用于串行通信链路。以下是Modbus RTU的一些关键特点:
1.2.1.2. 数据格式
Modbus RTU使用二进制数据格式进行通信,与Modbus ASCII的文本格式相比,RTU格式更加紧凑,可以在相同的波特率下传输更多的数据。
数据包由地址、功能码、数据和一个校验和(通常是循环冗余校验CRC)组成。
1.2.1.3. 串行通信
Modbus RTU通常通过RS-232、RS-485或RS-422串行接口进行通信。
RS-485是Modbus RTU中最常用的通信接口,因为它支持多点通信,可以在较长的距离上以较高的速率传输数据。
1.2.1.4. 波特率
Modbus RTU的通信速率(波特率)可以是9600、19200、38400、57600或115200等,具体取决于通信距离和现场环境。
1.2.1.5. CRC校验
Modbus RTU消息使用16位的CRC校验来确保数据的完整性。在发送和接收过程中,都会计算CRC,以确保数据在传输过程中没有发生错误。
1.2.1.6. 帧结构
Modbus RTU的消息帧包括起始位、从站地址、功能码、数据、CRC校验和停止位。
消息帧之间必须有至少3.5个字符时间的间隔,以确保帧与帧之间能够正确区分。
1.2.1.7. 应用
Modbus RTU广泛应用于工业自动化领域,特别是在需要可靠和稳定的串行通信的场合,如PLC、变频器、传感器和仪表之间的通信。
1.2.1.8. 限制
Modbus RTU的一个主要限制是它不支持消息的在线编辑,一旦消息开始传输,就必须等待它完全发送完毕。
另一个限制是,由于串行通信的性质,RTU通常不支持高速或长距离的数据传输,尤其是在波特率较低时。
Modbus RTU由于其简单、稳定和广泛的支持,仍然是工业通信领域的重要协议之一。
2)Modbus ASCII
1.2.1. Modbus ASCII协议概述:
串口通信:与Modbus RTU一样,Modbus ASCII协议也是为串行通信接口设计的,如RS-232、RS-485或RS-422。
ASCII码传输:Modbus ASCII协议使用ASCII码进行数据传输。这意味着每个8位的字节都被转换成两个ASCII字符,通常是两个十六进制数字。
特殊字符标志:Modbus ASCII消息帧的开始和结束都有特殊字符作为标志。帧的开始是冒号(:),而帧的结束是回车(CR)和换行(LF)字符。这些特殊字符帮助接收设备识别消息的边界。
传输效率:由于每个字节都需要用两个ASCII字符来表示,Modbus ASCII的传输效率确实比Modbus RTU低。对于相同的消息,ASCII格式的数据量是RTU格式的两倍。
适用场景:由于传输效率较低,Modbus ASCII通常用于通讯量较小的应用场景,或者当设备需要使用基于文本的协议时。
错误检测:Modbus ASCII使用纵向冗余校验(LRC)来检测传输错误,而不是Modbus RTU中的CRC校验。
如何将十六进制字节转换为ASCII字符:
十六进制字节 0xAF(1010 1111)将被转换为ASCII字符 ‘A’(41)和 ‘F’(46)。
#include // 包含 uint8_t 和 uint16_t 的定义
uint16_t combineUint8ToUint16(uint8_t highByte, uint8_t lowByte) {
uint16_t combinedValue = 0;
// 将 highByte 移动到高8位,lowByte 保持不变
combinedValue = (uint16_t)highByte << 8; // 高8位
combinedValue |= lowByte; // 低8位
return combinedValue;
}
int main() {
uint8_t highByte = 0x12; // 示例高位字节
uint8_t lowByte = 0x34; // 示例低位字节
// 将两个 uint8_t 值组合成一个 uint16_t 值
uint16_t combinedValue = combineUint8ToUint16(highByte, lowByte);
// 输出结果
printf("组合后的 uint16_t 值为: 0x%04X
", combinedValue);
return 0;
}
由于Modbus ASCII协议的特点,它在以下情况下更为适用:
当设备的处理器能力有限,无法处理二进制数据格式时。
当需要通过更通用的媒介(如电子邮件或文本消息)传输Modbus消息时。
当使用Modbus协议的设备需要与使用ASCII码的其他系统进行互操作时。
尽管Modbus ASCII不如Modbus RTU高效,但它仍然在某些特定的应用场景中发挥着作用。
3)Modbus TCP
Modbus TCP 是 Modbus 协议的一种实现,专门用于以太网和 TCP/IP 网络环境。它是由施耐德电气(Schneider Electric)在 1996 年推出的,旨在将 Modbus 协议的优势扩展到基于 TCP/IP 的网络。
1.2.2. 关键特点:
1. 网络层和传输层
网络层:使用 IP 协议。
传输层:使用 TCP 协议,端口号默认为 502。
2. Modbus TCP 帧
Modbus TCP 帧 structure 与传统的 Modbus RTU 和 Modbus ASCII 不同,它包括以下部分:
事务标识符(Transaction Identifier):用于事务配对,标识 Modbus 请求/响应的对应关系。
协议标识符(Protocol Identifier):对于 Modbus TCP,这个值始终为 0x0000。
长度字段:表示后续字段的字节数,包括单元标识符、功能码和数据字段。
单元标识符(Unit Identifier):在 Modbus TCP 中,这个字段通常用于标识特定的服务器或设备。
功能码(Function Code):与 Modbus RTU 和 Modbus ASCII 中的功能码相同,用于指示请求或响应的类型。
数据字段:包含请求或响应的具体数据。
校验和:在 Modbus TCP 中,由于 TCP/IP 协议已经提供了错误检测机制,因此不需要额外的校验和。
3. 通信过程
建立连接:客户端通过 TCP/IP 网络与服务器建立连接。
发送请求:客户端发送 Modbus TCP 请求帧到服务器。
处理请求:服务器接收请求,处理请求,并生成响应。
发送响应:服务器将 Modbus TCP 响应帧发送回客户端。
关闭连接:完成通信后,客户端和服务器可以关闭 TCP 连接,也可以保持连接状态以供后续通信使用。
1.3. 优势:
易于实现:基于成熟的 TCP/IP 协议栈,简化了网络通信的实现。
可靠性:利用 TCP 的错误检测和重传机制,提高了通信的可靠性。
兼容性:可以与现有的 Modbus 应用程序和设备兼容,只需进行适当的网关转换。
免费,简单,易于使用。
1.4. 应用场景:
Modbus TCP 广泛应用于工业自动化领域,特别是在需要通过以太网进行设备监控和控制的环境中。它被用于各种类型的设备,包括 PLCs、HMI、SCADA 系统、传感器和执行器等。
由于 Modbus TCP 依赖于稳定的网络环境,因此在网络条件良好的情况下,它是实现工业设备网络通信的理想选择。
2. ModbusTCP的协议格式
Modbus TCP 是 Modbus 协议的以太网版本,它将 Modbus RTU 或 Modbus ASCII 协议的数据帧封装在 TCP/IP 协议中。
2.1. Modbus TCP 的协议格式:
具体协议分析可参考:
实例分享 | ModbusTCP报文详解
2.1.1. Modbus TCP 数据帧结构:
ModbusTcp协议包含三部分:报文头、功能码、数据。
2.1.1.1. MBAP:Modbus Application Protocol (modbus报文头) - 固定7个字节
事务标识符(Transaction Identifier) - 2 字节
这是一个唯一标识符,用于匹配请求和响应。在请求和响应中,这个字段的值是相同的。
协议标识符(Protocol Identifier) - 2 字节
对于 Modbus TCP,这个字段的值始终是 0x0000。
长度字段(Length Field) - 2 字节
表示接下来的单元标识符、功能码和数据字段的字节数。不包括事务标识符、协议标识符和长度字段本身。
单元标识符(Unit Identifier) - 1 字节
也称为设备地址或从站地址。在 Modbus TCP 中,这个字段用于标识网络上的 Modbus 从设备。
2.1.1.2. PDU:Protocol Data Unit(协议数据单元)
功能码(Function Code) - 1 字节
表示请求或响应的具体功能。例如,读保持寄存器是功能码 0x03,写单个寄存器是功能码 0x06。
数据字段(Data Field) - n 字节
这个字段包含了与功能码相关的具体数据。例如,如果功能码是读保持寄存器,那么这个字段将包含起始地址、寄存器数量等信息。
Modbus TCP 数据帧示例:
+----------------+-+-+-+-----------+-+-------------+-+----------+-+-+
| Transaction ID | 0 | 0 | Protocol | Unit ID | Function Code | Data |
| (2 bytes) | 0 | 0 | ID (2 | (1 byte) | (1 byte) | (n |
+----------------+-+-+-+-----------+-+-------------+-+----------+-+-+
| | T | L | bytes) | | | bytes)
+----------------+-+-+-+-----------+-+-------------+-+----------+-+-+
2.1.1.3. Modbus TCP/IP协议最大数据帧长度为260字节
2.1.1.4. 例子:
假设我们要读取从设备地址为 0x01 的保持寄存器,起始地址为 0x0002,读取 2 个寄存器。
数据帧如下:
Transaction ID: 0x0001
Protocol ID: 0x0000
Length: 0x0006 (6 bytes for Unit ID, Function Code, and Data)
Unit ID: 0x01
Function Code: 0x03 (Read Holding Registers)
Data: 0x0002 0x0002 (起始地址为 0x0002,读取 2 个寄存器)
实际的字节流(十六进制表示)可能是:
这个数据帧会被封装在 TCP/IP 数据包中,并通过以太网发送。接收方解析这个数据帧,并根据功能码和数据字段执行相应的操作,然后返回一个包含相同事务标识符的响应。
2.2. 报文头
7个字节
2.3. 寄存器
2.3.1. 线圈寄存器(Coil Registers)
类型:位寄存器(每个寄存器占 1 位,但在传输时通常以字节为单位)
用途:控制离散输出(如继电器、开关)
特性:可读可写
功能码:
读线圈:0x01
写单个线圈:0x05
写多个线圈:0x0F
2.3.2. 离散输入寄存器(Discrete Input Registers)
类型:位寄存器(每个寄存器占 1 位,但在传输时通常以字节为单位)
用途:读取离散输入(如按钮、传感器状态)
特性:只读
功能码:
读离散输入:0x02
2.3.3. 保持寄存器(Holding Registers)
类型:字寄存器(每个寄存器占 2 字节)
用途:存储可读写的数据,如配置参数、设备状态
特性:可读可写
功能码:
读保持寄存器:0x03
写单个保持寄存器:0x06
写多个保持寄存器:0x10
2.3.4. 输入寄存器(Input Registers)
类型:字寄存器(每个寄存器占 2 字节)
用途:读取设备输入的数据,如传感器读数、计数器值
特性:只读
功能码:
读输入寄存器:0x04
读操作:通常一个功能码可以用于读取单个或多个寄存器。
写操作:写单个和写多个寄存器通常有不同的功能码。
可读可写寄存器:具有三个功能码(读多个、读/写单个、写多个)。
只读寄存器:具有一个功能码(读多个)。
2.4. 功能码
功能码占一个字节
2.5. Modbus Slave/Poll
下载与安装,modbus Slave/Poll ,可以用于具体的查看modbus数据帧的结果。会以表格的方式呈现出来,软件界面如下:
下载,安装以及使用我会在下一篇文档中详细说明
本篇只需要通过代码实现数据帧的收发
在Modbus通信网络中,Modbus Slave(从设备)和Modbus Master(主设备)共同工作,以实现数据交换和控制命令的传递。以下是Modbus Slave和Master(Poller)的功能:
2.5.1. Modbus Slave 功能
响应请求:从设备监听来自主设备的请求,并根据请求类型(功能码)提供相应的数据或执行指定的操作。
数据存储:Slave设备通常具有四种类型的寄存器(线圈、离散输入、输入寄存器、保持寄存器),用于存储数据。
数据处理:Slave设备根据主设备的请求处理数据,例如读取传感器数据、执行控制命令等。
错误检测:在处理请求时,Slave设备会检测可能的错误,并在响应中包含错误码,以便主设备了解请求的状态。
支持多种功能码:Slave设备需要支持多种功能码,以响应不同类型的请求,如读/写线圈、读/写寄存器等。
串行或网络通信:Slave设备能够通过串行通信(如RS-485)或以太网进行数据交换。
2.5.2. Modbus Master(Poller)功能
发起请求:主设备(Poller)负责发起通信,向从设备发送请求以读取数据或写入命令。
轮询从设备:主设备定期轮询从设备,以获取状态更新或监控数据。
处理响应:主设备接收从设备的响应,并处理这些数据,例如更新人机界面(HMI)、记录数据或触发报警。
错误处理:主设备能够识别从设备响应中的错误,并采取相应的措施,如重试请求、记录错误日志或通知操作员。
支持多种功能码:主设备需要支持多种功能码,以执行不同的操作,如读取输入寄存器、写入保持寄存器等。
通信管理:主设备负责管理通信网络,包括维护通信链路、处理网络故障和优化数据传输。
数据整合:主设备可能需要将从不同从设备收集的数据整合到一个统一的系统中,以便进行更高级的数据处理和分析。
时间同步:在某些应用中,主设备可能还需要负责网络中的时间同步,确保所有从设备使用相同的时钟源。
2.6. 在虚拟机写程序实现主机功能,编写客户端实现和Slave通信。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("sock err");
return 0;
}
struct sockaddr_in addr, caddr;
addr.sin_family = AF_INET;
addr.sin_port = htons(502);
addr.sin_addr.s_addr = inet_addr("192.168.0.116");
int len = sizeof(caddr);
printf("connect ok
");
connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
// 写保持寄存器,让第6位为6
uint8_t data[12] = {
0x00,
0x00, // 服务器
0x00, // 协议标示
0x00,
0x00,
0x06, // 字节数
0x01, // 单元标识
0x06, // 功能
0x00,
0x06, // 位置
0x00,
0x07 // 值
};
send(sockfd, data, sizeof(data), 0);
uint8_t d[12];
int res = recv(sockfd, d, 12, 0);
for (int i = 0; i < 12; i++)
{
printf("%02x ", d[i]);
}
printf("
");
close(sockfd);
return 0;
}
2.6.1. 如何封装函数实现03,05功能码的作用。大家可以尝试一下。