设备-云端协议 v1

本文件是固件与服务端的共用契约,改字段前先改契约

传输

项值
协议MQTT 3.1.1(生产环境跑在 WSS 之上)
client_id / usernamedevice_id
passworddevice_secret,32 字节随机数,出厂写入 NVS
keepalive建议 60 s

主题

主题方向QoSRetain载荷
logs/{device_id}设备 → 云1否批量日志 JSON
status/{device_id}设备 → 云1是online / offline

status 必须配 LWT:遗嘱载荷 offline、retain = 1。云端靠这条 retained 消息判断在线状态,设备掉电时由 broker 代发。

日志批次载荷

{
  "device_id": "SW-A1B2C3",
  "boot_id":   "a1b2c3d4",
  "seq":       12345,
  "lines": [
    {"ts": 123456, "lvl": "I", "msg": "wifi: connected, rssi=-52"}
  ]
}
字段说明
device_idSW- + MAC 后 6 位大写 hex,首次启动生成后写 NVS,终身不变
boot_id每次重启随机 8 位 hex,用于区分重启前后
seq批次序号,全局单调递增,每 5 分钟落一次 NVS,掉电可续
lines[].ts毫秒,开机相对时间
lines[].lvlE/W/D/I,单字符
lines[].msg单行文本,上限 512 字节,超长固件侧截断

seq 语义(关键)

粒度是批次不是行,一个 seq 对应一条 MQTT 消息。服务端据此做三件事:

  1. 去重 —— (device_id, seq) 唯一。QoS 1 重发、离线回放与实时流重叠,都只入库一次。
  2. 查缺口 —— 若 seq > max_seq + 1,则 [max_seq+1, seq-1] 记为缺失批次。这是「离线缓冲是否无损补齐」的客观判据。
  3. 排序 —— 历史回放时保证顺序。

回放历史不误判:seq <= max_seq 时服务端不判缺口,视为迟到或重发,只走去重路径。

级别解析(固件侧)

含 ERROR / ERR / F / [E]  → E
含 WARN / W               → W
含 DEBUG / D              → D
其余                       → I

服务端只接受单字符,多字符一律归一为 I(不取首字母)。

攒批策略

攒满 50 行或 200 ms 到达即发。服务端单批次硬上限 512 行,超出部分截断并记录告警。

异常行为

场景设备行为
WiFi 断连继续抓取入 RAM → 落 littlefs,界面/LED 显示离线缓冲
broker 不可达指数退避重连 1→2→5→10→30 s + 随机抖动,期间不丢日志
CONNACK rc=4/5认证失败,记 E 级日志,清 WiFi 配置进配网(保留 device_id/secret)
PUBACK 超时重发该批次(服务端靠 seq 去重,幂等)

服务端约束:MQTT 回调里只做解析和入队,绝不碰数据库。回调线程一旦被磁盘 IO 卡住,QoS 1 的 PUBACK 就会超时,设备端无限重发,最终雪崩。落库集中在单线程串行执行;队列上限 20,000,满了宁可丢批次也不让内存爆。