1	/**
2	 * wm_strings_zh.h
3	 * 中文语言包 for WiFiManager
4	 * 基于 wm_strings_en.h 翻译
5	 *
6	 * WiFiManager, a library for the ESP8266/Arduino platform
7	 * for configuration of WiFi credentials using a Captive Portal
8	 *
9	 * @author Creator tzapu
10	 * @author tablatronix
11	 * @version 0.0.0
12	 * @license MIT
13	 */
14
15	#ifndef _WM_STRINGS_ZH_H_
16	#define _WM_STRINGS_ZH_H_
17
18	// strings files must include a consts file!
19	#include "wm_consts_en.h" // include constants, tokens, routes
20
21	const char WM_LANGUAGE[] PROGMEM = "zh-CN"; // i18n lang code
22
23	const char HTTP_HEAD_START[]       PROGMEM = "<!DOCTYPE html>"
24	""
25	"<meta name='format-detection' content='telephone=no'>"
26	"<meta charset='UTF-8'>"
27	"<meta  name='viewport' content='width=device-width,initial-scale=1,user-scalable=no'/>"
28	"<title>{v}</title>";
29
30	const char HTTP_SCRIPT[]           PROGMEM = "";
37
38	const char HTTP_HEAD_END[]         PROGMEM = "
"; // {c} = _bodyclass
39
40	const char HTTP_ROOT_MAIN[]        PROGMEM = "
{t}

{v}
";
41
42	const char * const HTTP_PORTAL_MENU[] PROGMEM = {
43	"<form action='/wifi'    method='get'><button>配置 WiFi</button></form>
\n",     // MENU_WIFI
44	"<form action='/0wifi'   method='get'><button>配置 WiFi (不扫描)</button></form>
\n", // MENU_WIFINOSCAN
45	"<form action='/info'    method='get'><button>系统信息</button></form>
\n",       // MENU_INFO
46	"<form action='/param'   method='get'><button>参数设置</button></form>
\n",       // MENU_PARAM
47	"<form action='/close'   method='get'><button>关闭页面</button></form>
\n",       // MENU_CLOSE
48	"<form action='/restart' method='get'><button>重启设备</button></form>
\n",       // MENU_RESTART
49	"<form action='/exit'    method='get'><button>退出配网</button></form>
\n",       // MENU_EXIT
50	"<form action='/erase'   method='get'><button class='D'>清除配置</button></form>
\n", // MENU_ERASE
51	"<form action='/update'  method='get'><button>固件升级</button></form>
\n",       // MENU_UPDATE
52	"

" // MENU_SEP
53	};
54
55	const char HTTP_PORTAL_OPTIONS[]   PROGMEM = "";
56	const char HTTP_ITEM_QI[]          PROGMEM = "

";
57	const char HTTP_ITEM_QP[]          PROGMEM = "
{r}%
";
58	const char HTTP_ITEM[]             PROGMEM = "
{v}{qi}{qp}
";
59
60	const char HTTP_FORM_START[]       PROGMEM = "<form method='POST' action='{v}'>";
61	const char HTTP_FORM_WIFI[]        PROGMEM = "WiFi 名称 (SSID)<input id='s' name='s' maxlength='32' autocorrect='off' autocapitalize='none' placeholder='{v}'>
WiFi 密码<input id='p' name='p' maxlength='64' type='password' placeholder='{p}'><input type='checkbox' id='showpass' onclick='f()'> 显示密码
";
62	const char HTTP_FORM_WIFI_END[]    PROGMEM = "";
63	const char HTTP_FORM_STATIC_HEAD[] PROGMEM = "

";
64	const char HTTP_FORM_END[]         PROGMEM = "

<button type='submit'>保存配置</button></form>";
65	const char HTTP_FORM_LABEL[]       PROGMEM = "{t}";
66	const char HTTP_FORM_PARAM_HEAD[]  PROGMEM = "

";
67	const char HTTP_FORM_PARAM[]       PROGMEM = "
<input id='{i}' name='{n}' maxlength='{l}' value='{v}' {c}>\n";
68
69	const char HTTP_SCAN_LINK[]        PROGMEM = "
<form action='/wifi?refresh=1' method='POST'><button name='refresh' value='1'>重新扫描</button></form>";
70	const char HTTP_SAVED[]            PROGMEM = "
正在保存配置
尝试连接 WiFi...
如果连接失败，请重新连接 AP 再试一次
";
71	const char HTTP_PARAMSAVED[]       PROGMEM = "
配置已保存

";
72	const char HTTP_END[]              PROGMEM = "
";
73	const char HTTP_ERASEBTN[]         PROGMEM = "
<form action='/erase' method='get'><button class='D'>清除 WiFi 配置</button></form>";
74	const char HTTP_UPDATEBTN[]        PROGMEM = "
<form action='/update' method='get'><button>固件升级</button></form>";
75	const char HTTP_BACKBTN[]          PROGMEM = "

<form action='/' method='get'><button>返回首页</button></form>";
76
77	const char HTTP_STATUS_ON[]        PROGMEM = "
已连接 {v}
IP 地址: {i}
";
78	const char HTTP_STATUS_OFF[]       PROGMEM = "
未连接 {v}{r}
";
79	const char HTTP_STATUS_OFFPW[]     PROGMEM = "
WiFi 密码错误";
80	const char HTTP_STATUS_OFFNOAP[]   PROGMEM = "
未找到该 WiFi";
81	const char HTTP_STATUS_OFFFAIL[]   PROGMEM = "
无法连接";
82	const char HTTP_STATUS_NONE[]      PROGMEM = "
未设置 WiFi
";
83	const char HTTP_BR[]               PROGMEM = "
";
84
85	const char HTTP_STYLE[]            PROGMEM = "";
109
110	#ifndef WM_NOHELP
111	const char HTTP_HELP[]             PROGMEM =
112	 "

可用页面

"
113	 "
"
114	 "

页面	功能

"
115	 "
/	"
116	 "主菜单页面
"
117	 "
/wifi	"
118	 "显示 WiFi 扫描结果并配置连接 (/0wifi 不扫描)
"
119	 "
/wifisave	"
120	 "保存 WiFi 配置并连接设备
"
121	 "
/param	"
122	 "参数设置页面
"
123	 "
/info	"
124	 "系统信息页面
"
125	 "
/u	"
126	 "固件 OTA 升级
"
127	 "
/close	"
128	 "关闭弹出窗口，配网服务保持运行
"
129	 "
/exit	"
130	 "退出配网模式，配网服务将关闭
"
131	 "
/restart	"
132	 "重启设备
"
133	 "
/erase	"
134	 "清除 WiFi 配置并重启。清除后设备不会自动连接，需重新配网
"
135	 "
"
136	 "
GitHub https://github.com/tzapu/WiFiManager.";
137	#else
138	const char HTTP_HELP[]             PROGMEM = "";
139	#endif
140
141	const char HTTP_UPDATE[] PROGMEM = "上传新固件
<form method='POST' action='u' enctype='multipart/form-data' onchange=\"(function(el){document.getElementById('uploadbin').style.display = el.value=='' ? 'none' : 'initial';})(this)\"><input type='file' name='update' accept='.bin,application/octet-stream'><button id='uploadbin' type='submit' class='h D'>开始升级</button></form>* 如果在配网页面内无法使用，请在浏览器中打开 http://192.168.4.1";
142	const char HTTP_UPDATE_FAIL[] PROGMEM = "
升级失败!
请重启设备后重试
";
143	const char HTTP_UPDATE_SUCCESS[] PROGMEM = "
升级成功!
设备正在重启...
";
144
145	#ifdef WM_JSTEST
146	const char HTTP_JS[] PROGMEM =
147	"";
163	#endif
164
165	// Info html
166	#ifdef ESP32
167		const char HTTP_INFO_esphead[]    PROGMEM = "
ESP32 信息

";
168		const char HTTP_INFO_chiprev[]    PROGMEM = "
芯片版本

{1}
";
169	  	const char HTTP_INFO_lastreset[]  PROGMEM = "
上次复位原因

CPU0: {1}
CPU1: {2}
";
170	  	const char HTTP_INFO_aphost[]     PROGMEM = "
AP 主机名

{1}
";
171	    const char HTTP_INFO_psrsize[]    PROGMEM = "
PSRAM 大小

{1} 字节
";
172		const char HTTP_INFO_temp[]       PROGMEM = "
芯片温度

{1}°C / {2}°F
";
173	    const char HTTP_INFO_hall[]       PROGMEM = "
霍尔传感器

{1}
";
174	#else
175		const char HTTP_INFO_esphead[]    PROGMEM = "
ESP8266 信息

";
176		const char HTTP_INFO_fchipid[]    PROGMEM = "
Flash 芯片 ID

{1}
";
177		const char HTTP_INFO_corever[]    PROGMEM = "
Core 版本

{1}
";
178		const char HTTP_INFO_bootver[]    PROGMEM = "
Boot 版本

{1}
";
179		const char HTTP_INFO_lastreset[]  PROGMEM = "
上次复位原因

{1}
";
180		const char HTTP_INFO_flashsize[]  PROGMEM = "
实际 Flash 大小

{1} 字节
";
181	#endif
182
183	const char HTTP_INFO_memsmeter[]  PROGMEM = "
<progress value='{1}' max='{2}'></progress>
";
184	const char HTTP_INFO_memsketch[]  PROGMEM = "
内存 - 程序大小

已用 / 总计
{1} / {2} 字节";
185	const char HTTP_INFO_freeheap[]   PROGMEM = "
内存 - 可用堆

{1} 字节
";
186	const char HTTP_INFO_wifihead[]   PROGMEM = "

WiFi 信息

";
187	const char HTTP_INFO_uptime[]     PROGMEM = "
运行时间

{1} 分 {2} 秒
";
188	const char HTTP_INFO_chipid[]     PROGMEM = "
芯片 ID

{1}
";
189	const char HTTP_INFO_idesize[]    PROGMEM = "
Flash 大小

{1} 字节
";
190	const char HTTP_INFO_sdkver[]     PROGMEM = "
SDK 版本

{1}
";
191	const char HTTP_INFO_cpufreq[]    PROGMEM = "
CPU 频率

{1}MHz
";
192	const char HTTP_INFO_apip[]       PROGMEM = "
AP IP 地址

{1}
";
193	const char HTTP_INFO_apmac[]      PROGMEM = "
AP MAC 地址

{1}
";
194	const char HTTP_INFO_apssid[]     PROGMEM = "
AP SSID

{1}
";
195	const char HTTP_INFO_apbssid[]    PROGMEM = "
BSSID

{1}
";
196	const char HTTP_INFO_stassid[]    PROGMEM = "
Station SSID

{1}
";
197	const char HTTP_INFO_staip[]      PROGMEM = "
Station IP

{1}
";
198	const char HTTP_INFO_stagw[]      PROGMEM = "
网关地址

{1}
";
199	const char HTTP_INFO_stasub[]     PROGMEM = "
子网掩码

{1}
";
200	const char HTTP_INFO_dnss[]       PROGMEM = "
DNS 服务器

{1}
";
201	const char HTTP_INFO_host[]       PROGMEM = "
主机名

{1}
";
202	const char HTTP_INFO_stamac[]     PROGMEM = "
Station MAC

{1}
";
203	const char HTTP_INFO_conx[]       PROGMEM = "
连接状态

{1}
";
204	const char HTTP_INFO_autoconx[]   PROGMEM = "
自动连接

{1}
";
205
206	const char HTTP_INFO_aboutver[]     PROGMEM = "
WiFiManager 版本

{1}
";
207	const char HTTP_INFO_aboutarduino[] PROGMEM = "
Arduino 版本

{1}
";
208	const char HTTP_INFO_aboutsdk[]     PROGMEM = "
ESP-SDK/IDF 版本

{1}
";
209	const char HTTP_INFO_aboutdate[]    PROGMEM = "
构建日期

{1}
";
210
211	// ============================================================
212	// 文本常量
213	// ============================================================
214	const char S_brand[]              PROGMEM = "健康监测配网";
215	const char S_debugPrefix[]        PROGMEM = "*wm:";
216	const char S_y[]                  PROGMEM = "是";
217	const char S_n[]                  PROGMEM = "否";
218	const char S_enable[]             PROGMEM = "已启用";
219	const char S_disable[]            PROGMEM = "已禁用";
220	const char S_GET[]                PROGMEM = "GET";
221	const char S_POST[]               PROGMEM = "POST";
222	const char S_NA[]                 PROGMEM = "未知";
223	const char S_passph[]             PROGMEM = "********";
224	const char S_titlewifisaved[]     PROGMEM = "WiFi 已保存";
225	const char S_titlewifisettings[]  PROGMEM = "设置已保存";
226	const char S_titlewifi[]          PROGMEM = "配置 WiFi";
227	const char S_titleinfo[]          PROGMEM = "系统信息";
228	const char S_titleparam[]         PROGMEM = "参数设置";
229	const char S_titleparamsaved[]    PROGMEM = "设置已保存";
230	const char S_titleexit[]          PROGMEM = "退出配网";
231	const char S_titlereset[]         PROGMEM = "重置";
232	const char S_titleerase[]         PROGMEM = "清除配置";
233	const char S_titleclose[]         PROGMEM = "关闭页面";
234	const char S_options[]            PROGMEM = "选项";
235	const char S_nonetworks[]         PROGMEM = "未发现 WiFi 网络，请点击刷新重新扫描。";
236	const char S_staticip[]           PROGMEM = "静态 IP";
237	const char S_staticgw[]           PROGMEM = "静态网关";
238	const char S_staticdns[]          PROGMEM = "静态 DNS";
239	const char S_subnet[]             PROGMEM = "子网掩码";
240	const char S_exiting[]            PROGMEM = "正在退出配网模式";
241	const char S_resetting[]          PROGMEM = "设备将在几秒后重启";
242	const char S_closing[]            PROGMEM = "可以关闭此页面，配网服务将继续运行";
243	const char S_error[]              PROGMEM = "发生错误";
244	const char S_notfound[]           PROGMEM = "页面未找到\n\n";
245	const char S_uri[]                PROGMEM = "URI: ";
246	const char S_method[]             PROGMEM = "\n请求方法: ";
247	const char S_args[]               PROGMEM = "\n参数: ";
248	const char S_parampre[]           PROGMEM = "param_";
249
250	// debug strings
251	const char D_HR[]                 PROGMEM = "--------------------";
252
253	// softap ssid default prefix
254	#ifdef ESP8266
255	    const char S_ssidpre[]        PROGMEM = "ESP";
256	#elif defined(ESP32)
257	    const char S_ssidpre[]        PROGMEM = "ESP32";
258	#else
259	    const char S_ssidpre[]        PROGMEM = "WM";
260	#endif
261
262	#endif
263

