- 0.1.19 配网热点:没配 WiFi / 连续 60 s 连不上时自己开 SoftAP,192.168.4.1 网页里加删 WiFi、改 MQTT; 配置拼成 wifi / mqtt 命令走唯一的命令处理器(新链路号 3 CMD_LINK_AP),MQTT 仍不收配置命令 - 0.1.20 热点口令固定 88888888 - 0.1.21 风扇:板温 >=40 ℃ 上电、<38 ℃ 断电,40~65 ℃ 线性调速;fan 命令加可选 sec(下限到点自动交回) - mqtt 命令加 keep_pass;net 回应加 ap;status 的 fan 加 left - 新增 test_prov(13 例),test_t_thermal +3;29 ctest 全绿 Co-Authored-By: Claude Opus 5.5 <[email protected]>
105 行
5.5 KiB
C
105 行
5.5 KiB
C
/* 配网热点(SoftAP + 网页,0.1.19,2026-10-06 用户定)的平台无关部分。
|
||
ESP 胶水(热点、HTTP 服务、DNS 劫持、热点口令的 NVS)在 prov_esp.c。
|
||
|
||
---- 为什么要有 ------------------------------------------------------------
|
||
0.1.18 之前改网络配置只有 USB 一条路(netcfg.h):路由器换了口令 / 板子搬了地方,
|
||
就得拆机插线,而 Hub 休眠之后连 COM 口都不在。现在:**连不上 WiFi 就自己开一个热点**,
|
||
手机连上去、在 192.168.4.1 的网页里改。
|
||
|
||
---- 什么时候开、什么时候关(`prov_step`)---------------------------------
|
||
| 情形 | 热点 |
|
||
|---|---|
|
||
| 一组 WiFi 都没配 | 立刻开(`nocfg`) |
|
||
| 配了,但连续 `PROV_FAIL_MS` 没拿到地址(含启动后一直连不上、连上过又掉) | 开(`fail`) |
|
||
| 开着、STA 仍没连上 | **一直开着**(不设超时:超时关掉 = 又只剩拆机) |
|
||
| STA 连上了,热点上没人 | 立刻关 |
|
||
| STA 连上了,热点上还有人 | 再留 `PROV_LINGER_MS` 让网页看到「连上了」,然后关(`linger`) |
|
||
|
||
⚠️ 路由器重启这类短暂断网也会让热点冒出来一会儿,网一恢复它就自己关。这是有意的:
|
||
「等多久才算真的连不上」没有对的数,而热点开着不碰任何输出 —— 五台手机照常供电(§10.2)。
|
||
|
||
---- 热点开着时 STA 还在重连 ----------------------------------------------
|
||
STA 每试一次都要离开热点的信道去扫一圈(≈2~3 s),试得太密网页就卡。所以
|
||
**热点上有人、且最近 `PROV_HOLD_MS` 内动过网页**时,重连间隔不短于 `PROV_SLOW_MS`
|
||
(`hold`,mlink_esp.c 的 on_wifi 里用)。只是放慢、**从不停**:停掉的话,
|
||
一台连着热点忘了断的手机就能让板子永远不回网。
|
||
|
||
---- 安全 ------------------------------------------------------------------
|
||
· 热点是 WPA2,口令**所有板子都是 `PROV_PASS`(88888888,0.1.20,2026-10-06 用户定)**,编在固件里。
|
||
0.1.19 是每板一份随机口令存 NVS —— 断网时人手边未必有那张纸条,用户选了好记的。
|
||
`net` 命令的 `ap.pass` 照样报它;它不是 netcfg.h 说的那「两个密码」:知道它只能**写**配置,
|
||
网页与 `net` 一样从不回显 WiFi / MQTT 口令。
|
||
· 网页只做三件事:读 `net`、扫描、提交 `wifi` / `mqtt`。别的命令从这条链路进不来
|
||
(`prov_build_line` 只会拼出这两条)。
|
||
· 能连上热点的人可以把板子指到另一个 broker —— 等同于拿到这块板。口令是公开的固定值,
|
||
所以这道门实际只靠两条:热点只在「板子没网」时才开,而且人必须在无线范围内。 */
|
||
#ifndef PROV_H
|
||
#define PROV_H
|
||
|
||
#include <stdbool.h>
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#define PROV_FAIL_MS 60000u /* STA 连续这么久没地址 ⇒ 开热点 */
|
||
#define PROV_LINGER_MS 60000u /* STA 连上后,热点上还有人时再留多久 */
|
||
#define PROV_HOLD_MS 300000u /* 最后一次动网页之后,放慢重连持续多久 */
|
||
#define PROV_SLOW_MS 20000u /* 放慢时两次重连之间至少隔多久 */
|
||
|
||
typedef enum {
|
||
PROV_WHY_OFF = 0,
|
||
PROV_WHY_NOCFG,
|
||
PROV_WHY_FAIL,
|
||
PROV_WHY_LINGER,
|
||
} prov_why_t;
|
||
|
||
typedef struct {
|
||
int n_wifi; /* 表里有几组 WiFi */
|
||
bool wifi_up; /* STA 有地址 */
|
||
int ap_sta; /* 热点上连着几台 */
|
||
bool touched; /* 上一拍以来网页有过「人的动作」(打开页面 / 扫描 / 提交;状态轮询不算) */
|
||
} prov_in_t;
|
||
|
||
typedef struct {
|
||
bool ap; /* 热点该开着 */
|
||
bool hold; /* STA 重连要放慢 */
|
||
prov_why_t why;
|
||
} prov_out_t;
|
||
|
||
void prov_init(uint32_t now);
|
||
prov_out_t prov_step(const prov_in_t *in, uint32_t now);
|
||
const char *prov_why_str(prov_why_t w);
|
||
|
||
/* ---- 热点的名字与口令 ------------------------------------------------------ */
|
||
|
||
#define PROV_SSID_MAX 20 /* "PhoneRail-" + id 末 6 位 + 0 */
|
||
#define PROV_PASS "88888888" /* 热点口令,所有板子一样(见文件头「安全」)。WPA2 要 8~63 位 */
|
||
|
||
/* `PhoneRail-<id 末 6 位>`。 */
|
||
void prov_ssid(const char *id, char out[PROV_SSID_MAX]);
|
||
|
||
/* ---- 网页表单 → 一行命令 ---------------------------------------------------
|
||
网页 POST 的正文是按行分的纯文本(免得在固件里做 URL 解码):
|
||
|
||
wifi\n<ssid>\n<口令> 加一组 / 改同名那组的口令
|
||
del\n<ssid> 删一组
|
||
mqtt\n<uri>\n<账号>\n<口令>\n<tele_ms> 口令留空 = 不改(`keep_pass`);tele_ms 留空 = 默认
|
||
|
||
拼出来的就是 USB 上那两条命令,照样进唯一的命令处理器(cmd.h),校验一处都不重写。
|
||
命令的 JSON 解析器不认转义(cmd_json.h),所以字段里有引号 / 反斜杠 / 控制字符一律拒。
|
||
返回行长;0 = 拒,`*err` 是原因(`bad_form` / `bad_char` / `too_long`)。 */
|
||
size_t prov_build_line(const char *body, uint32_t seq, char *out, size_t cap, const char **err);
|
||
|
||
/* 一段不可信的字符串(扫描到的 SSID)→ JSON 字符串的内容:引号 / 反斜杠 / 控制字符写成 \u00XX,
|
||
其余字节原样(按 UTF-8 理解)。返回写了几个字符;装不下返回 0 且 out 为空串。 */
|
||
size_t prov_json_esc(const char *s, char *out, size_t cap);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* PROV_H */
|