1. ModbusRTU介绍
- ModbusRTC本身只定义了数据报文的结构,底层物理链接依赖与串行通信标准。
- 主从通信模式,一个主站设备发起请求,一个或多个从站设备响应请求
- 全双工 异步通信
2. 物理层 - modbusRTU的载体
2.1 RS485
RS-485 是 Modbus RTU 在工业应用中最常见的选择,其优点:
- 多点通信:支持半双工多点通信,一个主站可以连接多达32个从站(通过中继器可扩展更多)
- 远距离传输 :标准距离可达1200米
- 抗干扰能力强:采用差分信号,对共模噪声具有良好的抑制力,使用于工业恶劣环境
- 布线简单:采用两个双绞线进行链接
2.2 Modbus RTU over TCP/IP(网络透传)
Modbus RTU 协议通过 TCP/IP 网络进行传输,这种方式被称为 "Modbus RTU over TCP" 或 "Modbus RTU 网络透传"。这种实现方式在工业现场非常常见,主要有两种应用场景:
2.2.1.设备直接支持网络功能
工业设备(如智能传感器、变频器、PLC等)直接集成了以太网接口,能够将原本的 Modbus RTU 数据帧封装在 TCP/IP 数据包中进行传输:
- 优势:设备直接连接到企业网络,无需额外的转换设备
- 传输距离:几乎不受限制,可以通过互联网进行远程通信
- 多主站支持:TCP/IP 的特性使得多个主站可以同时访问同一个从站设备
- 应用场景:远程监控、云端数据采集、分布式控制系统
2.2.2. 串口转网络设备(透传模块)
对于不具备网络功能的传统 Modbus RTU 设备,可以通过专门的串口转网络设备(如串口服务器、DTU、网关等)实现网络化:
- 工作原理:透传设备一端连接传统的 RS-485/RS-232 串口设备,另一端连接到以太网,将串口数据透明传输到网络
- 设备类型:
- 串口服务器:专业的工业级设备,支持多串口、协议转换等功能
- DTU(数据传输单元):通常支持4G/WiFi等无线网络,适用于远程或移动场景
- 工业网关:集成多种协议转换功能,支持 Modbus RTU/TCP、OPC UA 等
- 配置要求:需要配置设备的 IP 地址、端口号、串口参数(波特率、数据位等)
- 应用优势:
- 成本效益:无需更换现有设备,只需添加转换设备
- 灵活部署:可以将现场设备接入企业网络或云平台
- 集中管理:通过网络实现设备的集中监控和管理
数据帧格式:
- Modbus RTU over TCP 保持了原有的 RTU 数据帧格式(包括 CRC 校验)
- 数据帧被完整地封装在 TCP 数据包中,不进行任何修改
- 与标准的 Modbus TCP 协议不同,Modbus TCP 使用 MBAP 头部替代了 CRC 校验
时序要求:
- 网络传输中不存在传统串口的 T3.5 帧间静默时间概念
- 透传设备通常会处理帧的分割和重组
- 需要注意网络延迟对通信时序的影响
实际应用建议:
- 选择合适的透传设备:根据现场环境选择有线或无线、单串口或多串口设备
- 网络规划:合理规划 IP 地址和端口,避免冲突
- 安全考虑:在通过互联网传输时,建议使用 VPN 或其他加密手段保护数据安全
- 兼容性测试:在正式部署前,充分测试主站软件与透传设备的兼容性
3. Modbus 数据模型:理解数据存储结构
Modbus 协议将设备内部的数据抽象为四个逻辑上独立的、地址空间互不重叠的数据表。每个表存储不同类型的数据,并具有特定的访问权限。
| 数据表 | 类型 | 访问权限 | Modbus协议地址范围 | PLC地址范围 | 典型用途 |
|---|---|---|---|---|---|
| 线圈 (Coils) | 单个位 (Bit) | 读/写 | 0x0000-0xFFFF | 00001-09999 | 控制继电器、开关、指示灯、电机启停等布尔量输出 |
| 离散量输入 (Discrete Inputs) | 单个位 (Bit) | 只读 | 0x0000-0xFFFF | 10001-19999 | 读取数字输入信号、传感器状态(门开/关)、按钮状态等 |
| 保持寄存器 (Holding Registers) | 16位字 (Word) | 读/写 | 0x0000-0xFFFF | 40001-49999 | 存储配置参数、设定值、控制变量、模拟量输出、PLC内部变量 |
| 输入寄存器 (Input Registers) | 16位字 (Word) | 只读 | 0x0000-0xFFFF | 30001-39999 | 读取测量值(温度、压力)、设备状态、传感器数据、模拟量输入 |
重要提示:
- PLC地址是工业自动化领域常用的地址表示方法(基于1的编址),而Modbus协议地址是通信时实际使用的地址(基于0的编址)。两者的转换关系为:Modbus协议地址 = PLC地址 - 基础地址(如40001、30001等)。 例如,参考地址
40003对应协议中的偏移地址0x0002=40003-40001。 - 线圈和离散量输入是位(Bit)操作,而保持寄存器和输入寄存器是字(Word,即16位)操作。
- 每个表的地址空间是独立的,即
0x0001的线圈与0x0001的保持寄存器是完全不同的概念。
4. 协议基础结构:Modbus RTU 数据帧
个标准的 Modbus RTU 数据帧(或称报文)是主站与从站之间进行通信的基本单位。它包含以下几个关键部分:
4.1 数据帧格式概览
[从站地址] [功能码] [数据字段] [CRC校验] 1字节 1字节 N字节 2字节4.11 从站地址(slave-address) - 1字节
- 作用:用于唯一标识网络上的目标从站设备。
- 范围:通常为 1-247。
0是广播地址,发送给所有从站,但从站不响应。248-255为保留地址。
- 要求:每个从站设备在 Modbus 网络中必须配置一个唯一的地址。
4.1.2 功能码(Function Code)- 1个字节
- 作用:定义了主站希望从站执行的操作类型(如读取数据、写入数据)。
| 功能码 (十进制/十六进制) | 名称 | 数据类型 | 访问权限 | 说明 |
|---|---|---|---|---|
| 01 (0x01) | 读取线圈状态 | Bit | 读 | 读取一个或多个输出线圈(Coils)的开/关状态 |
| 02 (0x02) | 读取离散量输入状态 | Bit | 读 | 读取一个或多个离散量输入(Discrete Inputs)的开/关状态 |
| 03 (0x03) | 读取保持寄存器 | Word | 读 | 读取一个或多个保持寄存器(Holding Registers)的值 |
| 04 (0x04) | 读取输入寄存器 | Word | 读 | 读取一个或多个输入寄存器(Input Registers)的值 |
| 05 (0x05) | 强制单个线圈 | Bit | 写 | 设置单个输出线圈的状态(开/关) |
| 06 (0x06) | 预置单个寄存器 | Word | 写 | 设置单个保持寄存器的值 |
| 15 (0x0F) | 强制多个线圈 | Bit | 写 | 设置多个输出线圈的状态 |
| 16 (0x10) | 预置多个寄存器 | Word | 写 | 设置多个保持寄存器的值 |
4.1.3 数据字段(Data Field)- 变长
- 作用:包含执行特定功能所需的具体信息,如起始地址、读取或写入的数量、要写入的值等。
- 长度:根据功能码的不同而变化。
- 读取请求:通常包含起始地址和数量。
- 写入请求:通常包含起始地址和要写入的值。
4.1.4 CRC校验(Cyclic Redundancy Check)- 2个字节
作用:提供数据的完整性校验,用于检测在传输过程中是否发生错误。
算法:Modbus RTU 使用 CRC-16 算法。
- 发送方计算从从站地址到数据字段末尾的所有字节的 CRC 值,并将其附加在帧的末尾(低字节在前,高字节在后)。
- 接收方接收到数据帧后,以同样的方式对接收到的数据(不含接收到的 CRC)进行 CRC 计算。
- 如果计算结果与接收到的 CRC 值不匹配,则认为数据传输有误,从站通常会丢弃该帧不予处理或返回错误。
//crc计算代码示例: WORD CRC16 (const BYTE *data, WORD length) { static const WORD table[] = { 0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, 0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, 0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, 0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, 0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, 0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, 0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, 0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, 0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, 0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, 0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, 0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, 0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, 0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, 0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, 0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, 0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, 0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, 0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, 0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, 0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, 0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, 0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, 0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, 0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, 0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, 0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, 0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, 0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, 0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, 0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, 0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 }; BYTE temp; WORD word = 0xFFFF; while (length--) { temp = *data++ ^ word; word >>= 8; word ^= table[temp]; } return word; }4.1.5 帧间静默时间(T3.5)
- 重要性:这是 Modbus RTU 协议中一个非常关键的特性,用于判断一帧数据的开始和结束。
- 定义:在 Modbus RTU 模式下,一个数据帧的发送必须以至少 3.5 个字符传输时间的静默间隔作为结束标志。当接收方检测到连续的 3.5 个字符的静默时间后,就会认为前一帧数据已经完整接收。同样,一个新帧的开始也必须以 3.5 个字符的静默时间作为前导。
- 作用:
- 帧同步:确保接收方能够准确地识别一帧的边界,避免粘包或断帧问题。
- 错误检测:如果两个字符之间的静默时间超过 1.5 个字符但小于 3.5 个字符,则认为这是一个传输错误(可能表示帧被截断)。
- 计算:
T3.5 = (3.5 * 10位 / 波特率)秒(每个字符通常包含1起始位+8数据位+1停止位 = 10位)。例如,在9600 bps下,T3.5约为3.64毫秒。
5. 异常响应:错误处理机制
当主站向从站发送一个有效但从站无法处理的请求时(例如,请求读取一个不存在的寄存器地址,或者数据值超出范围),从站不会返回正常响应,而是会返回一个异常响应帧。
5.1 异常响应帧的格式
[从站地址] [功能码+0x80] [异常码] [CRC校验] 1字节 1字节 1字节 2字节- 功能码+0x80:异常响应的标志。从站会将原始请求功能码的最高位设置为 1(即原始功能码与
0x80进行按位或操作),以此告诉主站这是一个异常响应。例如,如果请求的功能码是0x03,异常响应的功能码将是0x83。 - 异常码:一个字节,指示错误的具体类型。如下:
| 异常码 (十六进制) | 名称 | 说明 |
|---|---|---|
| 01 (0x01) | 非法功能码 (Illegal Function) | 从站不支持该功能码。 |
| 02 (0x02) | 非法数据地址 (Illegal Data Address) | 请求的数据地址(寄存器或线圈地址)在从站中不存在或超出其支持范围。 |
| 03 (0x03) | 非法数据值 (Illegal Data Value) | 请求中包含的数据值对于从站无效(例如,试图写入负数到无符号寄存器,或写入值超出设备限制)。 |
| 04 (0x04) | 从站设备故障 (Slave Device Failure) | 从站在尝试执行操作时发生不可恢复的内部错误。 |
| 05 (0x05) | 确认 (Acknowledge) | 接收请求但需要长时间处理,通常用于复杂操作。 |
| 06 (0x06) | 从站忙 (Slave Device Busy) | 从站正忙于处理其他任务,无法响应请求。 |
| 0A (0x0A) | 网关路径不可用 (Gateway Path Unavailable) | Modbus TCP/IP 网关无法路由请求到物理串行端口。 |
| 0B (0x0B) | 网关目标设备无响应 (Gateway Target Device Failed to Respond) | Modbus TCP/IP 网关未能从目标从站接收到响应。 |
6. 实际应用示例:深入理解通信流程
6.1 示例1:读取温度传感器数据(功能码 0x03 - 读取保持寄存器)
假设我们有一个地址为1的温度传感器,其当前温度值存储在保持寄存器(Holding Register)的地址40001(Modbus协议偏移地址0x0000)。我们要读取这一个寄存器的值。
发送命令(主站 -> 从站):01 03 02 00 64 B9 30 命令解析: 01:从站地址为 1。 03:功能码为 3 (0x03),表示读取保持寄存器。 00 00:起始寄存器地址为 0x0000 (对应 Modbus 参考地址 40001)。 00 01:请求读取 1 个寄存器。 84 0A:CRC-16 校验码。 设备响应(从站 -> 主站):01 03 02 00 64 B9 30 响应解析: 01:从站地址为 1。 03:功能码为 3 (0x03),正常响应。 02:数据字节数,表示接下来的数据有 2 个字节(因为读取了 1 个 16 位寄存器,即 2 个字节)。 00 64:实际读取到的数据。0x0064 转换为十进制是 100。 B9 30:CRC-16 校验码。6.2 示例2:设置输出线圈状态(功能码 0x05 - 强制单个线圈)
要将地址为2的设备的输出线圈(Coil)地址00001(Modbus协议偏移地址0x0000) 设置为开启状态。
发送命令(主站 -> 从站): 02 05 00 00 FF 00 8C 3A 命令解析: 02:从站地址为 2。 05:功能码为 5 (0x05),表示强制单个线圈。 00 00:线圈地址为 0x0000 (对应 Modbus 参考地址 00001)。 FF 00:写入的值。0xFF00 表示将线圈设置为 ON(开启),0x0000 表示设置为 OFF(关闭)。 8C 3A:CRC-16 校验码。 设备响应(从站 -> 主站): 02 05 00 00 FF 00 8C 3A 响应解析: 02:从站地址为 2。 05:功能码为 5。 00 00:线圈地址为 0x0000。 FF 00:确认线圈被设置为 ON。 8C 3A:CRC-16 校验码。 对于功能码 0x05,从站的正常响应是将其接收到的请求帧原样返回给主站,以确认操作已成功执行。7. 使用在线工具实践Modbus RTU
理论学习固然重要,但动手实践是掌握 Modbus RTU 的最佳途径。我们强烈推荐使用Modbus在线工具来辅助学习和调试:
Modbus RTU 命令生成器:
- 根据从站地址、功能码、起始地址和数量等参数,自动生成符合 Modbus RTU 标准的请求命令帧(包括 CRC 校验)。
- 非常适合验证你手动构建的命令是否正确。
Modbus RTU 数据解析器:
- 输入十六进制的 Modbus RTU 响应数据帧,工具将自动解析出从站地址、功能码、数据内容以及 CRC 校验结果。
- 支持多种数据类型(如 UINT16, INT16, UINT32, FLOAT32)和字节序(ABCD, DCBA, BADC, CDAB)的解析,帮助你验证从设备读取到的数据是否正确。
CRC计算器:
- 专门用于计算 Modbus RTU 消息的 CRC-16 校验码,确保数据包的完整性。
Modbus 在线调试工具:
- 模拟主站功能,实时与真实的 Modbus 设备进行通信,发送请求并接收响应,是现场调试的利器。
9. 常见问题和解决方案
9.1 通信超时(Timeout)
现象:主站发送请求后,在设定的时间内未收到从站的响应。
常见原因:
- 波特率、数据位、停止位、奇偶校验等串口参数设置不匹配。
- 从站地址错误,主站请求的地址与从站实际地址不符。
- 从站设备未通电、未启动或故障。
- 接线错误(RS-485 的 A/B 线接反,RS-232 接线不正确)。
- RS-485 网络中终端电阻缺失或不正确,导致信号反射。
- T3.5 帧间静默时间控制不精确,导致从站误判帧结束。
解决方案:
- 逐一核对主站和从站的所有串口通信参数,确保完全一致。
- 验证从站地址配置是否正确,避免冲突。
- 检查从站状态:确认从站已正常上电并运行。
- 检查物理接线:使用万用表检查 A/B 线是否正确,确保接触良好。
- RS-485 终端电阻:在总线两端(最远两个设备)各连接一个 120 欧姆的终端电阻。
- 增加超时时间:在调试阶段适当增加主站的超时等待时间,排除因响应慢导致的问题。
9.2 CRC校验失败
现象:接收到的数据帧通过 CRC 校验器验证不通过。
常见原因:
- 数据传输过程中发生错误:线路干扰、电磁噪声、传输距离过长、波特率过高。
- 主站或从站的 CRC 算法实现不正确:虽然 Modbus CRC-16 是标准算法,但仍可能因实现细节错误导致计算不匹配。
- 数据帧被截断或额外插入字节:硬件或软件问题导致帧不完整。
解决方案:
- 检查线路连接:确保线缆质量好,屏蔽层有效接地,避免与强电线缆并行布线。
- 降低通信速率:尝试降低波特率,看是否能解决 CRC 错误。
- 使用标准 CRC-16 算法:确认主站和从站程序中使用的 CRC-16 算法是 Modbus 标准的(多项式
0xA001或反序0x8005)。 - 隔离干扰源:排除可能导致干扰的设备。
9.3 数据解析错误(数值不正确)
现象:成功收到从站响应,但解析出的数据与预期不符(例如,温度值显示为乱码或错误的大数字)。
常见原因:
- 字节序(Byte Order)或字序(Word Order)配置错误:这是最常见的问题,尤其在处理 32 位数据(UINT32, INT32, FLOAT32)时。
- 数据类型选择错误:例如,将浮点数按整数解析,或将有符号数按无符号数解析。
- 寄存器地址对应关系错误:读取的寄存器地址与设备手册中定义的实际数据地址不匹配。
- 数据缩放或偏移未处理:设备可能输出原始ADC值,需要经过计算才能得到实际物理量。
解决方案:
- 查阅设备手册:严格按照设备手册确认数据类型、占用寄存器数量、字节序和字序。
- 尝试不同字节序组合:使用 Modbus 在线工具或调试软件测试 ABCD, DCBA, BADC, CDAB 等所有可能的字节序组合,直到数据正确显示。
- 确认数据类型:确保主站解析时使用与从站一致的数据类型。
- 核对寄存器地址:确保请求的寄存器地址是正确的,并且与设备的具体数据点对应。
- 处理缩放/偏移:如果设备输出的是原始值,需要在主站程序中加入相应的转换公式。