文件
zhou yang和Claude Opus 5.5 a32fa06c43 固件:A12 命令处理器 + A13 USB 分帧 + A14 WiFi/MQTT(含 C1 配网)
- A12 单一命令处理器(cmd*.c):一行 JSON 进出、seq 全局判重(32 条按值)、
  限幅 / 空槽一律拒 / 拒绝记事件;命令号与错误码冻结(进 flash)。新增 power 命令,
  fan 改为只抬不压的下限(t_thermal_set_floor)。rail_set_mv 注明不拿锁,调用方包 xact。
- A13 USB-Serial-JTAG 分帧(ulink*.c):@C / @R 前缀 + CRC-16,日志行首 @ 垫空格,
  任意切块可切;关掉 USB 副控制台(它绕开驱动写 FIFO)。PC 端客户端 tools/prlink.py。
- A14 + C1(netcfg / mlink / cmd_net):topic phonerail/<mac>/{cmd,rsp,tele,state},
  mqtts(evse-ca 编进固件)、1 s 遥测、遗嘱离线;wifi / mqtt 配置只收 USB、存 NVS,
  net 永不回显密码。
- 协议定稿写进需求文档 §10.8(A12 / A13 / A14 三节);N7 撤销改 D7(机架管理前后端)。

宿主机 23 个 ctest / 400 例全绿;板 B 已烧 0.1.0-a14,经 WiFi → 公网 → 8883 TLS 真连网验过。
固件 0xE2220 / 1 MB 分区只剩 12%,C2 前要重排分区表。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 12:33:18 +08:00

76 行
3.2 KiB
C

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* WiFi/MQTT 链路的平台无关部分(A14,需求 §10.8「A14 定稿的 MQTT topic」)。
ESP 胶水(WiFi 驱动、esp-mqtt、NVS)在 mlink_esp.c。
---- topic(base = `phonerail/<id>`,id = STA MAC 12 位小写十六进制)----------
| topic | 方向 | QoS | retain | 内容 |
|---|---|---|---|---|
| `<base>/cmd` | 服务器 → 板 | 1 | 否 | 一条命令 JSON(与 USB 的 `@C` 负载完全相同,**不带前缀与 CRC**) |
| `<base>/rsp` | 板 → 服务器 | 1 | 否 | 一条回应 JSON |
| `<base>/tele` | 板 → 服务器 | 0 | 否 | 周期遥测 = 一条 `status` 回应(无 seq),默认 1 s |
| `<base>/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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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 */