/**
 * wm_strings_zh.h
 * 中文语言包 for WiFiManager
 * 基于 wm_strings_en.h 翻译
 *
 * WiFiManager, a library for the ESP8266/Arduino platform
 * for configuration of WiFi credentials using a Captive Portal
 *
 * @author Creator tzapu
 * @author tablatronix
 * @version 0.0.0
 * @license MIT
 */

#ifndef _WM_STRINGS_ZH_H_
#define _WM_STRINGS_ZH_H_

// strings files must include a consts file!
#include "wm_consts_en.h" // include constants, tokens, routes

const char WM_LANGUAGE[] PROGMEM = "zh-CN"; // i18n lang code

const char HTTP_HEAD_START[]       PROGMEM = "<!DOCTYPE html>"
""
"<meta name='format-detection' content='telephone=no'>"
"<meta charset='UTF-8'>"
"<meta  name='viewport' content='width=device-width,initial-scale=1,user-scalable=no'/>"
"<title>{v}</title>";

const char HTTP_SCRIPT[]           PROGMEM = "";

const char HTTP_HEAD_END[]         PROGMEM = "
"; // {c} = _bodyclass

const char HTTP_ROOT_MAIN[]        PROGMEM = "
{t}

{v}
";

const char * const HTTP_PORTAL_MENU[] PROGMEM = {
"<form action='/wifi'    method='get'><button>配置 WiFi</button></form>
\n",     // MENU_WIFI
"<form action='/0wifi'   method='get'><button>配置 WiFi (不扫描)</button></form>
\n", // MENU_WIFINOSCAN
"<form action='/info'    method='get'><button>系统信息</button></form>
\n",       // MENU_INFO
"<form action='/param'   method='get'><button>参数设置</button></form>
\n",       // MENU_PARAM
"<form action='/close'   method='get'><button>关闭页面</button></form>
\n",       // MENU_CLOSE
"<form action='/restart' method='get'><button>重启设备</button></form>
\n",       // MENU_RESTART
"<form action='/exit'    method='get'><button>退出配网</button></form>
\n",       // MENU_EXIT
"<form action='/erase'   method='get'><button class='D'>清除配置</button></form>
\n", // MENU_ERASE
"<form action='/update'  method='get'><button>固件升级</button></form>
\n",       // MENU_UPDATE
"

" // MENU_SEP
};

const char HTTP_PORTAL_OPTIONS[]   PROGMEM = "";
const char HTTP_ITEM_QI[]          PROGMEM = "

";
const char HTTP_ITEM_QP[]          PROGMEM = "
{r}%
";
const char HTTP_ITEM[]             PROGMEM = "
{v}{qi}{qp}
";

const char HTTP_FORM_START[]       PROGMEM = "<form method='POST' action='{v}'>";
const char HTTP_FORM_WIFI[]        PROGMEM = "WiFi 名称 (SSID)<input id='s' name='s' maxlength='32' autocorrect='off' autocapitalize='none' placeholder='{v}'>
WiFi 密码<input id='p' name='p' maxlength='64' type='password' placeholder='{p}'><input type='checkbox' id='showpass' onclick='f()'> 显示密码
";
const char HTTP_FORM_WIFI_END[]    PROGMEM = "";
const char HTTP_FORM_STATIC_HEAD[] PROGMEM = "

";
const char HTTP_FORM_END[]         PROGMEM = "

<button type='submit'>保存配置</button></form>";
const char HTTP_FORM_LABEL[]       PROGMEM = "{t}";
const char HTTP_FORM_PARAM_HEAD[]  PROGMEM = "

";
const char HTTP_FORM_PARAM[]       PROGMEM = "
<input id='{i}' name='{n}' maxlength='{l}' value='{v}' {c}>\n";

const char HTTP_SCAN_LINK[]        PROGMEM = "
<form action='/wifi?refresh=1' method='POST'><button name='refresh' value='1'>重新扫描</button></form>";
const char HTTP_SAVED[]            PROGMEM = "
正在保存配置
尝试连接 WiFi...
如果连接失败，请重新连接 AP 再试一次
";
const char HTTP_PARAMSAVED[]       PROGMEM = "
配置已保存

";
const char HTTP_END[]              PROGMEM = "
";
const char HTTP_ERASEBTN[]         PROGMEM = "
<form action='/erase' method='get'><button class='D'>清除 WiFi 配置</button></form>";
const char HTTP_UPDATEBTN[]        PROGMEM = "
<form action='/update' method='get'><button>固件升级</button></form>";
const char HTTP_BACKBTN[]          PROGMEM = "

<form action='/' method='get'><button>返回首页</button></form>";

const char HTTP_STATUS_ON[]        PROGMEM = "
已连接 {v}
IP 地址: {i}
";
const char HTTP_STATUS_OFF[]       PROGMEM = "
未连接 {v}{r}
";
const char HTTP_STATUS_OFFPW[]     PROGMEM = "
WiFi 密码错误";
const char HTTP_STATUS_OFFNOAP[]   PROGMEM = "
未找到该 WiFi";
const char HTTP_STATUS_OFFFAIL[]   PROGMEM = "
无法连接";
const char HTTP_STATUS_NONE[]      PROGMEM = "
未设置 WiFi
";
const char HTTP_BR[]               PROGMEM = "
";

const char HTTP_STYLE[]            PROGMEM = "";

#ifndef WM_NOHELP
const char HTTP_HELP[]             PROGMEM =
 "

可用页面

"
 "
"
 "

页面	功能

"
 "
/	"
 "主菜单页面
"
 "
/wifi	"
 "显示 WiFi 扫描结果并配置连接 (/0wifi 不扫描)
"
 "
/wifisave	"
 "保存 WiFi 配置并连接设备
"
 "
/param	"
 "参数设置页面
"
 "
/info	"
 "系统信息页面
"
 "
/u	"
 "固件 OTA 升级
"
 "
/close	"
 "关闭弹出窗口，配网服务保持运行
"
 "
/exit	"
 "退出配网模式，配网服务将关闭
"
 "
/restart	"
 "重启设备
"
 "
/erase	"
 "清除 WiFi 配置并重启。清除后设备不会自动连接，需重新配网
"
 "
"
 "
GitHub https://github.com/tzapu/WiFiManager.";
#else
const char HTTP_HELP[]             PROGMEM = "";
#endif

const char HTTP_UPDATE[] PROGMEM = "上传新固件
<form method='POST' action='u' enctype='multipart/form-data' onchange=\"(function(el){document.getElementById('uploadbin').style.display = el.value=='' ? 'none' : 'initial';})(this)\"><input type='file' name='update' accept='.bin,application/octet-stream'><button id='uploadbin' type='submit' class='h D'>开始升级</button></form>* 如果在配网页面内无法使用，请在浏览器中打开 http://192.168.4.1";
const char HTTP_UPDATE_FAIL[] PROGMEM = "
升级失败!
请重启设备后重试
";
const char HTTP_UPDATE_SUCCESS[] PROGMEM = "
升级成功!
设备正在重启...
";

#ifdef WM_JSTEST
const char HTTP_JS[] PROGMEM =
"";
#endif

// Info html
#ifdef ESP32
	const char HTTP_INFO_esphead[]    PROGMEM = "
ESP32 信息

";
	const char HTTP_INFO_chiprev[]    PROGMEM = "
芯片版本

{1}
";
  	const char HTTP_INFO_lastreset[]  PROGMEM = "
上次复位原因

CPU0: {1}
CPU1: {2}
";
  	const char HTTP_INFO_aphost[]     PROGMEM = "
AP 主机名

{1}
";
    const char HTTP_INFO_psrsize[]    PROGMEM = "
PSRAM 大小

{1} 字节
";
	const char HTTP_INFO_temp[]       PROGMEM = "
芯片温度

{1}°C / {2}°F
";
    const char HTTP_INFO_hall[]       PROGMEM = "
霍尔传感器

{1}
";
#else
	const char HTTP_INFO_esphead[]    PROGMEM = "
ESP8266 信息

";
	const char HTTP_INFO_fchipid[]    PROGMEM = "
Flash 芯片 ID

{1}
";
	const char HTTP_INFO_corever[]    PROGMEM = "
Core 版本

{1}
";
	const char HTTP_INFO_bootver[]    PROGMEM = "
Boot 版本

{1}
";
	const char HTTP_INFO_lastreset[]  PROGMEM = "
上次复位原因

{1}
";
	const char HTTP_INFO_flashsize[]  PROGMEM = "
实际 Flash 大小

{1} 字节
";
#endif

const char HTTP_INFO_memsmeter[]  PROGMEM = "
<progress value='{1}' max='{2}'></progress>
";
const char HTTP_INFO_memsketch[]  PROGMEM = "
内存 - 程序大小

已用 / 总计
{1} / {2} 字节";
const char HTTP_INFO_freeheap[]   PROGMEM = "
内存 - 可用堆

{1} 字节
";
const char HTTP_INFO_wifihead[]   PROGMEM = "

WiFi 信息

";
const char HTTP_INFO_uptime[]     PROGMEM = "
运行时间

{1} 分 {2} 秒
";
const char HTTP_INFO_chipid[]     PROGMEM = "
芯片 ID

{1}
";
const char HTTP_INFO_idesize[]    PROGMEM = "
Flash 大小

{1} 字节
";
const char HTTP_INFO_sdkver[]     PROGMEM = "
SDK 版本

{1}
";
const char HTTP_INFO_cpufreq[]    PROGMEM = "
CPU 频率

{1}MHz
";
const char HTTP_INFO_apip[]       PROGMEM = "
AP IP 地址

{1}
";
const char HTTP_INFO_apmac[]      PROGMEM = "
AP MAC 地址

{1}
";
const char HTTP_INFO_apssid[]     PROGMEM = "
AP SSID

{1}
";
const char HTTP_INFO_apbssid[]    PROGMEM = "
BSSID

{1}
";
const char HTTP_INFO_stassid[]    PROGMEM = "
Station SSID

{1}
";
const char HTTP_INFO_staip[]      PROGMEM = "
Station IP

{1}
";
const char HTTP_INFO_stagw[]      PROGMEM = "
网关地址

{1}
";
const char HTTP_INFO_stasub[]     PROGMEM = "
子网掩码

{1}
";
const char HTTP_INFO_dnss[]       PROGMEM = "
DNS 服务器

{1}
";
const char HTTP_INFO_host[]       PROGMEM = "
主机名

{1}
";
const char HTTP_INFO_stamac[]     PROGMEM = "
Station MAC

{1}
";
const char HTTP_INFO_conx[]       PROGMEM = "
连接状态

{1}
";
const char HTTP_INFO_autoconx[]   PROGMEM = "
自动连接

{1}
";

const char HTTP_INFO_aboutver[]     PROGMEM = "
WiFiManager 版本

{1}
";
const char HTTP_INFO_aboutarduino[] PROGMEM = "
Arduino 版本

{1}
";
const char HTTP_INFO_aboutsdk[]     PROGMEM = "
ESP-SDK/IDF 版本

{1}
";
const char HTTP_INFO_aboutdate[]    PROGMEM = "
构建日期

{1}
";

// ============================================================
// 文本常量
// ============================================================
const char S_brand[]              PROGMEM = "健康监测配网";
const char S_debugPrefix[]        PROGMEM = "*wm:";
const char S_y[]                  PROGMEM = "是";
const char S_n[]                  PROGMEM = "否";
const char S_enable[]             PROGMEM = "已启用";
const char S_disable[]            PROGMEM = "已禁用";
const char S_GET[]                PROGMEM = "GET";
const char S_POST[]               PROGMEM = "POST";
const char S_NA[]                 PROGMEM = "未知";
const char S_passph[]             PROGMEM = "********";
const char S_titlewifisaved[]     PROGMEM = "WiFi 已保存";
const char S_titlewifisettings[]  PROGMEM = "设置已保存";
const char S_titlewifi[]          PROGMEM = "配置 WiFi";
const char S_titleinfo[]          PROGMEM = "系统信息";
const char S_titleparam[]         PROGMEM = "参数设置";
const char S_titleparamsaved[]    PROGMEM = "设置已保存";
const char S_titleexit[]          PROGMEM = "退出配网";
const char S_titlereset[]         PROGMEM = "重置";
const char S_titleerase[]         PROGMEM = "清除配置";
const char S_titleclose[]         PROGMEM = "关闭页面";
const char S_options[]            PROGMEM = "选项";
const char S_nonetworks[]         PROGMEM = "未发现 WiFi 网络，请点击刷新重新扫描。";
const char S_staticip[]           PROGMEM = "静态 IP";
const char S_staticgw[]           PROGMEM = "静态网关";
const char S_staticdns[]          PROGMEM = "静态 DNS";
const char S_subnet[]             PROGMEM = "子网掩码";
const char S_exiting[]            PROGMEM = "正在退出配网模式";
const char S_resetting[]          PROGMEM = "设备将在几秒后重启";
const char S_closing[]            PROGMEM = "可以关闭此页面，配网服务将继续运行";
const char S_error[]              PROGMEM = "发生错误";
const char S_notfound[]           PROGMEM = "页面未找到\n\n";
const char S_uri[]                PROGMEM = "URI: ";
const char S_method[]             PROGMEM = "\n请求方法: ";
const char S_args[]               PROGMEM = "\n参数: ";
const char S_parampre[]           PROGMEM = "param_";

