MQTT 心跳与掉线判定的量化校核
支持四象限运行、采用 FOC 驱动方式的 AQMD6008BLS-TE/2 这类机型常装在 AGV、卷扬等移动设备上,网络切换与弱覆盖是常态。这类设备最容易踩的坑不是连不上,而是"掉线了但云端还以为在线",或心跳太密把电池耗光。
一、超时判定不是心跳周期本身
阿里云物联网平台的保活时间取值范围为 30 秒~1200 秒,建议取值 300 秒以上,不在范围内会被拒绝连接;平台每隔 30 秒定时检测一次保活心跳,最大超时时间定义为"保活心跳时间×1.5+定时检测的等待时间",超过该时间未收到设备消息即断开连接。
取 keepalive=300s 做算例:
| 项目 | 数值 | 说明 |
|---|---|---|
| 保活心跳时间 | 300 s | 设备 CONNECT 中携带 |
| 1.5 倍部分 | 450 s | 平台判定基准 |
| 定时检测等待 | 0~30 s | 上线时刻与检测时刻的相位差 |
| 最大超时 | 450~480 s | 最坏情况 8 分钟 |
| 设备侧感知延迟 | ≤300 s | 下一次心跳失败才发现 |
这解释了两个现象:一是设备断网后,控制台状态可能几分钟才变离线,这是机制而非 bug;二是 keepalive 设成 60s 虽然让感知更快,但移动设备每天多唤醒 4 次以上,电池型设备撑不住。
二、心跳流量与唤醒成本
平台侧说明,单个 2 字节心跳包按每 120 秒一次计算,月均流量约 43.2 KB,且 MQTT 心跳不计入物联网平台的消息量统计,只产生网络流量。但真实成本不在字节数,而在射频唤醒:4G 模块一次附着与数据传输的能耗远高于传输本身,按示例值每次唤醒等效 500 KB 流量计,keepalive 从 300s 降到 60s 会让唤醒次数增加 5 倍,这是电池设备失效的主因。
判据建议:固定供电设备取 120~300s;电池/太阳能设备取 600~1200s 并配合业务消息复用;对实时性要求高的场景不要靠缩短心跳,而应改用业务数据上行来重置计时器(收到 PUBLISH、SUBSCRIBE、PING 或 PUBACK 都会重置计时器)。
三、实现要点
```c
/ 心跳与业务复用:有业务发送就不额外发 ping /
void keepalive_tick(uint32_t now_s, uint32_t last_tx_s, uint32_t keepalive_s)
{
if (now_s - last_tx_s < keepalive_s) return; / 业务已重置计时 /
mqtt_send_ping();
}
/ 重连采用指数退避,避免断网恢复时风暴 /
uint32_t backoff_ms(uint32_t attempt)
{
uint32_t t = 1000u << (attempt > 6 ? 6 : attempt); / 1s..64s /
return t + (rand() % 500); / 抖动 /
}
注意 SDK 侧的保活参数语义与平台不同:C Link SDK 中 AIOT_MQTTOPT_KEEPALIVE_SEC 默认 1200、取值 30~1200,AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS 默认 25000ms,AIOT_MQTTOPT_HEARTBEAT_MAX_LOST 默认 2。三个参数要一起算,只看 keepalive 会误判实际重连时机。
四、验证方法
一是现场拔网/切卡测试,用抓包记录设备最后一次发包与控制台转离线的时刻差,核对是否落在 450~480s 区间;二是长时间挂机统计心跳包数与流量,与 43.2 KB/月量级对照;三是弱网注入丢包,观察是否出现"心跳丢失但未重连"或"频繁重连风暴"。
五、小结
掉线判定按"保活×1.5+检测等待"计算,不是心跳周期本身,300s 心跳对应最坏 8 分钟才判离线。心跳成本主要在射频唤醒而非字节数,电池设备应放宽心跳并复用业务消息重置计时,重连必须加退避与抖动,最后用断网实测闭环。