/* WiFi/MQTT 链路的平台无关部分(A14,需求 §10.8「A14 定稿的 MQTT topic」)。 ESP 胶水(WiFi 驱动、esp-mqtt、NVS)在 mlink_esp.c。 ---- topic(base = `phonerail/`,id = STA MAC 12 位小写十六进制)---------- | topic | 方向 | QoS | retain | 内容 | |---|---|---|---|---| | `/cmd` | 服务器 → 板 | 1 | 否 | 一条命令 JSON(与 USB 的 `@C` 负载完全相同,**不带前缀与 CRC**) | | `/rsp` | 板 → 服务器 | 1 | 否 | 一条回应 JSON | | `/tele` | 板 → 服务器 | 0 | 否 | 周期遥测 = 一条 `status` 回应(无 seq),默认 1 s | | `/state` | 板 → 服务器 | 1 | **是** | `{"online":true,"fw":..,"ip":..}`;掉线由遗嘱改成 `{"online":false}` | · MQTT 下面是 TCP,完整性有保证,所以不要 CRC;分帧就是「一条消息一条命令」。 · QoS 1 意味着 broker 可能重投 ⇒ 板子会收到同一条命令两次 —— 这正是 seq 判重 (cmd.h)存在的理由,这里不做第二层去重。 · 服务器订阅 `phonerail/+/rsp`、`phonerail/+/tele`、`phonerail/+/state` 就能管所有板子, 新板上线时 retained 的 state 告诉它有这块板。 */ #ifndef MLINK_H #define MLINK_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define MLINK_TOPIC_ROOT "phonerail" #define MLINK_TOPIC_MAX 40 /* "phonerail/" + 12 + "/state" + 0 */ typedef enum { MLINK_T_CMD = 0, MLINK_T_RSP, MLINK_T_TELE, MLINK_T_STATE, MLINK_T_COUNT, } mlink_topic_t; /* MAC → id(12 位小写十六进制)+ 四个 topic。 */ void mlink_init(const uint8_t mac[6]); const char *mlink_id(void); const char *mlink_topic(mlink_topic_t t); /* 遗嘱消息(静态,建连时交给 broker)。 */ #define MLINK_STATE_OFFLINE "{\"online\":false}" /* 上线消息。返回长度,装不下返回 0。 */ size_t mlink_state_online(const char *fw, const char *ip, char *out, size_t cap); /* 一条入站 `cmd` 消息 → 可以投进 cmd_queue 的一行(带结尾 0)。 `len` 是本片长度,`total` 是整条消息长度(esp-mqtt 会把大消息切片送来)。 返回 NULL = 可以投;否则返回链路层错误名(`too_long` / `bad_frame`), 由胶水直接回到 rsp(不进命令处理器,同 USB 的链路层错误)。 */ const char *mlink_inbound(const char *data, int len, int total, char *line, size_t cap); /* WiFi 重连退避(需求 §10.12:指数退避 + 上限):第 attempt 次失败之后等多久。 1 s、2 s、4 s … 封顶 60 s。**不加随机**:一个机架五块板同时掉线再同时重连, 对家用 AP 不构成压力,而确定性让测试与现场日志都好读。 */ uint32_t mlink_backoff_ms(uint32_t attempt); #define MLINK_BACKOFF_MAX_MS 60000u /* 遥测节拍:到点了吗(并推进)。到点但上一条遥测还没发出去时,胶水跳过这一拍 (记 `tele_skipped`),免得网络堵的时候往命令队列里越塞越多。 */ typedef struct { uint32_t last_ms; bool started; } mlink_tele_t; bool mlink_tele_due(mlink_tele_t *t, uint32_t now_ms, uint32_t period_ms); #ifdef __cplusplus } #endif #endif /* MLINK_H */