// debug strings
const char D_HR[]                 PROGMEM = "--------------------";

// softap ssid default prefix
#ifdef ESP8266
    const char S_ssidpre[]        PROGMEM = "ESP";
#elif defined(ESP32)
    const char S_ssidpre[]        PROGMEM = "ESP32";
#else
    const char S_ssidpre[]        PROGMEM = "WM";
#endif

#endif


HTML是烧录在单片机里了吗


Now I have a complete picture. The HTML is indeed compiled into the firmware. The WiFiManager library stores all HTML as C++ PROGMEM strings in `wm_strings_en.h` (English) and `wm_strings_zh.h` (Chinese - which is the one being used based on the Chinese content). These strings are then assembled programmatically in `WiFiManager.cpp` with functions like `handleRoot()`, `handleWifi()`, etc.

Let me show the user the key files and how it works.


对，**HTML 代码编译进了固件里**，烧录时跟随 `.bin` 文件写入 ESP32 的 Flash。

### 代码在哪里

配网页面的 HTML 分两层存放：

**① 库自带页面模板** (在 `.pio/libdeps/` 里，不随项目提交)：

| 文件 | 内容 |
|------|------|
| [wm_strings_zh.h](.pio\libdeps\esp32-s3-devkitc-1\WiFiManager\wm_strings_zh.h) | **中文** HTML 片段的 C++ 字符串 (`PROGMEM`) |
| `wm_strings_en.h` | 英文版本 |
| `WiFiManager.cpp` → `handleRoot()` / `handleWifi()` | 按顺序拼接 HTML 片段，填充变量后发送 |

