/* MQTT 链路的平台无关部分。topic 表与各条规矩在 mlink.h。 */ #include "mlink.h" #include #include #include "cmd.h" #include "cmd_json.h" static char s_id[13]; static char s_topic[MLINK_T_COUNT][MLINK_TOPIC_MAX]; static const char *const k_suffix[MLINK_T_COUNT] = { [MLINK_T_CMD] = "cmd", [MLINK_T_RSP] = "rsp", [MLINK_T_TELE] = "tele", [MLINK_T_STATE] = "state", }; void mlink_init(const uint8_t mac[6]) { snprintf(s_id, sizeof(s_id), "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); for (int t = 0; t < MLINK_T_COUNT; t++) { snprintf(s_topic[t], sizeof(s_topic[t]), MLINK_TOPIC_ROOT "/%s/%s", s_id, k_suffix[t]); } } const char *mlink_id(void) { return s_id; } const char *mlink_topic(mlink_topic_t t) { return (t < MLINK_T_COUNT) ? s_topic[t] : ""; } size_t mlink_state_online(const char *fw, const char *ip, char *out, size_t cap) { cj_writer_t w; cj_w_init(&w, out, cap); cj_w(&w, "{\"online\":true,\"fw\":\"%s\",\"ip\":\"%s\"}", fw, ip); return w.overflow ? 0 : w.pos; } const char *mlink_inbound(const char *data, int len, int total, char *line, size_t cap) { /* 被切片的消息:命令最长 CMD_LINE_MAX,esp-mqtt 的缓冲 1 KB,切片只会出现在 超长消息上 —— 直接拒,不做重组(重组就是一块要管生命周期的缓冲)。 */ if (data == NULL || len <= 0 || len != total || cap == 0 || (size_t)len >= cap || len >= CMD_LINE_MAX) { return "too_long"; } for (int i = 0; i < len; i++) { const unsigned char c = (unsigned char)data[i]; /* 同 USB 的理由:NUL 会让 JSON 解析提前结束,后面的内容就被放过了。 */ if ((c < 0x20 && c != '\t') || c == 0x7F) { return "bad_frame"; } } memcpy(line, data, (size_t)len); line[len] = '\0'; return NULL; } uint32_t mlink_backoff_ms(uint32_t attempt) { if (attempt >= 6) { return MLINK_BACKOFF_MAX_MS; /* 1 s << 6 = 64 s,已经过顶 */ } const uint32_t ms = 1000u << attempt; return ms > MLINK_BACKOFF_MAX_MS ? MLINK_BACKOFF_MAX_MS : ms; } bool mlink_tele_due(mlink_tele_t *t, uint32_t now_ms, uint32_t period_ms) { if (!t->started) { t->started = true; t->last_ms = now_ms; return true; /* 一连上就先报一条:服务器立刻有数据 */ } if ((uint32_t)(now_ms - t->last_ms) < period_ms) { return false; } /* 推进一个整周期而不是「= now」:被挤晚了不累积漂移;落后超过一个周期就对齐到现在, 不补发(补发的现象是网络一恢复连着冒出一串旧遥测)。 */ t->last_ms += period_ms; if ((uint32_t)(now_ms - t->last_ms) >= period_ms) { t->last_ms = now_ms; } return true; }