3. 心跳报文结构:三大协议对比
聊完心跳机制的原理,咱们得看看实际报文长什么样。不同协议对心跳的定义完全不同——有的把它藏在数据帧里,有的专门开一个通道,还有的干脆拿寄存器当心跳用。我最早接触这些协议时,也被它们的报文结构搞晕过。后来踩了几个坑,才慢慢摸清门道。
今天咱们就掰开揉碎,看看 EtherCAT、PROFINET 和 Modbus TCP 这三种主流协议,它们的心跳报文到底怎么组织的。
3.1 EtherCAT:藏在过程数据里的“隐形心跳”
EtherCAT 的心跳,说白了就是过程数据对象(PDO)里的一个位或一个字节。它没有独立的报文类型,而是作为周期性数据的一部分,跟着控制周期一起跑。
我个人习惯把 EtherCAT 心跳分为两种:
- PDO 心跳位:在 TxPDO(从站→主站)中分配一个 bit,每次通信时翻转。主站检测到该位长时间不变,就判定从站掉线。
- SM 同步管理器看门狗:EtherCAT 从站芯片内部有硬件看门狗,如果 SM 通道在规定时间内没有数据交换,从站自动进入 Safe-Operational 状态。
下面是一个典型的 EtherCAT 从站心跳 PDO 映射表:
| 索引 | 名称 | 类型 | 位宽 | 说明 |
|---|---|---|---|---|
| 0x6000:01 | Heartbeat_Counter | UNSIGNED8 | 8 bit | 每周期递增,溢出归零 |
| 0x6000:02 | Heartbeat_Status | BIT2 | 2 bit | 00=正常, 01=警告, 10=错误 |
| 0x6000:03 | Watchdog_Timeout | UNSIGNED16 | 16 bit | 看门狗超时时间,单位 μs |
我在项目中遇到过一个问题:某 EtherCAT 从站偶尔会触发看门狗复位,但主站日志里没有任何错误。后来发现是 PDO 映射里心跳位和状态位挤在同一个字节,位操作冲突导致计数器没更新。嗯,这里要注意——EtherCAT 的位映射必须仔细核对,尤其是多个从站共用同一个映射模板时。
3.2 PROFINET:用“数据保持”机制做心跳
PROFINET 的心跳机制和 EtherCAT 不太一样。它没有专门的心跳报文,而是靠 数据保持(Data Hold) 和 AR(应用关系)监控 来实现。
你想想看,PROFINET 是基于以太网的标准 TCP/IP 协议栈,它的实时数据走 RT(实时)通道,非实时数据走 UDP/IP。心跳检测主要依赖以下三个层次:
- LLDP 邻居发现:每个 PROFINET 设备每隔几秒发送 LLDP 帧,宣告自己的存在。如果主站连续丢失 N 个 LLDP 帧,就认为邻居丢失。
- AR 监控:应用关系(Application Relation)建立后,主站和从站之间有一个隐式的保活机制。如果超过监控时间没收到任何数据,AR 自动断开。
- I&M 数据中的心跳计数器:PROFINET 的 Identification & Maintenance 功能块里,有一个可选的 Heartbeat Counter,用于诊断。
下面是一个 PROFINET 从站的 GSDML 文件中关于心跳的配置片段:
<ApplicationProcess>
<DeviceAccessPointList>
<DeviceAccessPointItem ID="DAP1">
<SystemDefinedSubmoduleList>
<SystemDefinedSubmoduleItem ID="Heartbeat">
<Name>Heartbeat</Name>
<IOData>
<Input>
<DataItem ID="HeartbeatCounter" DataType="Unsigned8"/>
</Input>
</IOData>
</SystemDefinedSubmoduleItem>
</SystemDefinedSubmoduleList>
</DeviceAccessPointItem>
</DeviceAccessPointList>
</ApplicationProcess>
我曾经调试过一个 PROFINET 从站,主站总是报“AR 超时断开”。查了半天,发现是从站的看门狗时间设得比主站的监控时间还长。结果主站先超时了,从站还觉得自己好好的。这个坑我印象很深——PROFINET 的看门狗时间必须小于主站的 AR 监控时间,否则就会出现这种“假在线”现象。
3.3 Modbus TCP:最直白的心跳——寄存器轮询
Modbus TCP 的心跳,说白了就是主站定期去读从站的某个寄存器。它没有协议层面的保活机制,完全靠应用层实现。
我个人习惯的做法是:在从站里分配一个 16 位寄存器(比如地址 0x0001),主站每隔 100ms 读一次。从站每收到一次读请求,就把这个寄存器的值加 1。如果主站连续 3 次读到的值没变化,或者干脆读不到,就判定从站掉线。
下面是一个典型的 Modbus TCP 心跳报文抓包示例:
// 主站发送:读保持寄存器请求
Transaction ID: 0x0001
Protocol ID: 0x0000
Length: 0x0006
Unit ID: 0x01
Function Code: 0x03 (Read Holding Registers)
Starting Address: 0x0001
Quantity: 0x0001
// 从站响应:返回心跳计数值
Transaction ID: 0x0001
Protocol ID: 0x0000
Length: 0x0005
Unit ID: 0x01
Function Code: 0x03
Byte Count: 0x02
Register Value: 0x00A3 // 当前心跳计数值 163
Modbus TCP 的心跳实现虽然简单,但有个致命问题:它依赖主站的主动轮询。如果主站程序卡死或网络拥堵,从站完全不知道。所以我在实际项目中,通常会在从站侧也加一个独立看门狗——如果超过 500ms 没收到任何 Modbus 请求,从站自动输出安全状态。
| 协议 | 心跳实现方式 | 报文开销 | 实时性 | 典型周期 |
|---|---|---|---|---|
| EtherCAT | PDO 内嵌计数器 + 硬件看门狗 | 极低(1~2 bit) | 高(μs 级) | 100 μs ~ 1 ms |
| PROFINET | LLDP + AR 监控 + I&M 计数器 | 低(周期性 LLDP 帧) | 中(ms 级) | 1 ms ~ 10 ms |
| Modbus TCP | 应用层寄存器轮询 | 高(完整 TCP 报文) | 低(10 ms 以上) | 50 ms ~ 500 ms |
3.4 三种协议的心跳结构对比图
下面这张图,我把三种协议的心跳报文结构画在一起了。你可以直观地看到它们的差异:
3.5 我的选型建议
三种协议的心跳机制各有优劣,没有绝对的好坏。我个人选型时主要看三点:
- 实时性要求:如果控制周期在 1ms 以下,别犹豫,直接上 EtherCAT。它的硬件看门狗和 PDO 内嵌心跳是最可靠的。
- 系统复杂度:PROFINET 适合西门子生态的工厂,它的 AR 监控和 LLDP 机制虽然复杂,但和西门子 PLC 配合得天衣无缝。
- 维护成本:Modbus TCP 虽然心跳报文最“笨”,但调试工具多、文档丰富。我见过不少老工程师,用串口助手加一个定时器就能搞定心跳检测。