**② 你项目里自定义的部分** ([src/wifi_manager.cpp](src/wifi_manager.cpp#L77-L83))：

```cpp
_wm.setTitle("🏥 五劳无感康养监测 - 设备配网");   // <title>
_wm.setCustomHeadElement("<style>                    // 注入额外CSS
    body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }
    .msg .info { font-size: 14px; }
</style>");
```

### 原理简图

```
浏览器请求 192.168.4.1/wifi
        │
        ▼
ESP32 WiFiManager 库 (运行在 ESP32 Flash 中)
        │
        ├─ 从 PROGMEM 读出 HTML 字符串 (wm_strings_zh.h)
        ├─ 拼接扫描到的 WiFi 列表
        ├─ 拼接自定义参数 (MQTT地址等)
        ├─ 注入 setCustomHeadElement() 的 CSS
        └─ 拼接成完整 HTML → 发回浏览器
```

整个 HTML 页面最长的一段是 `HTTP_STYLE[]`（[wm_strings_zh.h:85-108](.pio\libdeps\esp32-s3-devkitc-1\WiFiManager\wm_strings_zh.h#L85-L108)），一个 3KB 的内联 CSS 字符串，还包括 base64 的 WiFi 信号图标。

所以如果想改页面文字（比如把"WiFi 名称"改成"无线网络"），直接改 `wm_strings_zh.h` 即可。想要改样式或加额外元素，用 `setCustomHeadElement()` 注入。


<ide_opened_file>The user opened the file d:\PlatformIO1\Projects\Healthv2.0\src\wifi_manager.cpp in the IDE. This may or may not be related to the current task.</ide_opened_file>

我现在需要把配网界面优化。把代码给软件组


The user wants to optimize the WiFi provisioning interface and then hand over the code to the software team. They want me to improve the current WiFiManager configuration portal experience. Let me think about what improvements can be made:

1. The current interface is based on WiFiManager's default captive portal with custom parameters
2. They want to optimize it before giving it to the software team

Let me look at what can be improved in the current wifi_manager.cpp setup, and think about what kinds of optimizations would be valuable:

1. **Better styling** - The current custom CSS is minimal (just font-family and font-size). Could add better visual styling.
2. **Chinese localization improvements** - The default Chinese strings might need tuning.
3. **Better parameter organization** - Group related parameters, add descriptions.
4. **Improved UX flow** - Better messages, status indicators, etc.
5. **Custom HTML injection** - Use `setCustomHeadElement` or `setCustomMenuHTML` for more control.

Let me first read the full current state of wifi_manager.cpp and wifi_manager.h to understand what we have now, then plan the improvements.


让我先完整看一下当前的配网代码，然后做优化。


1	/**
2	 * @file wifi_manager.cpp
3	 * @brief WiFi 配网管理器实现
4	 */
5
6	#include "wifi_manager.h"
7
8	// 单例指针 + LED 任务
9	WiFiMgmt* WiFiMgmt::_instance = nullptr;
10	bool WiFiMgmt::_ledTaskRunning = false;
11	uint32_t WiFiMgmt::_ledTaskInterval = 300;
12
13	// ============================================================
14	// 构造函数 & 析构函数
15	// ============================================================
16
17	WiFiMgmt::WiFiMgmt()
18	    : _connected(false)
19	    , _lastCheckTime(0)
20	    , _buttonPressTime(0)
21	    , _buttonPressed(false)
22	    , _ledLastToggle(0)
23	    , _ledState(false)
24	    , _param_mqtt_server(nullptr)
25	    , _param_mqtt_port(nullptr)
26	    , _param_mqtt_user(nullptr)
27	    , _param_mqtt_pass(nullptr)
28	    , _param_device_id(nullptr)
29	    , _param_env_note(nullptr)
30	    , _provisionStateCb(nullptr)
31	{
32	    memset(&_config, 0, sizeof(_config));
33	    _instance = this;  // 设置单例
34	}
35
36	WiFiMgmt::~WiFiMgmt() {
37	    _instance = nullptr;
38	}
39
40	// ============================================================
41	// 初始化
42	// ============================================================
43
44	bool WiFiMgmt::begin() {
45	    Serial.println(F("\n========================================"));
46	    Serial.println(F("  WiFi 配网管理器 初始化"));
47	    Serial.println(F("========================================"));
48
49	    // 初始化引脚
50	    pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
51	    pinMode(LED_WIFI_PIN, OUTPUT);
52	    digitalWrite(LED_WIFI_PIN, LOW);
53
54	    // 初始化 LittleFS
55	    if (!LittleFS.begin(true)) {
56	        Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));
57	        return false;
58	    }
59	    Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));
60
61	    // 加载配置
62	    if (!_loadConfig()) {
63	        Serial.println(F("[WiFi] 使用默认配置"));
64	        // 设置默认值
65	        strncpy(_config.mqtt_server, MQTT_DEFAULT_SERVER,   sizeof(_config.mqtt_server) - 1);
66	        strncpy(_config.mqtt_port,   MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
67	        strncpy(_config.mqtt_user,   MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
68	        strncpy(_config.mqtt_pass,   MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
69	        strncpy(_config.device_id,   MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
70	        strncpy(_config.env_note,    MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);
71	    }
72
73	    // 配置 WiFiManager
74	    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
75	    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
76	    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
77	    _wm.setTitle("🏥 五劳无感康养监测 - 设备配网");  // 配网页面的中文标题
78	    _wm.setCustomHeadElement(
79	        "<style>"
80	        "body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"
81	        ".msg .info { font-size: 14px; }"
82	        "</style>"
83	    );
84	    WiFi.setAutoReconnect(true);  // ESP32 自动重连
85
86	    // 注册保存回调 (必须使用静态函数, 不能使用 lambda)
87	    _wm.setSaveConfigCallback(_saveParamsCallback);
88
89	    // 创建自定义参数
90	    _param_mqtt_server = new WiFiManagerParameter(
91	        "mqtt_server", "MQTT 服务器地址",
92	        _config.mqtt_server, 40);
93	    _param_mqtt_port = new WiFiManagerParameter(
94	        "mqtt_port", "MQTT 端口",
95	        _config.mqtt_port, 6);
96	    _param_mqtt_user = new WiFiManagerParameter(
97	        "mqtt_user", "MQTT 用户名",
98	        _config.mqtt_user, 20);
99	    _param_mqtt_pass = new WiFiManagerParameter(
100	        "mqtt_pass", "MQTT 密码",
101	        _config.mqtt_pass, 20);
102	    _param_device_id = new WiFiManagerParameter(
103	        "device_id", "设备名称",
104	        _config.device_id, 20);
105	    _param_env_note = new WiFiManagerParameter(
106	        "env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",
107	        _config.env_note, 20);
108
109	    // 注册自定义参数
110	    _wm.addParameter(_param_mqtt_server);
111	    _wm.addParameter(_param_mqtt_port);
112	    _wm.addParameter(_param_mqtt_user);
113	    _wm.addParameter(_param_mqtt_pass);
114	    _wm.addParameter(_param_device_id);
115	    _wm.addParameter(_param_env_note);
116
117	    // 设置 AP 模式回调 (静态函数)
118	    _wm.setAPCallback(_apCallback);
119
120	    // 尝试连接 WiFi
121	    _connected = _connectWiFi();
122
123	    if (!_connected) {
124	        // 如果自动连接失败, 启动配网页面
125	        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
126	        _emitProvisionState("ready");  // 协议 §4.4: 进入配网模式
127	        _setLEDMode(1);  // 快闪 = 配网模式
128	        // 配网期间 loop() 阻塞，LED 靠另一个小任务单独闪 (见 _startLEDTask)
129	        _startLEDTask(300);  // 300ms 快闪
130	        _connected = _connectWiFi();  // 内部会启动配网页面 (阻塞!)
131	        _stopLEDTask();
132	    }
133
134	    if (_connected) {
135	        _setLEDMode(2);  // 常亮 = 已连接
136	        _emitProvisionState("success");  // 协议 §4.4: 配网成功
137	    }
138
139	    _lastCheckTime = millis();
140	    return _connected;
141	}
142
143	// ============================================================
144	// 主循环
145	// ============================================================
146
147	bool WiFiMgmt::loop() {
148	    // 检测按钮长按
149	    if (_checkButtonLongPress()) {
150	        Serial.println(F("[WiFi] ⚠  检测到长按, 清除配置并重启..."));
151	        resetSettings();
152	        return false;
153	    }
154
155	    // 检查 WiFi 状态
156	    if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
157	        _lastCheckTime = millis();
158
159	        if (WiFi.status() != WL_CONNECTED) {
160	            _connected = false;
161	            _setLEDMode(0);  // 慢闪 = 未连接
162	            Serial.println(F("[WiFi] ⚠  WiFi 断开, 尝试重连..."));
163	            _connectWiFi();
164	        } else {
165	            _connected = true;
166	            _setLEDMode(2);  // 常亮
167	        }
168	    }
169
170	    // LED 闪烁处理
171	    if (!_connected) {
172	        uint32_t interval = (_wm.getConfigPortalActive()) ? 300 : 1000;
173	        if (millis() - _ledLastToggle > interval) {
174	            _ledLastToggle = millis();
175	            _ledState = !_ledState;
176	            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
177	        }
178	    }
179
180	    return _connected;
181	}
182
183	// ============================================================
184	// WiFi 连接
185	// ============================================================
186
187	bool WiFiMgmt::_connectWiFi() {
188	    // 检查是否有已保存的配置
189	    // autoConnect 会先尝试连接, 失败后启动 AP 配网页面
190	    _connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);
191
192	    if (_connected) {
193	        Serial.println(F("[WiFi] ✅ 已连接!"));
194	        Serial.printf("  WiFi名称: %s\n", WiFi.SSID().c_str());
195	        Serial.printf("  IP地址:   %s\n", WiFi.localIP().toString().c_str());
196	        Serial.printf("  MAC地址:  %s\n", WiFi.macAddress().c_str());
197
198	        // 重新加载配置 (确保获取最新值)
199	        // autoConnect 过程中如果有保存, 回调已写入 LittleFS
200	        _loadConfig();
201
202	        // 同步自定义参数 (如果配置是从 LittleFS 加载的)
203	        _param_mqtt_server->setValue(_config.mqtt_server, sizeof(_config.mqtt_server));
204	        _param_mqtt_port->setValue(_config.mqtt_port, sizeof(_config.mqtt_port));
205	        _param_mqtt_user->setValue(_config.mqtt_user, sizeof(_config.mqtt_user));
206	        _param_mqtt_pass->setValue(_config.mqtt_pass, sizeof(_config.mqtt_pass));
207	        _param_device_id->setValue(_config.device_id, sizeof(_config.device_id));
208	        _param_env_note->setValue(_config.env_note, sizeof(_config.env_note));
209
210	        Serial.printf("  设备名: %s\n", _config.device_id);
211	        Serial.printf("  环境:   %s\n", _config.env_note);
212	        Serial.printf("  MQTT服务器: %s:%s\n", _config.mqtt_server, _config.mqtt_port);
213
214	        _emitProvisionState("success");  // 协议 §4.4: 配网成功
215	    } else {
216	        _emitProvisionState("wifi_failed");  // 协议 §4.4: WiFi 连接失败
217	    }
218
219	    return _connected;
220	}
221
222	// ============================================================
223	// 配网状态回调注册
224	// ============================================================
225
226	void WiFiMgmt::onProvisionState(ProvisionStateCallback cb) {
227	    _provisionStateCb = cb;
228	}
229
230	void WiFiMgmt::_emitProvisionState(const char* state) {
231	    Serial.printf("[WiFi] 📡 配网状态: %s\n", state);
232	    if (_provisionStateCb) {
233	        _provisionStateCb(state);
234	    }
235	}
236
237	// ============================================================
238	// 配网页面
239	// ============================================================
240
241	void WiFiMgmt::startConfigPortal() {
242	    Serial.println(F("[WiFi] 🔧 手动启动配网页面..."));
243	    _emitProvisionState("ready");  // 协议 §4.4: 进入配网模式
244	    _setLEDMode(1);
245	    _startLEDTask(300);  // 快闪
246	    _wm.startConfigPortal(WIFI_AP_SSID, WIFI_AP_PASSWORD);
247	    _stopLEDTask();
248	    _connected = (WiFi.status() == WL_CONNECTED);
249	    if (_connected) {
250	        _setLEDMode(2);
251	        _emitProvisionState("success");  // 协议 §4.4: 配网成功
252	    }
253	}
254
255	// ============================================================
256	// 重置设置
257	// ============================================================
258
259	void WiFiMgmt::resetSettings() {
260	    Serial.println(F("[WiFi] 清除所有配置..."));
261
262	    // 删除 LittleFS 中的配置文件
263	    if (LittleFS.exists(CONFIG_FILE_WIFI)) LittleFS.remove(CONFIG_FILE_WIFI);
264	    if (LittleFS.exists(CONFIG_FILE_MQTT)) LittleFS.remove(CONFIG_FILE_MQTT);
265	    if (LittleFS.exists(CONFIG_FILE_DEVICE)) LittleFS.remove(CONFIG_FILE_DEVICE);
266
267	    // 清除 WiFiManager 的配置
268	    _wm.resetSettings();
269
270	    // LED 快闪提示
271	    for (int i = 0; i < 10; i++) {
272	        digitalWrite(LED_WIFI_PIN, !digitalRead(LED_WIFI_PIN));
273	        delay(100);
274	    }
275
276	    Serial.println(F("[WiFi] 🔄 正在重启..."));
277	    delay(500);
278	    ESP.restart();
279	}
280
281	// ============================================================
282	// 按钮长按检测
283	// ============================================================
284
285	bool WiFiMgmt::_checkButtonLongPress() {
286	    bool pressed = (digitalRead(CONFIG_BUTTON_PIN) == LOW);
287
288	    if (pressed && !_buttonPressed) {
289	        // 按下
290	        _buttonPressed = true;
291	        _buttonPressTime = millis();
292	    } else if (!pressed && _buttonPressed) {
293	        // 松开
294	        _buttonPressed = false;
295	        if (millis() - _buttonPressTime >= BUTTON_LONG_PRESS_MS) {
296	            return true;  // 长按确认
297	        }
298	    }
299
300	    return false;
301	}
302
303	// ============================================================
304	// LittleFS 配置持久化
305	// ============================================================
306
307	bool WiFiMgmt::_loadConfig() {
308	    if (!LittleFS.exists(CONFIG_FILE_DEVICE)) {
309	        return false;
310	    }
311
312	    File file = LittleFS.open(CONFIG_FILE_DEVICE, "r");
313	    if (!file) return false;
314
315	    JsonDocument doc;
316	    DeserializationError err = deserializeJson(doc, file);
317	    file.close();
318
319	    if (err) {
320	        Serial.printf("[WiFi] JSON 解析失败: %s\n", err.c_str());
321	        return false;
322	    }
323
324	    strncpy(_config.mqtt_server, doc["mqtt_server"] | MQTT_DEFAULT_SERVER, sizeof(_config.mqtt_server) - 1);
325	    strncpy(_config.mqtt_port,   doc["mqtt_port"]   | MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
326	    strncpy(_config.mqtt_user,   doc["mqtt_user"]   | MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
327	    strncpy(_config.mqtt_pass,   doc["mqtt_pass"]   | MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
328	    strncpy(_config.device_id,   doc["device_id"]   | MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
329	    strncpy(_config.env_note,    doc["env_note"]    | MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);
330
331	    Serial.println(F("[WiFi] ✅ 设备配置已加载"));
332	    Serial.printf("  设备名: %s, 环境: %s\n", _config.device_id, _config.env_note);
333	    return true;
334	}
335
336	bool WiFiMgmt::_saveConfig() {
337	    JsonDocument doc;
338	    doc["mqtt_server"] = _config.mqtt_server;
339	    doc["mqtt_port"]   = _config.mqtt_port;
340	    doc["mqtt_user"]   = _config.mqtt_user;
341	    doc["mqtt_pass"]   = _config.mqtt_pass;
342	    doc["device_id"]   = _config.device_id;
343	    doc["env_note"]    = _config.env_note;
344
345	    File file = LittleFS.open(CONFIG_FILE_DEVICE, "w");
346	    if (!file) {
347	        Serial.println(F("[WiFi] ❌ 无法创建配置文件"));
348	        return false;
349	    }
350
351	    if (serializeJson(doc, file) == 0) {
352	        Serial.println(F("[WiFi] ❌ 配置写入失败"));
353	        file.close();
354	        return false;
355	    }
356
357	    file.close();
358	    Serial.println(F("[WiFi] ✅ 设备配置已保存"));
359	    return true;
360	}
361
362	// ============================================================
363	// WiFiManager 保存参数回调
364	// ============================================================
365
366	// ============================================================
367	// 静态回调函数 (WiFiManager 要求 C 函数指针)
368	// ============================================================
369
370	void WiFiMgmt::_apCallback(WiFiManager* wm) {
371	    Serial.println(F("[WiFi] 🔧 进入配网模式"));
372	    Serial.printf("  AP WiFi名称: %s\n", WIFI_AP_SSID);
373	    Serial.printf("  AP 密码: %s\n", WIFI_AP_PASSWORD);
374	    Serial.println(F("  手机连接此 WiFi 即可打开配网页面 (自动弹出)"));
375	}
376
377	void WiFiMgmt::_saveParamsCallback() {
378	    if (!_instance) return;
379
380	    Serial.println(F("[WiFi] 💾 参数已保存"));
381
382	    // 从 WiFiManager 参数中读取值
383	    strncpy(_instance->_config.mqtt_server, _instance->_param_mqtt_server->getValue(),
384	            sizeof(_instance->_config.mqtt_server) - 1);
385	    strncpy(_instance->_config.mqtt_port,   _instance->_param_mqtt_port->getValue(),
386	            sizeof(_instance->_config.mqtt_port) - 1);
387	    strncpy(_instance->_config.mqtt_user,   _instance->_param_mqtt_user->getValue(),
388	            sizeof(_instance->_config.mqtt_user) - 1);
389	    strncpy(_instance->_config.mqtt_pass,   _instance->_param_mqtt_pass->getValue(),
390	            sizeof(_instance->_config.mqtt_pass) - 1);
391	    strncpy(_instance->_config.device_id,   _instance->_param_device_id->getValue(),
392	            sizeof(_instance->_config.device_id) - 1);
393	    strncpy(_instance->_config.env_note,    _instance->_param_env_note->getValue(),
394	            sizeof(_instance->_config.env_note) - 1);
395
396	    _instance->_saveConfig();
397	    _instance->_freshProvision = true;  // 标记刚完成配网
398
399	    Serial.println(F("[WiFi] 📋 新配置:"));
400	    Serial.printf("  MQTT: %s:%s\n",
401	                  _instance->_config.mqtt_server,
402	                  _instance->_config.mqtt_port);
403	    Serial.printf("  设备: %s\n", _instance->_config.device_id);
404	    Serial.printf("  环境: %s\n", _instance->_config.env_note);
405	}
406
407	// ============================================================
408	// Getter 方法
409	// ============================================================
410
411	bool WiFiMgmt::isConnected() const {
412	    return _connected && (WiFi.status() == WL_CONNECTED);
413	}
414
415	DeviceConfig WiFiMgmt::getConfig() const {
416	    return _config;
417	}
418
419	const char* WiFiMgmt::getDeviceId() const {
420	    return _config.device_id;
421	}
422
423	const char* WiFiMgmt::getEnvNote() const {
424	    return _config.env_note;
425	}
426
427	// ============================================================
428	// LED 模式控制
429	// ============================================================
430
431	void WiFiMgmt::_setLEDMode(uint8_t mode) {
432	    switch (mode) {
433	    case 0:
434	    case 1:
435	        break;
436	    case 2:
437	        digitalWrite(LED_WIFI_PIN, HIGH);
438	        _ledState = true;
439	        break;
440	    }
441	}
442
443	// ============================================================
444	// LED 闪烁任务 (配网阻塞期间的独立线程)
445	// ============================================================
446
447	void WiFiMgmt::_startLEDTask(uint32_t interval) {
448	    _stopLEDTask();
449	    _ledTaskInterval = interval;
450	    _ledTaskRunning = true;
451	    xTaskCreatePinnedToCore(
452	        _ledBlinkTask, "LedBlink",
453	        2048, nullptr, 1, nullptr, 0
454	    );
455	}
456
457	void WiFiMgmt::_stopLEDTask() {
458	    _ledTaskRunning = false;
459	}
460
461	void WiFiMgmt::_ledBlinkTask(void* param) {
462	    while (_ledTaskRunning) {
463	        digitalWrite(LED_WIFI_PIN, HIGH);
464	        vTaskDelay(pdMS_TO_TICKS(_ledTaskInterval / 2));
465	        digitalWrite(LED_WIFI_PIN, LOW);
466	        vTaskDelay(pdMS_TO_TICKS(_ledTaskInterval / 2));
467	    }
468	    vTaskDelete(nullptr);
469	}
470

/**
 * @file wifi_manager.cpp
 * @brief WiFi 配网管理器实现
 */

#include "wifi_manager.h"

// 单例指针 + LED 任务
WiFiMgmt* WiFiMgmt::_instance = nullptr;
bool WiFiMgmt::_ledTaskRunning = false;
uint32_t WiFiMgmt::_ledTaskInterval = 300;

// ============================================================
// 构造函数 & 析构函数
// ============================================================

WiFiMgmt::WiFiMgmt()
    : _connected(false)
    , _lastCheckTime(0)
    , _buttonPressTime(0)
    , _buttonPressed(false)
    , _ledLastToggle(0)
    , _ledState(false)
    , _param_mqtt_server(nullptr)
    , _param_mqtt_port(nullptr)
    , _param_mqtt_user(nullptr)
    , _param_mqtt_pass(nullptr)
    , _param_device_id(nullptr)
    , _param_env_note(nullptr)
    , _provisionStateCb(nullptr)
{
    memset(&_config, 0, sizeof(_config));
    _instance = this;  // 设置单例
}

WiFiMgmt::~WiFiMgmt() {
    _instance = nullptr;
}

// ============================================================
// 初始化
// ============================================================

bool WiFiMgmt::begin() {
    Serial.println(F("\n========================================"));
    Serial.println(F("  WiFi 配网管理器 初始化"));
    Serial.println(F("========================================"));

    // 初始化引脚
    pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
    pinMode(LED_WIFI_PIN, OUTPUT);
    digitalWrite(LED_WIFI_PIN, LOW);

    // 初始化 LittleFS
    if (!LittleFS.begin(true)) {
        Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));
        return false;
    }
    Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));

    // 加载配置
    if (!_loadConfig()) {
        Serial.println(F("[WiFi] 使用默认配置"));
        // 设置默认值
        strncpy(_config.mqtt_server, MQTT_DEFAULT_SERVER,   sizeof(_config.mqtt_server) - 1);
        strncpy(_config.mqtt_port,   MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
        strncpy(_config.mqtt_user,   MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
        strncpy(_config.mqtt_pass,   MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
        strncpy(_config.device_id,   MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
        strncpy(_config.env_note,    MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);
    }

    // 配置 WiFiManager
    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("🏥 五劳无感康养监测 - 设备配网");  // 配网页面的中文标题
    _wm.setCustomHeadElement(
        "<style>"
        "body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"
        ".msg .info { font-size: 14px; }"
        "</style>"
    );
    WiFi.setAutoReconnect(true);  // ESP32 自动重连

    // 注册保存回调 (必须使用静态函数, 不能使用 lambda)
    _wm.setSaveConfigCallback(_saveParamsCallback);

    // 创建自定义参数
    _param_mqtt_server = new WiFiManagerParameter(
        "mqtt_server", "MQTT 服务器地址",
        _config.mqtt_server, 40);
    _param_mqtt_port = new WiFiManagerParameter(
        "mqtt_port", "MQTT 端口",
        _config.mqtt_port, 6);
    _param_mqtt_user = new WiFiManagerParameter(
        "mqtt_user", "MQTT 用户名",
        _config.mqtt_user, 20);
    _param_mqtt_pass = new WiFiManagerParameter(
        "mqtt_pass", "MQTT 密码",
        _config.mqtt_pass, 20);
    _param_device_id = new WiFiManagerParameter(
        "device_id", "设备名称",
        _config.device_id, 20);
    _param_env_note = new WiFiManagerParameter(
        "env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",
        _config.env_note, 20);

    // 注册自定义参数
    _wm.addParameter(_param_mqtt_server);
    _wm.addParameter(_param_mqtt_port);
    _wm.addParameter(_param_mqtt_user);
    _wm.addParameter(_param_mqtt_pass);
    _wm.addParameter(_param_device_id);
    _wm.addParameter(_param_env_note);

    // 设置 AP 模式回调 (静态函数)
    _wm.setAPCallback(_apCallback);

    // 尝试连接 WiFi
    _connected = _connectWiFi();

    if (!_connected) {
        // 如果自动连接失败, 启动配网页面
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
        _emitProvisionState("ready");  // 协议 §4.4: 进入配网模式
        _setLEDMode(1);  // 快闪 = 配网模式
        // 配网期间 loop() 阻塞，LED 靠另一个小任务单独闪 (见 _startLEDTask)
        _startLEDTask(300);  // 300ms 快闪
        _connected = _connectWiFi();  // 内部会启动配网页面 (阻塞!)
        _stopLEDTask();
    }

    if (_connected) {
        _setLEDMode(2);  // 常亮 = 已连接
        _emitProvisionState("success");  // 协议 §4.4: 配网成功
    }

    _lastCheckTime = millis();
    return _connected;
}

// ============================================================
// 主循环
// ============================================================

bool WiFiMgmt::loop() {
    // 检测按钮长按
    if (_checkButtonLongPress()) {
        Serial.println(F("[WiFi] ⚠  检测到长按, 清除配置并重启..."));
        resetSettings();
        return false;
    }

    // 检查 WiFi 状态
    if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
        _lastCheckTime = millis();

        if (WiFi.status() != WL_CONNECTED) {
            _connected = false;
            _setLEDMode(0);  // 慢闪 = 未连接
            Serial.println(F("[WiFi] ⚠  WiFi 断开, 尝试重连..."));
            _connectWiFi();
        } else {
            _connected = true;
            _setLEDMode(2);  // 常亮
        }
    }

    // LED 闪烁处理
    if (!_connected) {
        uint32_t interval = (_wm.getConfigPortalActive()) ? 300 : 1000;
        if (millis() - _ledLastToggle > interval) {
            _ledLastToggle = millis();
            _ledState = !_ledState;
            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
        }
    }

    return _connected;
}

// ============================================================
// WiFi 连接
// ============================================================

bool WiFiMgmt::_connectWiFi() {
    // 检查是否有已保存的配置
    // autoConnect 会先尝试连接, 失败后启动 AP 配网页面
    _connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);

    if (_connected) {
        Serial.println(F("[WiFi] ✅ 已连接!"));
        Serial.printf("  WiFi名称: %s\n", WiFi.SSID().c_str());
        Serial.printf("  IP地址:   %s\n", WiFi.localIP().toString().c_str());
        Serial.printf("  MAC地址:  %s\n", WiFi.macAddress().c_str());

        // 重新加载配置 (确保获取最新值)
        // autoConnect 过程中如果有保存, 回调已写入 LittleFS
        _loadConfig();

        // 同步自定义参数 (如果配置是从 LittleFS 加载的)
        _param_mqtt_server->setValue(_config.mqtt_server, sizeof(_config.mqtt_server));
        _param_mqtt_port->setValue(_config.mqtt_port, sizeof(_config.mqtt_port));
        _param_mqtt_user->setValue(_config.mqtt_user, sizeof(_config.mqtt_user));
        _param_mqtt_pass->setValue(_config.mqtt_pass, sizeof(_config.mqtt_pass));
        _param_device_id->setValue(_config.device_id, sizeof(_config.device_id));
        _param_env_note->setValue(_config.env_note, sizeof(_config.env_note));

        Serial.printf("  设备名: %s\n", _config.device_id);
        Serial.printf("  环境:   %s\n", _config.env_note);
        Serial.printf("  MQTT服务器: %s:%s\n", _config.mqtt_server, _config.mqtt_port);

        _emitProvisionState("success");  // 协议 §4.4: 配网成功
    } else {
        _emitProvisionState("wifi_failed");  // 协议 §4.4: WiFi 连接失败
    }

    return _connected;
}

// ============================================================
// 配网状态回调注册
// ============================================================

void WiFiMgmt::onProvisionState(ProvisionStateCallback cb) {
    _provisionStateCb = cb;
}

void WiFiMgmt::_emitProvisionState(const char* state) {
    Serial.printf("[WiFi] 📡 配网状态: %s\n", state);
    if (_provisionStateCb) {
        _provisionStateCb(state);
    }
}

// ============================================================
// 配网页面
// ============================================================

void WiFiMgmt::startConfigPortal() {
    Serial.println(F("[WiFi] 🔧 手动启动配网页面..."));
    _emitProvisionState("ready");  // 协议 §4.4: 进入配网模式
    _setLEDMode(1);
    _startLEDTask(300);  // 快闪
    _wm.startConfigPortal(WIFI_AP_SSID, WIFI_AP_PASSWORD);
    _stopLEDTask();
    _connected = (WiFi.status() == WL_CONNECTED);
    if (_connected) {
        _setLEDMode(2);
        _emitProvisionState("success");  // 协议 §4.4: 配网成功
    }
}

// ============================================================
// 重置设置
// ============================================================

void WiFiMgmt::resetSettings() {
    Serial.println(F("[WiFi] 清除所有配置..."));

    // 删除 LittleFS 中的配置文件
    if (LittleFS.exists(CONFIG_FILE_WIFI)) LittleFS.remove(CONFIG_FILE_WIFI);
    if (LittleFS.exists(CONFIG_FILE_MQTT)) LittleFS.remove(CONFIG_FILE_MQTT);
    if (LittleFS.exists(CONFIG_FILE_DEVICE)) LittleFS.remove(CONFIG_FILE_DEVICE);

    // 清除 WiFiManager 的配置
    _wm.resetSettings();

    // LED 快闪提示
    for (int i = 0; i < 10; i++) {
        digitalWrite(LED_WIFI_PIN, !digitalRead(LED_WIFI_PIN));
        delay(100);
    }

    Serial.println(F("[WiFi] 🔄 正在重启..."));
    delay(500);
    ESP.restart();
}

// ============================================================
// 按钮长按检测
// ============================================================

bool WiFiMgmt::_checkButtonLongPress() {
    bool pressed = (digitalRead(CONFIG_BUTTON_PIN) == LOW);

    if (pressed && !_buttonPressed) {
        // 按下
        _buttonPressed = true;
        _buttonPressTime = millis();
    } else if (!pressed && _buttonPressed) {
        // 松开
        _buttonPressed = false;
        if (millis() - _buttonPressTime >= BUTTON_LONG_PRESS_MS) {
            return true;  // 长按确认
        }
    }

    return false;
}

// ============================================================
// LittleFS 配置持久化
// ============================================================

bool WiFiMgmt::_loadConfig() {
    if (!LittleFS.exists(CONFIG_FILE_DEVICE)) {
        return false;
    }

    File file = LittleFS.open(CONFIG_FILE_DEVICE, "r");
    if (!file) return false;

    JsonDocument doc;
    DeserializationError err = deserializeJson(doc, file);
    file.close();

    if (err) {
        Serial.printf("[WiFi] JSON 解析失败: %s\n", err.c_str());
        return false;
    }

    strncpy(_config.mqtt_server, doc["mqtt_server"] | MQTT_DEFAULT_SERVER, sizeof(_config.mqtt_server) - 1);
    strncpy(_config.mqtt_port,   doc["mqtt_port"]   | MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
    strncpy(_config.mqtt_user,   doc["mqtt_user"]   | MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
    strncpy(_config.mqtt_pass,   doc["mqtt_pass"]   | MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
    strncpy(_config.device_id,   doc["device_id"]   | MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
    strncpy(_config.env_note,    doc["env_note"]    | MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);

    Serial.println(F("[WiFi] ✅ 设备配置已加载"));
    Serial.printf("  设备名: %s, 环境: %s\n", _config.device_id, _config.env_note);
    return true;
}

bool WiFiMgmt::_saveConfig() {
    JsonDocument doc;
    doc["mqtt_server"] = _config.mqtt_server;
    doc["mqtt_port"]   = _config.mqtt_port;
    doc["mqtt_user"]   = _config.mqtt_user;
    doc["mqtt_pass"]   = _config.mqtt_pass;
    doc["device_id"]   = _config.device_id;
    doc["env_note"]    = _config.env_note;

    File file = LittleFS.open(CONFIG_FILE_DEVICE, "w");
    if (!file) {
        Serial.println(F("[WiFi] ❌ 无法创建配置文件"));
        return false;
    }

    if (serializeJson(doc, file) == 0) {
        Serial.println(F("[WiFi] ❌ 配置写入失败"));
        file.close();
        return false;
    }

    file.close();
    Serial.println(F("[WiFi] ✅ 设备配置已保存"));
    return true;
}

// ============================================================
// WiFiManager 保存参数回调
// ============================================================

// ============================================================
// 静态回调函数 (WiFiManager 要求 C 函数指针)
// ============================================================

void WiFiMgmt::_apCallback(WiFiManager* wm) {
    Serial.println(F("[WiFi] 🔧 进入配网模式"));
    Serial.printf("  AP WiFi名称: %s\n", WIFI_AP_SSID);
    Serial.printf("  AP 密码: %s\n", WIFI_AP_PASSWORD);
    Serial.println(F("  手机连接此 WiFi 即可打开配网页面 (自动弹出)"));
}

void WiFiMgmt::_saveParamsCallback() {
    if (!_instance) return;

    Serial.println(F("[WiFi] 💾 参数已保存"));

    // 从 WiFiManager 参数中读取值
    strncpy(_instance->_config.mqtt_server, _instance->_param_mqtt_server->getValue(),
            sizeof(_instance->_config.mqtt_server) - 1);
    strncpy(_instance->_config.mqtt_port,   _instance->_param_mqtt_port->getValue(),
            sizeof(_instance->_config.mqtt_port) - 1);
    strncpy(_instance->_config.mqtt_user,   _instance->_param_mqtt_user->getValue(),
            sizeof(_instance->_config.mqtt_user) - 1);
    strncpy(_instance->_config.mqtt_pass,   _instance->_param_mqtt_pass->getValue(),
            sizeof(_instance->_config.mqtt_pass) - 1);
    strncpy(_instance->_config.device_id,   _instance->_param_device_id->getValue(),
            sizeof(_instance->_config.device_id) - 1);
    strncpy(_instance->_config.env_note,    _instance->_param_env_note->getValue(),
            sizeof(_instance->_config.env_note) - 1);

    _instance->_saveConfig();
    _instance->_freshProvision = true;  // 标记刚完成配网

    Serial.println(F("[WiFi] 📋 新配置:"));
    Serial.printf("  MQTT: %s:%s\n",
                  _instance->_config.mqtt_server,
                  _instance->_config.mqtt_port);
    Serial.printf("  设备: %s\n", _instance->_config.device_id);
    Serial.printf("  环境: %s\n", _instance->_config.env_note);
}

// ============================================================
// Getter 方法
// ============================================================

bool WiFiMgmt::isConnected() const {
    return _connected && (WiFi.status() == WL_CONNECTED);
}

DeviceConfig WiFiMgmt::getConfig() const {
    return _config;
}

const char* WiFiMgmt::getDeviceId() const {
    return _config.device_id;
}

const char* WiFiMgmt::getEnvNote() const {
    return _config.env_note;
}

// ============================================================
// LED 模式控制
// ============================================================

void WiFiMgmt::_setLEDMode(uint8_t mode) {
    switch (mode) {
    case 0:
    case 1:
        break;
    case 2:
        digitalWrite(LED_WIFI_PIN, HIGH);
        _ledState = true;
        break;
    }
}

// ============================================================
// LED 闪烁任务 (配网阻塞期间的独立线程)
// ============================================================

void WiFiMgmt::_startLEDTask(uint32_t interval) {
    _stopLEDTask();
    _ledTaskInterval = interval;
    _ledTaskRunning = true;
    xTaskCreatePinnedToCore(
        _ledBlinkTask, "LedBlink",
        2048, nullptr, 1, nullptr, 0
    );
}

void WiFiMgmt::_stopLEDTask() {
    _ledTaskRunning = false;
}

void WiFiMgmt::_ledBlinkTask(void* param) {
    while (_ledTaskRunning) {
        digitalWrite(LED_WIFI_PIN, HIGH);
        vTaskDelay(pdMS_TO_TICKS(_ledTaskInterval / 2));
        digitalWrite(LED_WIFI_PIN, LOW);
        vTaskDelay(pdMS_TO_TICKS(_ledTaskInterval / 2));
    }
    vTaskDelete(nullptr);
}


1	/**
2	 * @file wifi_manager.h
3	 * @brief WiFi 配网管理器
4	 *
5	 * 功能:
6	 *  - 自动连接已保存的 WiFi
7	 *  - 配网失败/首次启动 → AP 模式 + 强制门户页面
8	 *  - 自定义参数: MQTT 服务器/端口/用户/密码、设备名称、环境备注
9	 *  - 长按按钮重置配置
10	 *  - LED 状态指示
11	 *  - LittleFS 持久化存储
12	 */
13
14	#ifndef WIFI_MANAGER_H
15	#define WIFI_MANAGER_H
16
17	#include <Arduino.h>
18	#include <WiFiManager.h>
19	#include <LittleFS.h>
20	#include <ArduinoJson.h>
21	#include "config.h"
22
23	// 设备配置结构体
24	struct DeviceConfig {
25	    char mqtt_server[40];
26	    char mqtt_port[6];
27	    char mqtt_user[20];
28	    char mqtt_pass[20];
29	    char device_id[20];
30	    char env_note[20];      // 环境备注 (如: "主卧", "客厅", "老人房")
31	};
32
33	// 配网状态回调 (state: ready / wifi_connecting / wifi_failed / success)
34	typedef void (*ProvisionStateCallback)(const char* state);
35
36	class WiFiMgmt {
37	public:
38	    WiFiMgmt();
39	    ~WiFiMgmt();
40
41	    // 单例指针 (用于静态回调)
42	    static WiFiMgmt* _instance;
43
44	    /**
45	     * @brief 注册配网状态回调
46	     * @param cb 回调函数，参数为 state 字符串
47	     * @note  触发时机: 进入配网页面/连接WiFi中/WiFi失败/配网成功
48	     *        用于实现协议 §4.4 配网状态通知
49	     */
50	    void onProvisionState(ProvisionStateCallback cb);
51
52	    /**
53	     * @brief 初始化 WiFi 管理器
54	     * @return true=成功
55	     */
56	    bool begin();
57
58	    /**
59	     * @brief 主循环, 处理配网流程和按钮检测
60	     * @return true=WiFi 已连接
61	     */
62	    bool loop();
63
64	    /**
65	     * @brief WiFi 是否已连接
66	     */
67	    bool isConnected() const;
68
69	    /**
70	     * @brief 获取设备配置
71	     */
72	    DeviceConfig getConfig() const;
73
74	    /**
75	     * @brief 获取设备 ID (用于 MQTT client_id)
76	     */
77	    const char* getDeviceId() const;
78
79	    /**
80	     * @brief 获取环境备注
81	     */
82	    const char* getEnvNote() const;
83
84	    /**
85	     * @brief 是否为新配网 (本次启动通过配网页面保存了配置)
86	     * @return true=刚完成配网, false=使用已有配置启动
87	     */
88	    bool isFreshProvision() const { return _freshProvision; }
89
90	    /** 清除新配网标志 (发送配网成功消息后调用) */
91	    void clearFreshProvision() { _freshProvision = false; }
92
93	    /**
94	     * @brief 强制进入配网模式
95	     */
96	    void startConfigPortal();
97
98	    /**
99	     * @brief 清除所有配置并重启
100	     */
101	    void resetSettings();
102
103	    /** LED 闪烁任务 (配网阻塞期间用) */
104	    static void _ledBlinkTask(void* param);
105	    void _startLEDTask(uint32_t interval);
106	    void _stopLEDTask();
107
108	    static bool _ledTaskRunning;
109	    static uint32_t _ledTaskInterval;
110
111	private:
112	    /**
113	     * @brief 尝试连接 WiFi
114	     */
115	    bool _connectWiFi();
116
117	    /**
118	     * @brief 设置 LED 状态
119	     * @param mode 0=慢闪(未连接) 1=快闪(配网) 2=常亮(已连接)
120	     */
121	    void _setLEDMode(uint8_t mode);
122
123	    /**
124	     * @brief 检测按钮长按
125	     */
126	    bool _checkButtonLongPress();
127
128	    /**
129	     * @brief 加载/保存配置到 LittleFS
130	     */
131	    bool _loadConfig();
132	    bool _saveConfig();
133
134	    /**
135	     * @brief 保存 WiFi 参数回调 (WiFiManager 的回调)
136	     */
137	    static void _saveParamsCallback();
138	    static void _apCallback(WiFiManager* wm);
139
140	    WiFiManager     _wm;
141	    DeviceConfig    _config;
142	    bool            _connected;
143	    uint32_t        _lastCheckTime;
144	    uint32_t        _buttonPressTime;
145	    bool            _buttonPressed;
146
147	    // LED 闪烁状态
148	    uint32_t        _ledLastToggle;
149	    bool            _ledState;
150
151	    // WiFiManager 自定义参数
152	    WiFiManagerParameter* _param_mqtt_server;
153	    WiFiManagerParameter* _param_mqtt_port;
154	    WiFiManagerParameter* _param_mqtt_user;
155	    WiFiManagerParameter* _param_mqtt_pass;
156	    WiFiManagerParameter* _param_device_id;
157	    WiFiManagerParameter* _param_env_note;
158
159	    // 配网状态回调
160	    ProvisionStateCallback _provisionStateCb;
161
162	    // 本次启动是否为新配网 (配网页面保存过参数)
163	    bool _freshProvision = false;
164
165	    /**
166	     * @brief 触发配网状态回调 (内部用)
167	     */
168	    void _emitProvisionState(const char* state);
169	};
170
171	#endif // WIFI_MANAGER_H
172

/**
 * @file wifi_manager.h
 * @brief WiFi 配网管理器
 *
 * 功能:
 *  - 自动连接已保存的 WiFi
 *  - 配网失败/首次启动 → AP 模式 + 强制门户页面
 *  - 自定义参数: MQTT 服务器/端口/用户/密码、设备名称、环境备注
 *  - 长按按钮重置配置
 *  - LED 状态指示
 *  - LittleFS 持久化存储
 */

#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H

#include <Arduino.h>
#include <WiFiManager.h>
#include <LittleFS.h>
#include <ArduinoJson.h>
#include "config.h"

// 设备配置结构体
struct DeviceConfig {
    char mqtt_server[40];
    char mqtt_port[6];
    char mqtt_user[20];
    char mqtt_pass[20];
    char device_id[20];
    char env_note[20];      // 环境备注 (如: "主卧", "客厅", "老人房")
};

// 配网状态回调 (state: ready / wifi_connecting / wifi_failed / success)
typedef void (*ProvisionStateCallback)(const char* state);

class WiFiMgmt {
public:
    WiFiMgmt();
    ~WiFiMgmt();

    // 单例指针 (用于静态回调)
    static WiFiMgmt* _instance;

    /**
     * @brief 注册配网状态回调
     * @param cb 回调函数，参数为 state 字符串
     * @note  触发时机: 进入配网页面/连接WiFi中/WiFi失败/配网成功
     *        用于实现协议 §4.4 配网状态通知
     */
    void onProvisionState(ProvisionStateCallback cb);

    /**
     * @brief 初始化 WiFi 管理器
     * @return true=成功
     */
    bool begin();

    /**
     * @brief 主循环, 处理配网流程和按钮检测
     * @return true=WiFi 已连接
     */
    bool loop();

    /**
     * @brief WiFi 是否已连接
     */
    bool isConnected() const;

    /**
     * @brief 获取设备配置
     */
    DeviceConfig getConfig() const;

    /**
     * @brief 获取设备 ID (用于 MQTT client_id)
     */
    const char* getDeviceId() const;

    /**
     * @brief 获取环境备注
     */
    const char* getEnvNote() const;

    /**
     * @brief 是否为新配网 (本次启动通过配网页面保存了配置)
     * @return true=刚完成配网, false=使用已有配置启动
     */
    bool isFreshProvision() const { return _freshProvision; }

    /** 清除新配网标志 (发送配网成功消息后调用) */
    void clearFreshProvision() { _freshProvision = false; }

    /**
     * @brief 强制进入配网模式
     */
    void startConfigPortal();

    /**
     * @brief 清除所有配置并重启
     */
    void resetSettings();

    /** LED 闪烁任务 (配网阻塞期间用) */
    static void _ledBlinkTask(void* param);
    void _startLEDTask(uint32_t interval);
    void _stopLEDTask();

    static bool _ledTaskRunning;
    static uint32_t _ledTaskInterval;

private:
    /**
     * @brief 尝试连接 WiFi
     */
    bool _connectWiFi();

    /**
     * @brief 设置 LED 状态
     * @param mode 0=慢闪(未连接) 1=快闪(配网) 2=常亮(已连接)
     */
    void _setLEDMode(uint8_t mode);

    /**
     * @brief 检测按钮长按
     */
    bool _checkButtonLongPress();

    /**
     * @brief 加载/保存配置到 LittleFS
     */
    bool _loadConfig();
    bool _saveConfig();

    /**
     * @brief 保存 WiFi 参数回调 (WiFiManager 的回调)
     */
    static void _saveParamsCallback();
    static void _apCallback(WiFiManager* wm);

    WiFiManager     _wm;
    DeviceConfig    _config;
    bool            _connected;
    uint32_t        _lastCheckTime;
    uint32_t        _buttonPressTime;
    bool            _buttonPressed;

    // LED 闪烁状态
    uint32_t        _ledLastToggle;
    bool            _ledState;

    // WiFiManager 自定义参数
    WiFiManagerParameter* _param_mqtt_server;
    WiFiManagerParameter* _param_mqtt_port;
    WiFiManagerParameter* _param_mqtt_user;
    WiFiManagerParameter* _param_mqtt_pass;
    WiFiManagerParameter* _param_device_id;
    WiFiManagerParameter* _param_env_note;

    // 配网状态回调
    ProvisionStateCallback _provisionStateCb;

    // 本次启动是否为新配网 (配网页面保存过参数)
    bool _freshProvision = false;

    /**
     * @brief 触发配网状态回调 (内部用)
     */
    void _emitProvisionState(const char* state);
};

#endif // WIFI_MANAGER_H


我现在需要把配网界面优化。把代码给软件组


Now I have a complete picture. Let me plan the optimizations for the provisioning interface. The user wants to improve it before handing over to the software team.

Current issues and improvement opportunities:

1. **Styling**: The current custom CSS is minimal. Can add a better-looking design.
2. **UX flow**:
   - The default WiFiManager saves WiFi, then shows params page separately. This is confusing - two separate steps.
   - No clear grouping/guide text
   - The "saved" page is a plain message
3. **Parameter organization**: All 6 params are shown flat. Should be grouped: WiFi settings, MQTT settings, Device info.
4. **Chinese text**: Could add more helpful descriptions
5. **Success page**: Could show a nicer success page with connection details

WiFiManager limitations to consider:
- We can inject CSS via `setCustomHeadElement()`
- We can inject HTML/JS via `setCustomMenuHTML()`
- The main form pages (`/wifi`, `/param`) are generated by the library from PROGMEM strings
- We CANNOT easily change the HTML structure without modifying the library's `wm_strings_zh.h`

What we CAN improve in the project code:

1. **Better CSS** via `setCustomHeadElement()` - modern, clean look
2. **Custom menu HTML** via `setCustomMenuHTML()` - add guide text, reorganize flow
3. **Better parameter labels** - clearer, more professional
4. **Better AP callback** - show more useful info on serial
5. **Success page customization** - possible via API

Let me also check what WiFiManager API methods are available for customization.

Key WiFiManager customization APIs:
- `setTitle(const char*)` - page title
- `setCustomHeadElement(const char*)` - inject into `<head>`
- `setCustomMenuHTML(const char*)` - inject HTML on root page
- `setConfigPortalBlocking(bool)` - non-blocking mode
- `setSaveConfigCallback(cb)` - save callback
- `setSaveParamsCallback(cb)` - params save callback
- `setAPCallback(cb)` - AP mode entry callback
- `setSaveConnectCallback(cb)` - called when save+connect clicked
- `setShowPassword(bool)` - show/hide password toggle
- `setCaptivePortalEnable(bool)` - enable captive portal
- `setHostname(const char*)` - set mDNS hostname

Let me design the optimization:

1. **Enhanced CSS** - Modern mobile-first design with:
   - Better color scheme (health/medical theme - calm blues)
   - Card-based layout
   - Better typography
   - Rounded corners, shadows
   - Better button styles
   - Status indicators

2. **Custom menu HTML** - Add a welcome/intro section on the root page explaining the setup process

3. **Better parameter labels** - More professional Chinese labels with descriptions

4. **Improved AP callback info** - Print QR-code-like clear instructions

5. **Better organization** - Group params into sections (not easily doable without modifying library, but we can use labels creatively)

Actually, I realize the WiFiManager's page structure is fairly rigid. The main improvements we can make are:
- CSS overhaul (biggest visual impact)
- Custom menu HTML on root page
- Better parameter labels
- Better callback messages

Let me write the optimized code. I'll focus on:
1. A complete CSS redesign
2. A custom menu/welcome section
3. Better parameter labels and organization
4. Improved user guidance

Let me write the code now.

Actually, `setCustomMenuHTML` might not work as expected. Let me check what it does... It adds content to the root menu page. But the root page structure is generated from `HTTP_ROOT_MAIN` and `HTTP_PORTAL_MENU` arrays.

Looking at the WiFiManager source, `setCustomMenuHTML` adds raw HTML after the menu items on the root page.

OK, let me proceed with the optimization. The main things to change in `wifi_manager.cpp`:

1. Replace the `setCustomHeadElement` with comprehensive CSS
2. Add `setCustomMenuHTML` for welcome text and guidance
3. Improve parameter labels
4. Update `_apCallback` with better instructions

Let me code this up. I want to keep it clean and professional for the software team.

For the CSS, I'll create a modern, medical-healthcare themed design:
- Primary color: calming blue (#2196F3 or similar)
- Card-style form fields
- Clean typography
- Good mobile experience

For the menu/guidance, I'll add Chinese instructions at the top of the root page.

Let me write the optimized `begin()` function.


现在我清楚了。来对配网界面做一次全面优化——重点是 CSS 重设计、参数分组引导、更好的用户体验。


TodoWrite

重写 setCustomHeadElement CSS 样式

添加 setCustomMenuHTML 引导说明

优化参数标签（分组、更专业的描述）

更新 AP 模式回调解说


Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable

重写 setCustomHeadElement CSS 样式

添加 setCustomMenuHTML 引导说明

优化参数标签（分组、更专业的描述）

更新 AP 模式回调解说


Now let me rewrite the `begin()` function's WiFiManager configuration section with comprehensive improvements. I'll replace lines 73-118 (the WiFiManager setup section) with an optimized version.


Edit

    // 配置 WiFiManager
    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("🏥 五劳无感康养监测 - 设备配网");  // 配网页面的中文标题
    _wm.setCustomHeadElement(
        "<style>"
        "body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"
        ".msg .info { font-size: 14px; }"
        "</style>"
    );
    WiFi.setAutoReconnect(true);  // ESP32 自动重连

    // 注册保存回调 (必须使用静态函数, 不能使用 lambda)
    _wm.setSaveConfigCallback(_saveParamsCallback);

    // 创建自定义参数
    _param_mqtt_server = new WiFiManagerParameter(
        "mqtt_server", "MQTT 服务器地址",
        _config.mqtt_server, 40);
    _param_mqtt_port = new WiFiManagerParameter(
        "mqtt_port", "MQTT 端口",
        _config.mqtt_port, 6);
    _param_mqtt_user = new WiFiManagerParameter(
        "mqtt_user", "MQTT 用户名",
        _config.mqtt_user, 20);
    _param_mqtt_pass = new WiFiManagerParameter(
        "mqtt_pass", "MQTT 密码",
        _config.mqtt_pass, 20);
    _param_device_id = new WiFiManagerParameter(
        "device_id", "设备名称",
        _config.device_id, 20);
    _param_env_note = new WiFiManagerParameter(
        "env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",
        _config.env_note, 20);

    // 注册自定义参数
    _wm.addParameter(_param_mqtt_server);
    _wm.addParameter(_param_mqtt_port);
    _wm.addParameter(_param_mqtt_user);
    _wm.addParameter(_param_mqtt_pass);
    _wm.addParameter(_param_device_id);
    _wm.addParameter(_param_env_note);

    // 设置 AP 模式回调 (静态函数)
    _wm.setAPCallback(_apCallback);

// ============================================================
    // 配置 WiFiManager (配网页面的外观和行为)
    // ============================================================

    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("健康监测 · 设备配网");

    // --- 自定义 CSS (现代医疗主题, 移动端优先) ---
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#2c7da0'>"
        ""
    );

    // 开启密码可见切换
    _wm.setShowPassword(true);

    // --- 首页引导文字 (显示在 WiFi 列表上方) ---
    _wm.setCustomMenuHTML(
        "
"
        "
"
        "📋 配网步骤
"
        "
"
        "1 选择 WiFi 并输入密码
"
        "2 填写 MQTT 服务器参数
"
        "3 设置设备名称和安装环境
"
        "4 点击保存，等待连接成功"
        "

"
    );

    WiFi.setAutoReconnect(true);

    // 注册保存回调
    _wm.setSaveConfigCallback(_saveParamsCallback);

    // ============================================================
    // 自定义参数 (分三组: WiFi / MQTT / 设备)
    // ============================================================

    // --- 设备信息 ---
    _param_device_id = new WiFiManagerParameter(
        "device_id", "🔖 设备编号 (网关唯一标识)",
        _config.device_id, 20);
    _param_env_note = new WiFiManagerParameter(
        "env_note", "📍 安装环境 (如: 主卧 / 客厅 / 老人房)",
        _config.env_note, 20);

    // --- MQTT 服务器 ---
    _param_mqtt_server = new WiFiManagerParameter(
        "mqtt_server", "🌐 MQTT 服务器地址",
        _config.mqtt_server, 40);
    _param_mqtt_port = new WiFiManagerParameter(
        "mqtt_port", "🔌 MQTT 端口号",
        _config.mqtt_port, 6);
    _param_mqtt_user = new WiFiManagerParameter(
        "mqtt_user", "👤 MQTT 用户名",
        _config.mqtt_user, 20);
    _param_mqtt_pass = new WiFiManagerParameter(
        "mqtt_pass", "🔑 MQTT 密码",
        _config.mqtt_pass, 20);

    // 参数按顺序注册: 设备在前, MQTT 在后
    _wm.addParameter(_param_device_id);
    _wm.addParameter(_param_env_note);
    _wm.addParameter(_param_mqtt_server);
    _wm.addParameter(_param_mqtt_port);
    _wm.addParameter(_param_mqtt_user);
    _wm.addParameter(_param_mqtt_pass);

    // 设置 AP 模式回调
    _wm.setAPCallback(_apCallback);


The file d:\PlatformIO1\Projects\Healthv2.0\src\wifi_manager.cpp has been updated successfully. (file state is current in your context — no need to Read it back)

    // 配置 WiFiManager
    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("🏥 五劳无感康养监测 - 设备配网");  // 配网页面的中文标题
    _wm.setCustomHeadElement(
        "<style>"
        "body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"
        ".msg .info { font-size: 14px; }"
        "</style>"
    );
    WiFi.setAutoReconnect(true);  // ESP32 自动重连

    // 注册保存回调 (必须使用静态函数, 不能使用 lambda)
    _wm.setSaveConfigCallback(_saveParamsCallback);

    // 创建自定义参数
    _param_mqtt_server = new WiFiManagerParameter(
        "mqtt_server", "MQTT 服务器地址",
        _config.mqtt_server, 40);
    _param_mqtt_port = new WiFiManagerParameter(
        "mqtt_port", "MQTT 端口",
        _config.mqtt_port, 6);
    _param_mqtt_user = new WiFiManagerParameter(
        "mqtt_user", "MQTT 用户名",
        _config.mqtt_user, 20);
    _param_mqtt_pass = new WiFiManagerParameter(
        "mqtt_pass", "MQTT 密码",
        _config.mqtt_pass, 20);
    _param_device_id = new WiFiManagerParameter(
        "device_id", "设备名称",
        _config.device_id, 20);
    _param_env_note = new WiFiManagerParameter(
        "env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",
        _config.env_note, 20);

    // 注册自定义参数
    _wm.addParameter(_param_mqtt_server);
    _wm.addParameter(_param_mqtt_port);
    _wm.addParameter(_param_mqtt_user);
    _wm.addParameter(_param_mqtt_pass);
    _wm.addParameter(_param_device_id);
    _wm.addParameter(_param_env_note);

    // 设置 AP 模式回调 (静态函数)
    _wm.setAPCallback(_apCallback);

// ============================================================
    // 配置 WiFiManager (配网页面的外观和行为)
    // ============================================================

    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("健康监测 · 设备配网");

    // --- 自定义 CSS (现代医疗主题, 移动端优先) ---
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#2c7da0'>"
        ""
    );

    // 开启密码可见切换
    _wm.setShowPassword(true);

    // --- 首页引导文字 (显示在 WiFi 列表上方) ---
    _wm.setCustomMenuHTML(
        "
"
        "
"
        "📋 配网步骤
"
        "
"
        "1 选择 WiFi 并输入密码
"
        "2 填写 MQTT 服务器参数
"
        "3 设置设备名称和安装环境
"
        "4 点击保存，等待连接成功"
        "

"
    );

    WiFi.setAutoReconnect(true);

    // 注册保存回调
    _wm.setSaveConfigCallback(_saveParamsCallback);

    // ============================================================
    // 自定义参数 (分三组: WiFi / MQTT / 设备)
    // ============================================================

    // --- 设备信息 ---
    _param_device_id = new WiFiManagerParameter(
        "device_id", "🔖 设备编号 (网关唯一标识)",
        _config.device_id, 20);
    _param_env_note = new WiFiManagerParameter(
        "env_note", "📍 安装环境 (如: 主卧 / 客厅 / 老人房)",
        _config.env_note, 20);

    // --- MQTT 服务器 ---
    _param_mqtt_server = new WiFiManagerParameter(
        "mqtt_server", "🌐 MQTT 服务器地址",
        _config.mqtt_server, 40);
    _param_mqtt_port = new WiFiManagerParameter(
        "mqtt_port", "🔌 MQTT 端口号",
        _config.mqtt_port, 6);
    _param_mqtt_user = new WiFiManagerParameter(
        "mqtt_user", "👤 MQTT 用户名",
        _config.mqtt_user, 20);
    _param_mqtt_pass = new WiFiManagerParameter(
        "mqtt_pass", "🔑 MQTT 密码",
        _config.mqtt_pass, 20);

    // 参数按顺序注册: 设备在前, MQTT 在后
    _wm.addParameter(_param_device_id);
    _wm.addParameter(_param_env_note);
    _wm.addParameter(_param_mqtt_server);
    _wm.addParameter(_param_mqtt_port);
    _wm.addParameter(_param_mqtt_user);
    _wm.addParameter(_param_mqtt_pass);

    // 设置 AP 模式回调
    _wm.setAPCallback(_apCallback);

strncpy(_config.env_note,    MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);

-    _wm.setTitle("🏥 五劳无感康养监测 - 设备配网");  // 配网页面的中文标题

+        "<meta name='theme-color' content='#2c7da0'>"

-        "body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"

-        ".msg .info { font-size: 14px; }"

+        "*{box-sizing:border-box;margin:0;padding:0}"

+        "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"

+        "background:linear-gradient(135deg,#e8f4f8 0%,#f0f8f0 50%,#f5f0e8 100%);"

+        "min-height:100vh;padding:16px;color:#2d3436;"

+        "max-width:420px;margin:0 auto;"

+        "font-size:1.5em;font-weight:700;color:#2c7da0;"

+        "text-align:center;padding:20px 0 8px;"

+        "font-size:0.9em;font-weight:400;color:#636e72;"

+        "text-align:center;padding-bottom:16px;"

+        "background:#fff;border-radius:12px;padding:20px;"

+        "font-size:0.85em;font-weight:600;color:#636e72;"

+        "text-transform:uppercase;letter-spacing:0.5px;"

+        "padding-bottom:12px;margin-bottom:12px;"

+        "border-bottom:1px solid #eee;"

+        "display:flex;align-items:center;gap:6px;"

+        "display:block;font-size:0.85em;font-weight:500;"

+        "color:#555;margin:12px 0 4px;"

+        "display:block;width:100%;padding:10px 12px;"

+        "font-size:0.95em;border:1.5px solid #dfe6e9;"

+        "border-radius:8px;background:#fafafa;"

+        "transition:border-color 0.2s,background 0.2s;"

+        "border-color:#2c7da0;background:#fff;"

+        "input[type=checkbox]{width:auto;display:inline;margin-right:6px}"

+        "display:block;width:100%;padding:12px;font-size:1em;"

+        "font-weight:600;border:none;border-radius:10px;"

+        "cursor:pointer;transition:all 0.2s;"

+        "color:#fff;background:linear-gradient(135deg,#2c7da0,#3a9ec4);"

+        "button:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(44,125,160,0.35)}"

+        "button:active{transform:translateY(0);opacity:0.9}"

+        "button.D{background:linear-gradient(135deg,#d63031,#e17055);box-shadow:0 2px 6px rgba(214,48,49,0.25)}"

+        "padding:16px;margin:12px 0;border-radius:10px;"

+        "font-size:0.9em;line-height:1.6;"

+        "border-left:4px solid #636e72;"

+        ".msg.S{border-left-color:#00b894;background:#e6fff8}"

+        ".msg.P{border-left-color:#2c7da0;background:#e8f4f8}"

+        ".msg.D{border-left-color:#d63031;background:#fff0f0}"

+        ".q{display:inline-block;vertical-align:middle}"

+        "a{color:#2c7da0;font-weight:600;text-decoration:none}"

+        "a:hover{text-decoration:underline}"

+        "text-align:center;padding:8px 0;"

+        "display:inline-block;background:#2c7da0;color:#fff;"

+        "width:22px;height:22px;line-height:22px;border-radius:50%;"

+        "font-size:0.75em;font-weight:700;margin:0 4px;"

+        "text-align:center;font-size:0.75em;color:#b2bec3;"

+        "@media(prefers-color-scheme:dark){"

+        "body{background:#1a1a2e;color:#eee}"

+        ".card{background:#252540;box-shadow:0 2px 8px rgba(0,0,0,0.3)}"

+        ".card-title{border-bottom-color:#333;color:#aaa}"

+        "input,select{background:#1a1a2e;border-color:#444;color:#eee}"

+        "input:focus,select:focus{border-color:#3a9ec4}"

-    WiFi.setAutoReconnect(true);  // ESP32 自动重连

-    // 注册保存回调 (必须使用静态函数, 不能使用 lambda)

+    // 开启密码可见切换

+        "<div style='font-size:1.1em;font-weight:700;color:#2c7da0;margin-bottom:8px'>"

+        "<div style='line-height:2;font-size:0.9em;color:#555'>"

+        "3 设置设备名称和安装环境
"

+        "4 点击保存，等待连接成功"

+    // 注册保存回调

-    // 创建自定义参数

+    _param_device_id = new WiFiManagerParameter(

+        "device_id", "🔖 设备编号 (网关唯一标识)",

+    _param_env_note = new WiFiManagerParameter(

+        "env_note", "📍 安装环境 (如: 主卧 / 客厅 / 老人房)",

-    _param_env_note = new WiFiManagerParameter(

-        "env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",

-    // 注册自定义参数

+    // 参数按顺序注册: 设备在前, MQTT 在后

+    _wm.addParameter(_param_device_id);

-    _wm.addParameter(_param_device_id);

-    // 设置 AP 模式回调 (静态函数)

+    // 设置 AP 模式回调


Now update the AP callback with better serial instructions for the user.


现在更新 AP 模式回调，打印更清晰的配网指引。


void WiFiMgmt::_apCallback(WiFiManager* wm) {
    Serial.println(F("[WiFi] 🔧 进入配网模式"));
    Serial.printf("  AP WiFi名称: %s\n", WIFI_AP_SSID);
    Serial.printf("  AP 密码: %s\n", WIFI_AP_PASSWORD);
    Serial.println(F("  手机连接此 WiFi 即可打开配网页面 (自动弹出)"));
}

void WiFiMgmt::_apCallback(WiFiManager* wm) {
    Serial.println(F("\n╔══════════════════════════════════════════╗"));
    Serial.println(F("║  🔧 配网模式已启动                       ║"));
    Serial.println(F("╠══════════════════════════════════════════╣"));
    Serial.printf( "║  WiFi名称: %-29s ║\n", WIFI_AP_SSID);
    Serial.printf( "║  密码:     %-29s ║\n", WIFI_AP_PASSWORD);
    Serial.println(F("╠══════════════════════════════════════════╣"));
    Serial.println(F("║  📱 请按以下步骤操作:                   ║"));
    Serial.println(F("║  1. 手机打开 WiFi 设置                  ║"));
    Serial.println(F("║  2. 连接上方显示的 WiFi                 ║"));
    Serial.println(F("║  3. 浏览器将自动弹出配网页面            ║"));
    Serial.println(F("║     (如未弹出, 手动访问 192.168.4.1)    ║"));
    Serial.println(F("╚══════════════════════════════════════════╝\n"));
}


void WiFiMgmt::_apCallback(WiFiManager* wm) {
    Serial.println(F("[WiFi] 🔧 进入配网模式"));
    Serial.printf("  AP WiFi名称: %s\n", WIFI_AP_SSID);
    Serial.printf("  AP 密码: %s\n", WIFI_AP_PASSWORD);
    Serial.println(F("  手机连接此 WiFi 即可打开配网页面 (自动弹出)"));
}

void WiFiMgmt::_apCallback(WiFiManager* wm) {
    Serial.println(F("\n╔══════════════════════════════════════════╗"));
    Serial.println(F("║  🔧 配网模式已启动                       ║"));
    Serial.println(F("╠══════════════════════════════════════════╣"));
    Serial.printf( "║  WiFi名称: %-29s ║\n", WIFI_AP_SSID);
    Serial.printf( "║  密码:     %-29s ║\n", WIFI_AP_PASSWORD);
    Serial.println(F("╠══════════════════════════════════════════╣"));
    Serial.println(F("║  📱 请按以下步骤操作:                   ║"));
    Serial.println(F("║  1. 手机打开 WiFi 设置                  ║"));
    Serial.println(F("║  2. 连接上方显示的 WiFi                 ║"));
    Serial.println(F("║  3. 浏览器将自动弹出配网页面            ║"));
    Serial.println(F("║     (如未弹出, 手动访问 192.168.4.1)    ║"));
    Serial.println(F("╚══════════════════════════════════════════╝\n"));
}

-    Serial.println(F("[WiFi] 🔧 进入配网模式"));

-    Serial.printf("  AP WiFi名称: %s\n", WIFI_AP_SSID);

-    Serial.printf("  AP 密码: %s\n", WIFI_AP_PASSWORD);

-    Serial.println(F("  手机连接此 WiFi 即可打开配网页面 (自动弹出)"));

+    Serial.println(F("\n╔══════════════════════════════════════════╗"));

+    Serial.println(F("║  🔧 配网模式已启动                       ║"));

+    Serial.println(F("╠══════════════════════════════════════════╣"));

+    Serial.printf( "║  WiFi名称: %-29s ║\n", WIFI_AP_SSID);

+    Serial.printf( "║  密码:     %-29s ║\n", WIFI_AP_PASSWORD);

+    Serial.println(F("║  📱 请按以下步骤操作:                   ║"));

+    Serial.println(F("║  1. 手机打开 WiFi 设置                  ║"));

+    Serial.println(F("║  2. 连接上方显示的 WiFi                 ║"));

+    Serial.println(F("║  3. 浏览器将自动弹出配网页面            ║"));

+    Serial.println(F("║     (如未弹出, 手动访问 192.168.4.1)    ║"));

+    Serial.println(F("╚══════════════════════════════════════════╝\n"));


Now let me update the todo list and also update the `startConfigPortal()` method to match the same improved style, and also update `_connectWiFi()` success message to be more concise.

Let me also update the `startConfigPortal()` AP callback - actually it uses the same `_apCallback` so it's already updated.

Let me verify the final code looks good by reading the key sections.


TodoWrite

重写 setCustomHeadElement CSS 样式

添加 setCustomMenuHTML 引导说明

优化参数标签（分组、更专业的描述）

更新 AP 模式回调解说

验证最终代码完整性


重写 setCustomHeadElement CSS 样式

添加 setCustomMenuHTML 引导说明

优化参数标签（分组、更专业的描述）

更新 AP 模式回调解说

重写 setCustomHeadElement CSS 样式

添加 setCustomMenuHTML 引导说明

优化参数标签（分组、更专业的描述）

更新 AP 模式回调解说

验证最终代码完整性


Let me verify the final state of the begin() function and AP callback to make sure everything is coherent.


验证修改后的完整代码：


44	bool WiFiMgmt::begin() {
45	    Serial.println(F("\n========================================"));
46	    Serial.println(F("  WiFi 配网管理器 初始化"));
47	    Serial.println(F("========================================"));
48
49	    // 初始化引脚
50	    pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
51	    pinMode(LED_WIFI_PIN, OUTPUT);
52	    digitalWrite(LED_WIFI_PIN, LOW);
53
54	    // 初始化 LittleFS
55	    if (!LittleFS.begin(true)) {
56	        Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));
57	        return false;
58	    }
59	    Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));
60
61	    // 加载配置
62	    if (!_loadConfig()) {
63	        Serial.println(F("[WiFi] 使用默认配置"));
64	        // 设置默认值
65	        strncpy(_config.mqtt_server, MQTT_DEFAULT_SERVER,   sizeof(_config.mqtt_server) - 1);
66	        strncpy(_config.mqtt_port,   MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
67	        strncpy(_config.mqtt_user,   MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
68	        strncpy(_config.mqtt_pass,   MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
69	        strncpy(_config.device_id,   MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
70	        strncpy(_config.env_note,    MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);
71	    }
72
73	    // ============================================================
74	    // 配置 WiFiManager (配网页面的外观和行为)
75	    // ============================================================
76
77	    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
78	    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
79	    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
80	    _wm.setTitle("健康监测 · 设备配网");
81
82	    // --- 自定义 CSS (现代医疗主题, 移动端优先) ---
83	    _wm.setCustomHeadElement(
84	        "<meta name='theme-color' content='#2c7da0'>"
85	        "<style>"
86	        /* === 基础样式 === */
87	        "*{box-sizing:border-box;margin:0;padding:0}"
88	        "body{"
89	        "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
90	        "background:linear-gradient(135deg,#e8f4f8 0%,#f0f8f0 50%,#f5f0e8 100%);"
91	        "min-height:100vh;padding:16px;color:#2d3436;"
92	        "}"
93	        ".wrap{"
94	        "max-width:420px;margin:0 auto;"
95	        "}"
96	        /* === 顶部标题 === */
97	        "h1{"
98	        "font-size:1.5em;font-weight:700;color:#2c7da0;"
99	        "text-align:center;padding:20px 0 8px;"
100	        "}"
101	        "h3{"
102	        "font-size:0.9em;font-weight:400;color:#636e72;"
103	        "text-align:center;padding-bottom:16px;"
104	        "}"
105	        /* === 卡片容器 === */
106	        ".card{"
107	        "background:#fff;border-radius:12px;padding:20px;"
108	        "margin-bottom:12px;"
109	        "box-shadow:0 2px 8px rgba(0,0,0,0.06);"
110	        "}"
111	        ".card-title{"
112	        "font-size:0.85em;font-weight:600;color:#636e72;"
113	        "text-transform:uppercase;letter-spacing:0.5px;"
114	        "padding-bottom:12px;margin-bottom:12px;"
115	        "border-bottom:1px solid #eee;"
116	        "display:flex;align-items:center;gap:6px;"
117	        "}"
118	        /* === 表单输入 === */
119	        "label{"
120	        "display:block;font-size:0.85em;font-weight:500;"
121	        "color:#555;margin:12px 0 4px;"
122	        "}"
123	        "input,select{"
124	        "display:block;width:100%;padding:10px 12px;"
125	        "font-size:0.95em;border:1.5px solid #dfe6e9;"
126	        "border-radius:8px;background:#fafafa;"
127	        "transition:border-color 0.2s,background 0.2s;"
128	        "outline:none;color:#2d3436;"
129	        "}"
130	        "input:focus,select:focus{"
131	        "border-color:#2c7da0;background:#fff;"
132	        "box-shadow:0 0 0 3px rgba(44,125,160,0.1);"
133	        "}"
134	        "input[type=checkbox]{width:auto;display:inline;margin-right:6px}"
135	        /* === 按钮 === */
136	        "button{"
137	        "display:block;width:100%;padding:12px;font-size:1em;"
138	        "font-weight:600;border:none;border-radius:10px;"
139	        "cursor:pointer;transition:all 0.2s;"
140	        "color:#fff;background:linear-gradient(135deg,#2c7da0,#3a9ec4);"
141	        "box-shadow:0 2px 6px rgba(44,125,160,0.25);"
142	        "margin-top:8px;"
143	        "}"
144	        "button:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(44,125,160,0.35)}"
145	        "button:active{transform:translateY(0);opacity:0.9}"
146	        "button.D{background:linear-gradient(135deg,#d63031,#e17055);box-shadow:0 2px 6px rgba(214,48,49,0.25)}"
147	        /* === 消息提示 === */
148	        ".msg{"
149	        "padding:16px;margin:12px 0;border-radius:10px;"
150	        "font-size:0.9em;line-height:1.6;"
151	        "border-left:4px solid #636e72;"
152	        "background:#f8f9fa;"
153	        "}"
154	        ".msg.S{border-left-color:#00b894;background:#e6fff8}"
155	        ".msg.P{border-left-color:#2c7da0;background:#e8f4f8}"
156	        ".msg.D{border-left-color:#d63031;background:#fff0f0}"
157	        /* === 网络列表 === */
158	        ".q{display:inline-block;vertical-align:middle}"
159	        "a{color:#2c7da0;font-weight:600;text-decoration:none}"
160	        "a:hover{text-decoration:underline}"
161	        /* === 引导区 === */
162	        ".guide{"
163	        "text-align:center;padding:8px 0;"
164	        "font-size:0.85em;color:#888;"
165	        "}"
166	        ".guide .step{"
167	        "display:inline-block;background:#2c7da0;color:#fff;"
168	        "width:22px;height:22px;line-height:22px;border-radius:50%;"
169	        "font-size:0.75em;font-weight:700;margin:0 4px;"
170	        "}"
171	        /* === 页脚 === */
172	        ".footer{"
173	        "text-align:center;font-size:0.75em;color:#b2bec3;"
174	        "padding:20px 0;"
175	        "}"
176	        /* === 暗色模式适配 === */
177	        "@media(prefers-color-scheme:dark){"
178	        "body{background:#1a1a2e;color:#eee}"
179	        ".card{background:#252540;box-shadow:0 2px 8px rgba(0,0,0,0.3)}"
180	        ".card-title{border-bottom-color:#333;color:#aaa}"
181	        "label{color:#bbb}"
182	        "input,select{background:#1a1a2e;border-color:#444;color:#eee}"
183	        "input:focus,select:focus{border-color:#3a9ec4}"

bool WiFiMgmt::begin() {
    Serial.println(F("\n========================================"));
    Serial.println(F("  WiFi 配网管理器 初始化"));
    Serial.println(F("========================================"));

    // 初始化引脚
    pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
    pinMode(LED_WIFI_PIN, OUTPUT);
    digitalWrite(LED_WIFI_PIN, LOW);

    // 初始化 LittleFS
    if (!LittleFS.begin(true)) {
        Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));
        return false;
    }
    Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));

    // 加载配置
    if (!_loadConfig()) {
        Serial.println(F("[WiFi] 使用默认配置"));
        // 设置默认值
        strncpy(_config.mqtt_server, MQTT_DEFAULT_SERVER,   sizeof(_config.mqtt_server) - 1);
        strncpy(_config.mqtt_port,   MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
        strncpy(_config.mqtt_user,   MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
        strncpy(_config.mqtt_pass,   MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
        strncpy(_config.device_id,   MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
        strncpy(_config.env_note,    MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);
    }

    // ============================================================
    // 配置 WiFiManager (配网页面的外观和行为)
    // ============================================================

    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("健康监测 · 设备配网");

    // --- 自定义 CSS (现代医疗主题, 移动端优先) ---
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#2c7da0'>"
        "<style>"
        /* === 基础样式 === */
        "*{box-sizing:border-box;margin:0;padding:0}"
        "body{"
        "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
        "background:linear-gradient(135deg,#e8f4f8 0%,#f0f8f0 50%,#f5f0e8 100%);"
        "min-height:100vh;padding:16px;color:#2d3436;"
        "}"
        ".wrap{"
        "max-width:420px;margin:0 auto;"
        "}"
        /* === 顶部标题 === */
        "h1{"
        "font-size:1.5em;font-weight:700;color:#2c7da0;"
        "text-align:center;padding:20px 0 8px;"
        "}"
        "h3{"
        "font-size:0.9em;font-weight:400;color:#636e72;"
        "text-align:center;padding-bottom:16px;"
        "}"
        /* === 卡片容器 === */
        ".card{"
        "background:#fff;border-radius:12px;padding:20px;"
        "margin-bottom:12px;"
        "box-shadow:0 2px 8px rgba(0,0,0,0.06);"
        "}"
        ".card-title{"
        "font-size:0.85em;font-weight:600;color:#636e72;"
        "text-transform:uppercase;letter-spacing:0.5px;"
        "padding-bottom:12px;margin-bottom:12px;"
        "border-bottom:1px solid #eee;"
        "display:flex;align-items:center;gap:6px;"
        "}"
        /* === 表单输入 === */
        "label{"
        "display:block;font-size:0.85em;font-weight:500;"
        "color:#555;margin:12px 0 4px;"
        "}"
        "input,select{"
        "display:block;width:100%;padding:10px 12px;"
        "font-size:0.95em;border:1.5px solid #dfe6e9;"
        "border-radius:8px;background:#fafafa;"
        "transition:border-color 0.2s,background 0.2s;"
        "outline:none;color:#2d3436;"
        "}"
        "input:focus,select:focus{"
        "border-color:#2c7da0;background:#fff;"
        "box-shadow:0 0 0 3px rgba(44,125,160,0.1);"
        "}"
        "input[type=checkbox]{width:auto;display:inline;margin-right:6px}"
        /* === 按钮 === */
        "button{"
        "display:block;width:100%;padding:12px;font-size:1em;"
        "font-weight:600;border:none;border-radius:10px;"
        "cursor:pointer;transition:all 0.2s;"
        "color:#fff;background:linear-gradient(135deg,#2c7da0,#3a9ec4);"
        "box-shadow:0 2px 6px rgba(44,125,160,0.25);"
        "margin-top:8px;"
        "}"
        "button:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(44,125,160,0.35)}"
        "button:active{transform:translateY(0);opacity:0.9}"
        "button.D{background:linear-gradient(135deg,#d63031,#e17055);box-shadow:0 2px 6px rgba(214,48,49,0.25)}"
        /* === 消息提示 === */
        ".msg{"
        "padding:16px;margin:12px 0;border-radius:10px;"
        "font-size:0.9em;line-height:1.6;"
        "border-left:4px solid #636e72;"
        "background:#f8f9fa;"
        "}"
        ".msg.S{border-left-color:#00b894;background:#e6fff8}"
        ".msg.P{border-left-color:#2c7da0;background:#e8f4f8}"
        ".msg.D{border-left-color:#d63031;background:#fff0f0}"
        /* === 网络列表 === */
        ".q{display:inline-block;vertical-align:middle}"
        "a{color:#2c7da0;font-weight:600;text-decoration:none}"
        "a:hover{text-decoration:underline}"
        /* === 引导区 === */
        ".guide{"
        "text-align:center;padding:8px 0;"
        "font-size:0.85em;color:#888;"
        "}"
        ".guide .step{"
        "display:inline-block;background:#2c7da0;color:#fff;"
        "width:22px;height:22px;line-height:22px;border-radius:50%;"
        "font-size:0.75em;font-weight:700;margin:0 4px;"
        "}"
        /* === 页脚 === */
        ".footer{"
        "text-align:center;font-size:0.75em;color:#b2bec3;"
        "padding:20px 0;"
        "}"
        /* === 暗色模式适配 === */
        "@media(prefers-color-scheme:dark){"
        "body{background:#1a1a2e;color:#eee}"
        ".card{background:#252540;box-shadow:0 2px 8px rgba(0,0,0,0.3)}"
        ".card-title{border-bottom-color:#333;color:#aaa}"
        "label{color:#bbb}"
        "input,select{background:#1a1a2e;border-color:#444;color:#eee}"
        "input:focus,select:focus{border-color:#3a9ec4}"


The code looks good. Let me continue reading to verify the rest.