// ============================================================
// 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);
    }

    return _connected;
}

// ============================================================
// WiFi 连接 (仅尝试已保存凭据, 不启动配网页面)
// ============================================================

bool WiFiMgmt::_connectWiFi() {
    // 仅使用已保存的凭据尝试连接, 不启动配网页面
    // WiFiManager 已在 Preferences 中保存了凭据
    WiFi.begin();  // 使用 ESP32 内部保存的上一次 WiFi 凭据

    int retry = 0;
    while (WiFi.status() != WL_CONNECTED && retry < 40) {  // 20 秒超时
        delay(500);
        Serial.print(".");
        retry++;
    }
    Serial.println();

    _connected = (WiFi.status() == WL_CONNECTED);

    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());

        // 重新加载配置 (确保获取最新值)
        _loadConfig();

        // 同步自定义参数
        _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);
    }

    return _connected;
}

// ============================================================
// 自动配网 (尝试连接 → 失败则启动配网页面, 仅 begin() 使用)
// ============================================================

bool WiFiMgmt::_autoConnect() {
    // autoConnect 会先尝试已保存凭据, 失败后启动配网页面
    // 配网页面超时后 autoConnect 返回 false
    _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());

        _loadConfig();

        _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);
    } else {
        Serial.println(F("[WiFi] ⚠  配网超时 (60s), 将恢复尝试已保存连接..."));
    }

    return _connected;
}

+// WiFi 连接 (仅尝试已保存凭据, 不启动配网页面)

-    // 检查是否有已保存的配置

-    // autoConnect 会先尝试连接, 失败后启动 AP 配网页面

-    _connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);

+    // 仅使用已保存的凭据尝试连接, 不启动配网页面

+    // WiFiManager 已在 Preferences 中保存了凭据

+    WiFi.begin();  // 使用 ESP32 内部保存的上一次 WiFi 凭据

+    while (WiFi.status() != WL_CONNECTED && retry < 40) {  // 20 秒超时

+    _connected = (WiFi.status() == WL_CONNECTED);

// 重新加载配置 (确保获取最新值)

-        // autoConnect 过程中如果有保存, 回调已写入 LittleFS

-        // 同步自定义参数 (如果配置是从 LittleFS 加载的)

+        // 同步自定义参数

+// 自动配网 (尝试连接 → 失败则启动配网页面, 仅 begin() 使用)

+    // autoConnect 会先尝试已保存凭据, 失败后启动配网页面

+    _connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);

+        Serial.println(F("[WiFi] ✅ 已连接 (配网完成)!"));

+        Serial.printf("  WiFi名称: %s\n", WiFi.SSID().c_str());

+        Serial.printf("  IP地址:   %s\n", WiFi.localIP().toString().c_str());

+        _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.println(F("[WiFi] ⚠  配网超时 (60s), 将恢复尝试已保存连接..."));


现在要求，配网在60s没成功自动恢复连接前的，


Now I need to update `begin()` to use `_autoConnect()` instead of `_connectWiFi()` for the initial setup, and make sure `loop()` only uses `_connectWiFi()` (no portal). Let me also update the `begin()` method and the `loop()` method.

Let me re-read the current begin() to make the edit.


Now update `begin()` to use `_autoConnect()` for the initial setup with portal fallback:


255
256	    // 尝试连接 WiFi
257	    _connected = _connectWiFi();
258
259	    if (!_connected) {
260	        // 如果自动连接失败, 启动配网页面
261	        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
262	        _setLEDMode(2);  // 快闪 = 配网模式
263	        _connected = _connectWiFi();  // 内部会启动配网页面
264	    }
265
266	    if (_connected) {
267	        _setLEDMode(3);  // 常亮 = 已连接
268	    }
269
270	    _lastCheckTime = millis();
271	    return _connected;
272	}
273
274	// ============================================================

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

    if (!_connected) {
        // 如果自动连接失败, 启动配网页面
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
        _setLEDMode(2);  // 快闪 = 配网模式
        _connected = _connectWiFi();  // 内部会启动配网页面
    }

    if (_connected) {
        _setLEDMode(3);  // 常亮 = 已连接
    }

    _lastCheckTime = millis();
    return _connected;
}

// ============================================================


Now I need to update `begin()`:
- First try `_connectWiFi()` (saved creds only)
- If fails, call `_autoConnect()` (portal with 60s timeout)
- After `_autoConnect()` returns (whether success or timeout), the loop will handle reconnection

And the `loop()` should keep calling `_connectWiFi()` which now only tries saved creds without starting the portal.


Edit

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

    if (!_connected) {
        // 如果自动连接失败, 启动配网页面
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
        _setLEDMode(2);  // 快闪 = 配网模式
        _connected = _connectWiFi();  // 内部会启动配网页面
    }

    // 尝试连接 WiFi (先用已保存凭据)
    _connected = _connectWiFi();

    if (!_connected) {
        // 自动连接失败, 启动配网页面 (60s 超时后自动退出)
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式 (60s 超时)..."));
        _setLEDMode(2);  // 快闪 = 配网模式
        _connected = _autoConnect();  // 配网页面, 60s 超时自动退出
        if (!_connected) {
            // 配网超时, 恢复尝试已保存连接
            Serial.println(F("[WiFi] 配网超时, 尝试恢复已保存连接..."));
            _connected = _connectWiFi();
        }
    }


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)

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

    if (!_connected) {
        // 如果自动连接失败, 启动配网页面
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
        _setLEDMode(2);  // 快闪 = 配网模式
        _connected = _connectWiFi();  // 内部会启动配网页面
    }

    // 尝试连接 WiFi (先用已保存凭据)
    _connected = _connectWiFi();

    if (!_connected) {
        // 自动连接失败, 启动配网页面 (60s 超时后自动退出)
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式 (60s 超时)..."));
        _setLEDMode(2);  // 快闪 = 配网模式
        _connected = _autoConnect();  // 配网页面, 60s 超时自动退出
        if (!_connected) {
            // 配网超时, 恢复尝试已保存连接
            Serial.println(F("[WiFi] 配网超时, 尝试恢复已保存连接..."));
            _connected = _connectWiFi();
        }
    }

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

+    // 尝试连接 WiFi (先用已保存凭据)

-        // 如果自动连接失败, 启动配网页面

-        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));

+        // 自动连接失败, 启动配网页面 (60s 超时后自动退出)

+        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式 (60s 超时)..."));

-        _connected = _connectWiFi();  // 内部会启动配网页面

+        _connected = _autoConnect();  // 配网页面, 60s 超时自动退出

+            // 配网超时, 恢复尝试已保存连接

+            Serial.println(F("[WiFi] 配网超时, 尝试恢复已保存连接..."));

+            _connected = _connectWiFi();


<ide_diagnostics>[
  {
    "filePath": "/d:/PlatformIO1/Projects/Healthv2.0/src/wifi_manager.cpp",
    "line": 263,
    "column": 22,
    "message": "未定义标识符 \"_autoConnect\"",
    "code": "20",
    "severity": "Error"
  }
]</ide_diagnostics>


The IntelliSense is showing an error because `_autoConnect` is defined after `begin()` in the file. I need to check the order of methods in the file. Let me look at the structure - `_autoConnect` was added after `_connectWiFi`, but `begin()` is above both of them. I need to add a forward declaration, or move the `_autoConnect` method before `begin()`, or just add the declaration in the header file.

The easiest fix is to add `_autoConnect` to the header file's private section. Let me check the header.


需要在头文件中声明 `_autoConnect`：


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

    /**
     * @brief 尝试连接 WiFi (仅已保存凭据, 不启动配网页面)
     */
    bool _connectWiFi();

    /**
     * @brief 自动配网: 先尝试连接, 失败则启动配网页面 (60s 超时)
     */
    bool _autoConnect();


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

    /**
     * @brief 尝试连接 WiFi (仅已保存凭据, 不启动配网页面)
     */
    bool _connectWiFi();

    /**
     * @brief 自动配网: 先尝试连接, 失败则启动配网页面 (60s 超时)
     */
    bool _autoConnect();

/**
 * @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];      // 环境备注 (如: "主卧", "客厅", "老人房")
};

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

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

    /**
     * @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 强制进入配网模式
     */
    void startConfigPortal();

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

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

    /**
     * @brief 设置 LED 模式 (线程安全, 无阻塞)
     * @param mode 0=灭 1=慢闪(断连1s) 2=快闪(配网300ms) 3=常亮(已连接)
     */
    void _setLEDMode(uint8_t mode);

    /**
     * @brief LED 闪烁独立任务 (FreeRTOS, 不受主循环阻塞影响)
     */
    static void _ledTaskFunc(void* param);

    /**
     * @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 独立任务 (不被 startConfigPortal 阻塞影响)
    TaskHandle_t    _ledTaskHandle;
    volatile uint8_t _ledMode;  // 0=灭 1=慢闪 2=快闪 3=常亮

    // 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;
};

#endif // WIFI_MANAGER_H

+     * @brief 尝试连接 WiFi (仅已保存凭据, 不启动配网页面)

+     * @brief 自动配网: 先尝试连接, 失败则启动配网页面 (60s 超时)


Good, the declaration is added. Now let me also update the comment block for `_connectWiFi` to clarify the new behavior. Let me also verify the `loop()` method still uses `_connectWiFi()` (which now only tries saved creds without portal) — that should be fine, I didn't change it.

Let me verify the loop method and the overall changes are consistent.


验证一下整体改动是否正确：


254	    _wm.setAPCallback(_apCallback);
255
256	    // 尝试连接 WiFi (先用已保存凭据)
257	    _connected = _connectWiFi();
258
259	    if (!_connected) {
260	        // 自动连接失败, 启动配网页面 (60s 超时后自动退出)
261	        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式 (60s 超时)..."));
262	        _setLEDMode(2);  // 快闪 = 配网模式
263	        _connected = _autoConnect();  // 配网页面, 60s 超时自动退出
264	        if (!_connected) {
265	            // 配网超时, 恢复尝试已保存连接
266	            Serial.println(F("[WiFi] 配网超时, 尝试恢复已保存连接..."));
267	            _connected = _connectWiFi();
268	        }
269	    }
270
271	    if (_connected) {
272	        _setLEDMode(3);  // 常亮 = 已连接
273	    }
274
275	    _lastCheckTime = millis();
276	    return _connected;
277	}
278
279	// ============================================================
280	// 主循环
281	// ============================================================
282
283	bool WiFiMgmt::loop() {
284	    // 检测按钮长按
285	    if (_checkButtonLongPress()) {
286	        Serial.println(F("[WiFi] ⚠  检测到长按, 清除配置并重启..."));
287	        resetSettings();
288	        return false;
289	    }
290
291	    // 检查 WiFi 状态
292	    if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
293	        _lastCheckTime = millis();
294
295	        if (WiFi.status() != WL_CONNECTED) {
296	            _connected = false;
297	            _setLEDMode(1);  // 慢闪 = 未连接
298	            Serial.println(F("[WiFi] ⚠  WiFi 断开, 尝试重连..."));
299	            _connectWiFi();
300	        } else {
301	            _connected = true;
302	            _setLEDMode(3);  // 常亮 = 已连接
303	        }
304	    }
305
306	    // LED 由独立 FreeRTOS 任务处理 (不受 startConfigPortal 阻塞影响)
307	    return _connected;
308	}
309
310	// ============================================================
311	// WiFi 连接 (仅尝试已保存凭据, 不启动配网页面)
312	// ============================================================
313
314	bool WiFiMgmt::_connectWiFi() {
315	    // 仅使用已保存的凭据尝试连接, 不启动配网页面
316	    // WiFiManager 已在 Preferences 中保存了凭据
317	    WiFi.begin();  // 使用 ESP32 内部保存的上一次 WiFi 凭据
318
319	    int retry = 0;
320	    while (WiFi.status() != WL_CONNECTED && retry < 40) {  // 20 秒超时
321	        delay(500);
322	        Serial.print(".");
323	        retry++;
324	    }
325	    Serial.println();
326
327	    _connected = (WiFi.status() == WL_CONNECTED);
328

    _wm.setAPCallback(_apCallback);

    // 尝试连接 WiFi (先用已保存凭据)
    _connected = _connectWiFi();

    if (!_connected) {
        // 自动连接失败, 启动配网页面 (60s 超时后自动退出)
        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式 (60s 超时)..."));
        _setLEDMode(2);  // 快闪 = 配网模式
        _connected = _autoConnect();  // 配网页面, 60s 超时自动退出
        if (!_connected) {
            // 配网超时, 恢复尝试已保存连接
            Serial.println(F("[WiFi] 配网超时, 尝试恢复已保存连接..."));
            _connected = _connectWiFi();
        }
    }

    if (_connected) {
        _setLEDMode(3);  // 常亮 = 已连接
    }

    _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(1);  // 慢闪 = 未连接
            Serial.println(F("[WiFi] ⚠  WiFi 断开, 尝试重连..."));
            _connectWiFi();
        } else {
            _connected = true;
            _setLEDMode(3);  // 常亮 = 已连接
        }
    }

    // LED 由独立 FreeRTOS 任务处理 (不受 startConfigPortal 阻塞影响)
    return _connected;
}

// ============================================================
// WiFi 连接 (仅尝试已保存凭据, 不启动配网页面)
// ============================================================

bool WiFiMgmt::_connectWiFi() {
    // 仅使用已保存的凭据尝试连接, 不启动配网页面
    // WiFiManager 已在 Preferences 中保存了凭据
    WiFi.begin();  // 使用 ESP32 内部保存的上一次 WiFi 凭据

    int retry = 0;
    while (WiFi.status() != WL_CONNECTED && retry < 40) {  // 20 秒超时
        delay(500);
        Serial.print(".");
        retry++;
    }
    Serial.println();

    _connected = (WiFi.status() == WL_CONNECTED);


Everything looks correct now. Let me also read the `_autoConnect` function to verify it's properly defined:


345
346	        Serial.printf("  设备名: %s\n", _config.device_id);
347	        Serial.printf("  环境:   %s\n", _config.env_note);
348	        Serial.printf("  MQTT服务器: %s:%s\n", _config.mqtt_server, _config.mqtt_port);
349	    }
350
351	    return _connected;
352	}
353
354	// ============================================================
355	// 自动配网 (尝试连接 → 失败则启动配网页面, 仅 begin() 使用)
356	// ============================================================
357
358	bool WiFiMgmt::_autoConnect() {
359	    // autoConnect 会先尝试已保存凭据, 失败后启动配网页面
360	    // 配网页面超时后 autoConnect 返回 false
361	    _connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);
362
363	    if (_connected) {
364	        Serial.println(F("[WiFi] ✅ 已连接 (配网完成)!"));
365	        Serial.printf("  WiFi名称: %s\n", WiFi.SSID().c_str());
366	        Serial.printf("  IP地址:   %s\n", WiFi.localIP().toString().c_str());
367
368	        _loadConfig();
369
370	        _param_mqtt_server->setValue(_config.mqtt_server, sizeof(_config.mqtt_server));
371	        _param_mqtt_port->setValue(_config.mqtt_port, sizeof(_config.mqtt_port));
372	        _param_mqtt_user->setValue(_config.mqtt_user, sizeof(_config.mqtt_user));
373	        _param_mqtt_pass->setValue(_config.mqtt_pass, sizeof(_config.mqtt_pass));
374	        _param_device_id->setValue(_config.device_id, sizeof(_config.device_id));
375	        _param_env_note->setValue(_config.env_note, sizeof(_config.env_note));
376
377	        Serial.printf("  设备名: %s\n", _config.device_id);
378	        Serial.printf("  环境:   %s\n", _config.env_note);
379	    } else {
380	        Serial.println(F("[WiFi] ⚠  配网超时 (60s), 将恢复尝试已保存连接..."));
381	    }
382
383	    return _connected;
384	}

        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);
    }

    return _connected;
}

// ============================================================
// 自动配网 (尝试连接 → 失败则启动配网页面, 仅 begin() 使用)
// ============================================================

bool WiFiMgmt::_autoConnect() {
    // autoConnect 会先尝试已保存凭据, 失败后启动配网页面
    // 配网页面超时后 autoConnect 返回 false
    _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());

        _loadConfig();

        _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);
    } else {
        Serial.println(F("[WiFi] ⚠  配网超时 (60s), 将恢复尝试已保存连接..."));
    }

    return _connected;
}


Everything looks good. Let me summarize the changes for the user:

1. `config.h`: `WIFI_CONFIG_TIMEOUT` 从 300 改为 60 秒
2. `wifi_manager.h`: 新增 `_autoConnect()` 声明
3. `wifi_manager.cpp`:
   - `_connectWiFi()`: 现在只使用 `WiFi.begin()` 尝试已保存凭据，**不再启动配网页面**
   - `_autoConnect()`: 新方法，使用 `_wm.autoConnect()`（先尝试连接，失败后启动配网页面，60s 超时）
   - `begin()`: 先 `_connectWiFi()` → 失败 → `_autoConnect()`（60s 配网） → 超时 → 再 `_connectWiFi()` 恢复
   - `loop()`: 断线时只调用 `_connectWiFi()`，不会自动重启配网页面
   - `startConfigPortal()`: 手动触发配网（按钮长按/MQTT指令），不受影响

This is correct and clean.


改动完毕，总结一下：

### 修改了 3 个文件

| 文件 | 改动 |
|------|------|
| [config.h](include/config.h) | `WIFI_CONFIG_TIMEOUT` 300 → **60** 秒 |
| [wifi_manager.h](include/wifi_manager.h) | 新增 `_autoConnect()` 声明 |
| [wifi_manager.cpp](src/wifi_manager.cpp) | 拆分连接逻辑 |

### 新的行为流程

```
开机
  └─ _connectWiFi()           ← 纯尝试已保存凭据, 不动配网页面
       └─ 连上了? ✅ 常亮
       └─ 没连上?
            └─ _autoConnect()  ← 启动配网页面, 60s 超时
                 └─ 配网成功? ✅ 常亮
                 └─ 60s 超时?
                      └─ _connectWiFi()  ← 自动恢复尝试原连接

主循环 loop() 中断线
  └─ _connectWiFi()           ← 只尝试重连, 不再弹配网页面
```

**关键变化**：
- 配网页面 60 秒没人配置 → 自动退出，恢复尝试之前的 WiFi
- 日常断线重连**不会**再弹出配网页面，只有开机首次/手动长按按钮/MQTT 指令才会进入配网模式


<ide_selection>The user selected the lines 263 to 263 from d:\PlatformIO1\Projects\Healthv2.0\src\wifi_manager.cpp:
_autoConnect

This may or may not be related to the current task.</ide_selection>

未定义标识符 "_autoConnect"


The IntelliSense error is still showing. But I already added the declaration in the header file. Let me check if the header file was updated correctly. The issue might be that IntelliSense hasn't picked up the change yet, or the declaration might not be correct.

Let me re-read the header to verify.


让我检查头文件是否确实更新了：


82
83	private:
84	    /**
85	     * @brief 尝试连接 WiFi (仅已保存凭据, 不启动配网页面)
86	     */
87	    bool _connectWiFi();
88
89	    /**
90	     * @brief 自动配网: 先尝试连接, 失败则启动配网页面 (60s 超时)
91	     */
92	    bool _autoConnect();
93
94	    /**
95	     * @brief 设置 LED 模式 (线程安全, 无阻塞)
96	     * @param mode 0=灭 1=慢闪(断连1s) 2=快闪(配网300ms) 3=常亮(已连接)

private:
    /**
     * @brief 尝试连接 WiFi (仅已保存凭据, 不启动配网页面)
     */
    bool _connectWiFi();

    /**
     * @brief 自动配网: 先尝试连接, 失败则启动配网页面 (60s 超时)
     */
    bool _autoConnect();

    /**
     * @brief 设置 LED 模式 (线程安全, 无阻塞)
     * @param mode 0=灭 1=慢闪(断连1s) 2=快闪(配网300ms) 3=常亮(已连接)


The header file already has the `_autoConnect()` declaration on line 92. The IntelliSense error is stale — VS Code's C/C++ extension hasn't re-indexed the header file yet.

Let me verify the cpp file also has the implementation.


头文件声明已经在了（第 92 行 `bool _autoConnect();`），实现也在 cpp 里。这个 IntelliSense 报错是**缓存未刷新**——试试以下操作让它消失：

- **快捷键**：`Ctrl+Shift+P` → 输入 `C/C++: Reset IntelliSense Database` → 回车
- 或者直接**重新加载窗口**：`Ctrl+Shift+P` → `Developer: Reload Window`

> 💡 不影响实际编译，`_autoConnect` 的声明和实现都已就位，编译器能找到。只是 VS Code 的 C/C++ 插件索引还没更新。


配网界面能不能完全按照软件组写的。


The user wants the WiFi provisioning page (配网界面) to exactly match what the software team designed. Let me look at what the current code has and check if there's a design file or reference for the software team's design.

Let me check the `data/index.html` file since it was modified according to git status, and look for any design references.


让我先看看软件组的设计稿在哪里：


1	<!DOCTYPE html>
2
3
4	    <meta charset="UTF-8">
5	    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
6	    <title>健康监测系统 - 配网设置</title>
7
249
250
251

252
253

254
🏥

255
健康监测系统

256
ESP32-S3 + 毫米波雷达 · 配网设置

257

258
259
260

261

262	                📶 WiFi 设置
263

264

265	                WiFi 名称 (SSID)
266	                <input type="text" id="wifi-ssid" placeholder="请输入 WiFi 名称" autocomplete="off">
267

268

269	                WiFi 密码
270	                <input type="password" id="wifi-pass" placeholder="请输入 WiFi 密码">
271

272

273
274
275

276

277	                ☁ MQTT 云平台设置
278

279

280	                MQTT 服务器地址
281	                <input type="text" id="mqtt-server" placeholder="例如: 192.168.1.100" value="192.168.1.100">
282

283

284	                MQTT 端口
285	                <input type="number" id="mqtt-port" placeholder="1883" value="1883">
286

287

288	                MQTT 用户名 (可选)
289	                <input type="text" id="mqtt-user" placeholder="用户名">
290

291

292	                MQTT 密码 (可选)
293	                <input type="password" id="mqtt-pass" placeholder="密码">
294

295

296
297
298

299

300	                🏠 环境备注
301

302

303	                设备位置 / 房间名称
304	                <input type="text" id="env-note" placeholder="例如: 主卧 / 客厅 / 老人房 / 婴儿房" value="主卧">
305

306

307	                💡 请备注此设备安装的环境位置，方便在云平台上区分不同房间的数据
308

309

310
311
312

313

314	                📱 设备设置
315

316

317	                设备名称 / ID
318	                <input type="text" id="device-id" placeholder="例如: health_001" value="health_001">
319

320

321
322
323	        <button class="btn-submit" onclick="submitConfig()">
324	            💾 保存配置并连接
325	        </button>
326	        <button class="btn-reset" onclick="resetConfig()">
327	            🔄 恢复出厂设置
328	        </button>
329
330
331

332	             等待配置...
333

334

335
336
337

338
339
460
461
462

<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>健康监测系统 - 配网设置</title>

🏥

健康监测系统

ESP32-S3 + 毫米波雷达 · 配网设置

                📶 WiFi 设置

                WiFi 名称 (SSID)
                <input type="text" id="wifi-ssid" placeholder="请输入 WiFi 名称" autocomplete="off">

                WiFi 密码
                <input type="password" id="wifi-pass" placeholder="请输入 WiFi 密码">

                ☁ MQTT 云平台设置

                MQTT 服务器地址
                <input type="text" id="mqtt-server" placeholder="例如: 192.168.1.100" value="192.168.1.100">

                MQTT 端口
                <input type="number" id="mqtt-port" placeholder="1883" value="1883">

                MQTT 用户名 (可选)
                <input type="text" id="mqtt-user" placeholder="用户名">

                MQTT 密码 (可选)
                <input type="password" id="mqtt-pass" placeholder="密码">

                🏠 环境备注

                设备位置 / 房间名称
                <input type="text" id="env-note" placeholder="例如: 主卧 / 客厅 / 老人房 / 婴儿房" value="主卧">

                💡 请备注此设备安装的环境位置，方便在云平台上区分不同房间的数据

                📱 设备设置

                设备名称 / ID
                <input type="text" id="device-id" placeholder="例如: health_001" value="health_001">

        <button class="btn-submit" onclick="submitConfig()">
            💾 保存配置并连接
        </button>
        <button class="btn-reset" onclick="resetConfig()">
            🔄 恢复出厂设置
        </button>

             等待配置...


data\index.html


OK, so now I can see the two designs:

1. **`data/index.html`** — This is the software team's design. It has a blue theme (`--primary: #4A90D9`), a clean layout with cards, WiFi settings, MQTT settings, environment note (highlighted in yellow), device settings, and submit/reset buttons.

2. **`wifi_manager.cpp`** — This has a green theme (`--primary: #12a889`), a different layout with a header showing the product name "观照-五劳无感 · 设备配网", a help modal, and the WiFiManager's standard form with custom parameters.

The user wants the WiFiManager captive portal page to match the software team's design in `data/index.html`.

But there's a key difference: the WiFiManager captive portal is served by the ESP32 itself and uses WiFiManager's built-in form handling. The `data/index.html` is a standalone page that posts to `/save` endpoint.

To match the software team's design, I need to rewrite the `setCustomMenuHTML` in wifi_manager.cpp to match the design from `data/index.html` as closely as possible.

The WiFiManager's captive portal works like this:
- It serves a page with a WiFi scan list (SSID selector) and the custom parameters
- The `setCustomMenuHTML` replaces the content inside the form
- The form submission goes to WiFiManager's built-in `/save` endpoint

So the custom HTML I write needs to:
1. Match the visual design from `data/index.html`
2. Work within WiFiManager's form structure (it doesn't replace the entire page, just the menu/content area)
3. Include WiFiManager's required elements (SSID selector, password, etc.)

Wait, actually `setCustomMenuHTML` replaces the content between the header and the form, or the entire form content? Let me check...

Actually, in WiFiManager, `setCustomMenuHTML` appends custom HTML to the page. The WiFi scan results and the standard form fields (SSID, password) are still there. The custom parameters are added to the form.

Wait, I need to re-check. Looking at the WiFiManager library:

- `setCustomHeadElement` — adds content to the `<head>` (styles, meta tags)
- `setCustomMenuHTML` — this is NOT a standard WiFiManager method! Looking at the code...

Hmm, actually `setCustomMenuHTML` doesn't exist in the standard WiFiManager library. Let me check... Actually, it might be a method that was added in version 2.0+. Let me look at what version they're using: `tzapu/WiFiManager@^2.0.17`.

Actually, I'm not sure if `setCustomMenuHTML` exists in WiFiManager 2.x. It might be a custom addition or from a fork. In any case, looking at how it's used in the code, it seems to replace/add content in the menu/form area.

But regardless of what `setCustomMenuHTML` does exactly, the user wants the look of the page to match `data/index.html`.

Actually, let me reconsider. I think `setCustomMenuHTML` might be from WiFiManager 2.x's new API. In v2.x, the API was significantly changed. Let me check.

Actually, `WiFiManagerParameter` with 4-arg constructor (name, label, value, length) is the v2.x API. The v0.x/v1.x used a different constructor. And `setCustomMenuHTML` is indeed a v2.x method.

In WiFiManager v2.x, `setCustomMenuHTML` allows replacing or augmenting the HTML content of the configuration page. The WiFiManager still handles WiFi scanning, SSID selection, form submission, etc. But you can customize the look and add additional fields.

Let me focus on what the user wants: make the provisioning page look like the software team's design (`data/index.html`).

The key design elements from `data/index.html`:
1. **Blue theme** (`#4A90D9`) instead of green (`#12a889`)
2. **Center-aligned header** with hospital emoji 🏥 and "健康监测系统" title
3. **Card-based layout** with clean white cards
4. **WiFi card** with SSID and password fields
5. **MQTT card** with server, port, username, password
6. **Environment note card** — highlighted in yellow (`#FFF9E6` background, `#FFC107` border)
7. **Device card** with device name/ID
8. **Save button** (blue, rounded) and **Reset button** (red outline)
9. **Status bar** with pulsing dot at the bottom

Now, I need to translate this into WiFiManager's `setCustomHeadElement` (for CSS) and `setCustomMenuHTML` (for the HTML structure). But I need to be careful because:

1. WiFiManager has its own SSID selector (dropdown with scanned networks) and password field
2. The form structure needs to work with WiFiManager's form handling
3. Custom parameters (MQTT server, port, etc.) need to be properly named

Actually, I think what we need to do is:
- Use `setCustomHeadElement` for the complete CSS (matching `data/index.html` style)
- Use `setCustomMenuHTML` for the custom content

But wait, WiFiManager v2.x's `setCustomMenuHTML` might replace the ENTIRE form content. Let me think about what the WiFiManager page normally looks like:

WiFiManager v2.x normally shows:
1. WiFi network scan results (SSID list or dropdown)
2. WiFi password field
3. Custom parameters
4. Save button

If `setCustomMenuHTML` replaces the entire content area including the SSID selector, then we need to include ALL the fields in our custom HTML. But then how does WiFiManager process the form submission?

Actually, in WiFiManager v2.x, the form submission goes to the root `/` or a specific endpoint, and WiFiManager parses the form data. The SSID is submitted as `s`, password as `p`, and custom parameters by their configured names.

So the custom HTML needs to:
1. Have form fields with the correct names (`s` for SSID, `p` for password, and the custom parameter names)
2. Submit to the correct endpoint
3. Match the visual design

But actually, I think `setCustomMenuHTML` might just ADD content to the menu area, not replace the entire form. In that case, the standard WiFi fields are still there.

Hmm, let me look at how the current code uses it. The current `setCustomMenuHTML` contains a lot of HTML including help modal, header, etc., but does NOT include form fields for WiFi SSID/password. Those are presumably handled by WiFiManager's default form.

Actually wait, looking more carefully at the current code... The `setCustomMenuHTML` only has the header, gateway info, help modal, and script. It does NOT have form inputs. The form inputs (WiFi SSID, password, custom parameters) are rendered by WiFiManager automatically.

So WiFiManager v2.x:
1. Renders the page with its own header
2. Inserts `setCustomMenuHTML` content in the menu area
3. Renders the form with WiFi SSID selector, password, and custom parameters
4. Renders the save button

To match the software team's design, I think the best approach is:

1. **CSS** (`setCustomHeadElement`): Match the `data/index.html` styles exactly
2. **Custom Menu HTML** (`setCustomMenuHTML`): Add the header, gateway info, and help modal matching the software team's design

But the form fields themselves (WiFi SSID, password, custom parameters) are rendered by WiFiManager, and their appearance is controlled by CSS. So I need to make sure the CSS matches `data/index.html` for all form elements.

Let me now rewrite both `setCustomHeadElement` and `setCustomMenuHTML` to match `data/index.html`.

For the setCustomHeadElement CSS, I'll match:
- Blue primary color (#4A90D9)
- Card-style form layout
- Font styles matching
- Button styles (blue submit, red outline reset)
- Form input styles

For the setCustomMenuHTML, I'll match:
- Header with 🏥 icon and "健康监测系统" title
- Help modal with the same step-by-step guide (but updated to match the blue theme)
- Gateway info

Let me write this out. This is going to be a significant edit.

Actually, I realize I need to be careful. WiFiManager's form structure generates specific HTML elements. Let me think about what CSS selectors I need to target:

WiFiManager v2.x generates:
- A network scan list (`<select>` or radio buttons for SSIDs)
- Password input for WiFi
- Custom parameter inputs (labeled by their names)
- Submit button

The CSS from `data/index.html` uses specific class names like `.card`, `.form-group`, `.btn-submit`, etc. WiFiManager doesn't use these classes. So I need to adapt the CSS to target WiFiManager's actual HTML elements (likely using element selectors or generic selectors).

Actually, I think the approach should be:
1. Write CSS that targets WiFiManager's generated elements to make them look like `data/index.html`
2. Add the custom header, gateway info, and help sections
3. Override WiFiManager's default styling

But this is really hard to do perfectly because WiFiManager generates its own HTML structure. The CSS from `data/index.html` relies on specific class names and structure that WiFiManager doesn't use.

Let me think about a different approach...

Actually, maybe the user wants me to replace the WiFiManager's page ENTIRELY with a custom page that looks like `data/index.html`. This would mean:
1. Not using WiFiManager's default form at all
2. Building a complete custom HTML page
3. Making the form submission work with WiFiManager's backend

But this would break WiFiManager's functionality (SSID scanning, network selection, etc.).

I think the practical approach is:
1. Adapt the CSS (colors, fonts, spacing) to match `data/index.html` as closely as possible
2. Adapt the layout (header, cards, etc.) to work within WiFiManager's constraints
3. Keep the help modal functionality but update its design

Let me write the updated code. I'll focus on matching the key visual elements:
- Blue color scheme (#4A90D9)
- Card-style layout
- Typography
- Button styles
- The help modal adapted to the blue theme

Let me draft the CSS first, then the custom menu HTML.

Actually, wait. Let me re-examine the WiFiManager 2.x default page structure. In WiFiManager 2.x:

The page typically has:
1. A title/header
2. WiFi network scan results (clickable list or select)
3. Password field
4. Custom parameters
5. Save button

And `setCustomMenuHTML` is documented as being able to replace the menu area. The current code puts the header, gateway info, help modal, and a script in there.

I think the key thing the user wants changed is the visual style (colors, layout) to match `data/index.html`. The current code uses a green theme (#12a889) with its own design. The software team designed a blue theme (#4A90D9) with a different layout.

Let me now write the updated code. I'll replace both the CSS (setCustomHeadElement) and the HTML content (setCustomMenuHTML) to match the software team's design from `data/index.html`.

For the CSS, I'll match `data/index.html` exactly, adapting variables and selectors as needed for WiFiManager's page structure.

For the HTML, I'll match the structure: header → WiFi card → MQTT card → env note card → device card → buttons → status bar. But since WiFiManager handles the actual form fields, I'll work with what WiFiManager generates and style it accordingly.

Let me write this now. It'll be a big edit.

Actually, one more thing to consider: `data/index.html` has specific JS functionality (LocalStorage save/load, toast notifications, status bar animation). For the ESP32 captive portal, these need to be simplified since:
- No LocalStorage on ESP32 (could use cookies though)
- The form submission works differently (WiFiManager's backend)

I'll adapt the JS to work in the captive portal context.

OK let me write the code now. I'll be thorough.


让我对比一下两个设计的差异：

| 元素 | 当前 `wifi_manager.cpp` | 软件组 `data/index.html` |
|------|------------------------|--------------------------|
| 主色 | 绿色 `#12a889` | 蓝色 `#4A90D9` |
| 标题 | 观照-五劳无感 | 🏥 健康监测系统 |
| 布局 | 单一卡片 | 多卡片分区 |
| 帮助 | 绿色弹窗 | 无弹窗(但可以保留) |

现在把配网页面改成完全匹配软件组的设计：


75	    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
76	    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
77	    _wm.setTitle("观照-五劳无感 · 设备配网");
78	    _wm.setCustomHeadElement(
79	        "<meta name='theme-color' content='#12a889'>"
80	        "<style>"
81	        ":root{--primary:#12a889;--primary-light:#35c9aa;--primary-dark:#08745f;"
82	        "--bg:#f3faf8;--card:#fff;--card-green:#e9fff8;"
83	        "--text:#15201d;--text2:#4e6660;--text3:#8aa09a;"
84	        "--border:#dcebe7;--hover:#eef8f5;--divider:#edf5f2;--radius:16px}"
85	        "*{margin:0;padding:0;box-sizing:border-box}"
86	        "body{"
87	        "  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
88	        "  background:var(--bg);min-height:100vh;padding:12px 16px 32px;"
89	        "  color:var(--text);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased"
90	        "}"
91	        /* 隐藏 WiFiManager 默认 msg (我们用自己的 header) */
92	        ".msg{display:none}"
93	        ".msg:last-of-type{display:block;background:transparent!important;box-shadow:none!important;"
94	        "  text-align:center;font-size:11px;color:var(--text3);padding:16px 0 0}"
95	        /* 表单卡片 */
96	        "form{"
97	        "  max-width:420px;margin:0 auto;position:relative;z-index:1;"
98	        "  background:var(--card);border-radius:var(--radius);padding:20px;"
99	        "  box-shadow:0 2px 12px rgba(0,0,0,0.06)"
100	        "}"
101	        "label{"
102	        "  display:block;font-size:13px;font-weight:500;color:var(--text2);"
103	        "  margin:14px 0 6px"
104	        "}"
105	        "label:first-of-type{margin-top:0}"
106	        "input[type=text],input[type=password],input[type=number]{"
107	        "  width:100%;height:44px;padding:0 14px;font-size:14px;"
108	        "  border:none;border-radius:10px;color:var(--text);"
109	        "  background:var(--hover);outline:none;transition:all 0.2s;"
110	        "  -webkit-appearance:none"
111	        "}"
112	        "input:focus{background:#fff;box-shadow:inset 0 0 0 2px var(--primary)}"
113	        "input::placeholder{color:var(--text3)}"
114	        "button[type=submit]{"
115	        "  width:100%;height:48px;margin-top:20px;"
116	        "  border:none;border-radius:24px;font-size:15px;font-weight:600;"
117	        "  color:#fff;background:var(--primary);"
118	        "  box-shadow:0 2px 6px rgba(18,168,137,0.2);"
119	        "  cursor:pointer;transition:all 0.15s"
120	        "}"
121	        "button:active{transform:scale(0.98);opacity:0.92}"
122	        /* 引导弹窗 */
123	        ".modal-mask{"
124	        "  position:fixed;inset:0;background:rgba(15,31,28,0.55);"
125	        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
126	        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
127	        "}"
128	        ".modal-mask.open{opacity:1;visibility:visible}"
129	        ".modal{"
130	        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
131	        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
132	        "  transform:scale(0.85) translateY(20px);opacity:0;"
133	        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
134	        "}"
135	        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
136	        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--divider)}"
137	        ".mod-icon{width:40px;height:40px;border-radius:12px;background:var(--card-green);"
138	        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
139	        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--hover);"
140	        "  color:var(--text3);font-size:16px;display:flex;align-items:center;justify-content:center;cursor:pointer}"
141	        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
142	        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--hover)}"
143	        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
144	        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
145	        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--divider)}"
146	        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
147	        ".mod-btn .pri{background:var(--primary);color:#fff}"
148	        ".mod-btn .ghost{background:var(--hover);color:var(--text2)}"
149	        "@media(prefers-color-scheme:dark){"
150	        "  body{background:#0f1f1c;color:#e6f0ee}"
151	        "  form,.modal{background:#182b27;box-shadow:0 2px 12px rgba(0,0,0,0.4)}"
152	        "  input[type=text],input[type=password],input[type=number]{background:#1f3530;color:#e6f0ee}"
153	        "  input:focus{background:#243d38;box-shadow:inset 0 0 0 2px var(--primary-light)}"
154	        "  label{color:#a8bdb8}"
155	        "  button[type=submit]{box-shadow:0 2px 6px rgba(18,168,137,0.35)}"
156	        "}"
157	        "</style>"
158	    );
159

    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
    _wm.setTitle("观照-五劳无感 · 设备配网");
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#12a889'>"
        "<style>"
        ":root{--primary:#12a889;--primary-light:#35c9aa;--primary-dark:#08745f;"
        "--bg:#f3faf8;--card:#fff;--card-green:#e9fff8;"
        "--text:#15201d;--text2:#4e6660;--text3:#8aa09a;"
        "--border:#dcebe7;--hover:#eef8f5;--divider:#edf5f2;--radius:16px}"
        "*{margin:0;padding:0;box-sizing:border-box}"
        "body{"
        "  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
        "  background:var(--bg);min-height:100vh;padding:12px 16px 32px;"
        "  color:var(--text);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased"
        "}"
        /* 隐藏 WiFiManager 默认 msg (我们用自己的 header) */
        ".msg{display:none}"
        ".msg:last-of-type{display:block;background:transparent!important;box-shadow:none!important;"
        "  text-align:center;font-size:11px;color:var(--text3);padding:16px 0 0}"
        /* 表单卡片 */
        "form{"
        "  max-width:420px;margin:0 auto;position:relative;z-index:1;"
        "  background:var(--card);border-radius:var(--radius);padding:20px;"
        "  box-shadow:0 2px 12px rgba(0,0,0,0.06)"
        "}"
        "label{"
        "  display:block;font-size:13px;font-weight:500;color:var(--text2);"
        "  margin:14px 0 6px"
        "}"
        "label:first-of-type{margin-top:0}"
        "input[type=text],input[type=password],input[type=number]{"
        "  width:100%;height:44px;padding:0 14px;font-size:14px;"
        "  border:none;border-radius:10px;color:var(--text);"
        "  background:var(--hover);outline:none;transition:all 0.2s;"
        "  -webkit-appearance:none"
        "}"
        "input:focus{background:#fff;box-shadow:inset 0 0 0 2px var(--primary)}"
        "input::placeholder{color:var(--text3)}"
        "button[type=submit]{"
        "  width:100%;height:48px;margin-top:20px;"
        "  border:none;border-radius:24px;font-size:15px;font-weight:600;"
        "  color:#fff;background:var(--primary);"
        "  box-shadow:0 2px 6px rgba(18,168,137,0.2);"
        "  cursor:pointer;transition:all 0.15s"
        "}"
        "button:active{transform:scale(0.98);opacity:0.92}"
        /* 引导弹窗 */
        ".modal-mask{"
        "  position:fixed;inset:0;background:rgba(15,31,28,0.55);"
        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
        "}"
        ".modal-mask.open{opacity:1;visibility:visible}"
        ".modal{"
        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
        "  transform:scale(0.85) translateY(20px);opacity:0;"
        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
        "}"
        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--divider)}"
        ".mod-icon{width:40px;height:40px;border-radius:12px;background:var(--card-green);"
        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--hover);"
        "  color:var(--text3);font-size:16px;display:flex;align-items:center;justify-content:center;cursor:pointer}"
        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--hover)}"
        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--divider)}"
        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
        ".mod-btn .pri{background:var(--primary);color:#fff}"
        ".mod-btn .ghost{background:var(--hover);color:var(--text2)}"
        "@media(prefers-color-scheme:dark){"
        "  body{background:#0f1f1c;color:#e6f0ee}"
        "  form,.modal{background:#182b27;box-shadow:0 2px 12px rgba(0,0,0,0.4)}"
        "  input[type=text],input[type=password],input[type=number]{background:#1f3530;color:#e6f0ee}"
        "  input:focus{background:#243d38;box-shadow:inset 0 0 0 2px var(--primary-light)}"
        "  label{color:#a8bdb8}"
        "  button[type=submit]{box-shadow:0 2px 6px rgba(18,168,137,0.35)}"
        "}"
        "</style>"
    );


OK, now I have a clear picture of both designs. Let me write the new CSS and HTML that matches the software team's `data/index.html` design.

The software team's design key characteristics:
1. Blue theme (`#4A90D9` primary)
2. Clean white background (`#F5F7FA`)
3. Card-based layout with white cards, rounded corners, subtle shadows
4. Form inputs with light background and blue focus border
5. Environment note card highlighted in yellow
6. Blue submit button with `letter-spacing`
7. Red outline reset button
8. Status bar with pulsing dot
9. No dark mode (the `data/index.html` doesn't have a dark mode)

I need to:
1. Replace the CSS in `setCustomHeadElement` to match
2. Replace the `setCustomMenuHTML` content to match the layout from `data/index.html`
3. Keep the help modal but adapt it to the blue theme
4. Keep WiFiManager's form functionality (SSID scan, password, custom parameters)

Let me write this out. The key challenge is that WiFiManager generates its own form elements. I need to style them to match `data/index.html`.

WiFiManager v2.x generates something like:
- A `<form>` with inputs
- SSID as `<input name="s">` or a select/radio list
- Password as `<input name="p" type="password">`
- Custom parameters as `<input>` elements
- Submit button

The custom menu HTML from `setCustomMenuHTML` is inserted into the page. In WiFiManager v2.x, it appears before/between/after the form elements depending on the version.

Actually, I think `setCustomMenuHTML` just adds content to the page. The form elements are still generated by WiFiManager.

Let me look at this practically. The current code's `setCustomMenuHTML` has:
- A header div
- A gateway info div
- A help modal
- CSS animation for the help button
- JavaScript for the modal toggle

It does NOT have form inputs — those are from WiFiManager. The CSS in `setCustomHeadElement` styles the form, inputs, labels, and buttons that WiFiManager generates.

So my plan:
1. CSS: Match `data/index.html` styles, adapted for WiFiManager's element structure
2. Menu HTML: Header matching `data/index.html` + help modal adapted to blue theme

Let me write both now.

For the CSS, I need to match:
- `body` background: `#F5F7FA`
- Cards: WiFiManager's form IS the "card" → style it as a white card
- Inputs: style to match `data/index.html`
- Labels: style to match
- Submit button: blue, rounded
- Custom parameters section labels: styled as card titles

For the HTML, I'll create:
- Header section matching `data/index.html` (centered, 🏥 icon, title, subtitle)
- Help modal adapted to blue theme
- Gateway info

Let me code this.

Actually, I realize I should keep the help modal since it's useful for users, and the software team might not have designed one. I'll adapt it to the blue theme.

Also, I need to note that WiFiManager renders network scan results (a list of SSIDs or a select dropdown). I'll need to style those too. In WiFiManager v2.x, the SSID selection is typically rendered as a `<select>` dropdown or as a list of radio buttons.

Let me be practical and write the code now.

For the setCustomHeadElement, I'll match data/index.html's CSS exactly.

For the setCustomMenuHTML, I'll match data/index.html's HTML structure, adapted to work within WiFiManager's constraints.

Wait, actually, the more I think about it, the more I realize that `setCustomMenuHTML` in WiFiManager v2.x might work differently than I think. Let me just focus on making the visual appearance match.

I'll write both the CSS and HTML now.

One more thing — the `data/index.html` has WiFi SSID as a text input, but WiFiManager generates a select/scan list. The styling should handle both. For the SSID field, I'll style it to match the text input look.

OK, let me write this now.


现在把 `setCustomHeadElement` 和 `setCustomMenuHTML` 全面替换为软件组的设计：


    _wm.setTitle("观照-五劳无感 · 设备配网");
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#12a889'>"
        "<style>"
        ":root{--primary:#12a889;--primary-light:#35c9aa;--primary-dark:#08745f;"
        "--bg:#f3faf8;--card:#fff;--card-green:#e9fff8;"
        "--text:#15201d;--text2:#4e6660;--text3:#8aa09a;"
        "--border:#dcebe7;--hover:#eef8f5;--divider:#edf5f2;--radius:16px}"
        "*{margin:0;padding:0;box-sizing:border-box}"
        "body{"
        "  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
        "  background:var(--bg);min-height:100vh;padding:12px 16px 32px;"
        "  color:var(--text);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased"
        "}"
        /* 隐藏 WiFiManager 默认 msg (我们用自己的 header) */
        ".msg{display:none}"
        ".msg:last-of-type{display:block;background:transparent!important;box-shadow:none!important;"
        "  text-align:center;font-size:11px;color:var(--text3);padding:16px 0 0}"
        /* 表单卡片 */
        "form{"
        "  max-width:420px;margin:0 auto;position:relative;z-index:1;"
        "  background:var(--card);border-radius:var(--radius);padding:20px;"
        "  box-shadow:0 2px 12px rgba(0,0,0,0.06)"
        "}"
        "label{"
        "  display:block;font-size:13px;font-weight:500;color:var(--text2);"
        "  margin:14px 0 6px"
        "}"
        "label:first-of-type{margin-top:0}"
        "input[type=text],input[type=password],input[type=number]{"
        "  width:100%;height:44px;padding:0 14px;font-size:14px;"
        "  border:none;border-radius:10px;color:var(--text);"
        "  background:var(--hover);outline:none;transition:all 0.2s;"
        "  -webkit-appearance:none"
        "}"
        "input:focus{background:#fff;box-shadow:inset 0 0 0 2px var(--primary)}"
        "input::placeholder{color:var(--text3)}"
        "button[type=submit]{"
        "  width:100%;height:48px;margin-top:20px;"
        "  border:none;border-radius:24px;font-size:15px;font-weight:600;"
        "  color:#fff;background:var(--primary);"
        "  box-shadow:0 2px 6px rgba(18,168,137,0.2);"
        "  cursor:pointer;transition:all 0.15s"
        "}"
        "button:active{transform:scale(0.98);opacity:0.92}"
        /* 引导弹窗 */
        ".modal-mask{"
        "  position:fixed;inset:0;background:rgba(15,31,28,0.55);"
        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
        "}"
        ".modal-mask.open{opacity:1;visibility:visible}"
        ".modal{"
        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
        "  transform:scale(0.85) translateY(20px);opacity:0;"
        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
        "}"
        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--divider)}"
        ".mod-icon{width:40px;height:40px;border-radius:12px;background:var(--card-green);"
        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--hover);"
        "  color:var(--text3);font-size:16px;display:flex;align-items:center;justify-content:center;cursor:pointer}"
        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--hover)}"
        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--divider)}"
        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
        ".mod-btn .pri{background:var(--primary);color:#fff}"
        ".mod-btn .ghost{background:var(--hover);color:var(--text2)}"
        "@media(prefers-color-scheme:dark){"
        "  body{background:#0f1f1c;color:#e6f0ee}"
        "  form,.modal{background:#182b27;box-shadow:0 2px 12px rgba(0,0,0,0.4)}"
        "  input[type=text],input[type=password],input[type=number]{background:#1f3530;color:#e6f0ee}"
        "  input:focus{background:#243d38;box-shadow:inset 0 0 0 2px var(--primary-light)}"
        "  label{color:#a8bdb8}"
        "  button[type=submit]{box-shadow:0 2px 6px rgba(18,168,137,0.35)}"
        "}"
        "</style>"
    );

    _wm.setTitle("健康监测系统 - 配网设置");
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#4A90D9'>"
        "<style>"
        /* ======== 软件组设计规范 ======== */
        ":root{"
        "--primary:#4A90D9;--danger:#E74C3C;--success:#27AE60;"
        "--bg:#F5F7FA;--card-bg:#FFFFFF;--text:#2C3E50;--text-light:#7F8C8D;"
        "--border:#E1E8ED;--radius:12px"
        "}"
        "*{margin:0;padding:0;box-sizing:border-box}"
        "body{"
        "  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
        "  background:var(--bg);min-height:100vh;padding:16px;"
        "  color:var(--text);font-size:14px;line-height:1.5;"
        "  display:flex;flex-direction:column;align-items:center;-webkit-font-smoothing:antialiased"
        "}"
        /* 隐藏 WiFiManager 默认 msg */
        ".msg{display:none}"
        ".msg:last-of-type{display:block;background:transparent!important;box-shadow:none!important;"
        "  text-align:center;font-size:12px;color:var(--text-light);padding:16px 0 0}"
        /* 容器 */
        "form{"
        "  max-width:420px;width:100%;margin:0 auto;position:relative;z-index:1"
        "}"
        /* 卡片分区 — WiFiManager 生成的每个参数组 */
        "form>div,form>p,form>.wifi-select-group{"
        "  background:var(--card-bg);border-radius:var(--radius);"
        "  padding:20px;margin-bottom:12px;"
        "  box-shadow:0 1px 3px rgba(0,0,0,0.08)"
        "}"
        /* 卡片标题 */
        "label{"
        "  display:block;font-size:13px;font-weight:600;color:var(--text-light);"
        "  margin-bottom:6px;text-transform:uppercase;letter-spacing:0.5px"
        "}"
        "label:first-of-type{margin-top:0}"
        /* 输入框 */
        "input[type=text],input[type=password],input[type=number],select{"
        "  width:100%;height:44px;padding:0 14px;font-size:15px;"
        "  border:2px solid var(--border);border-radius:8px;color:var(--text);"
        "  background:var(--bg);outline:none;transition:border-color 0.2s,box-shadow 0.2s;"
        "  -webkit-appearance:none;appearance:none"
        "}"
        "input:focus,select:focus{"
        "  border-color:var(--primary);"
        "  box-shadow:0 0 0 3px rgba(74,144,217,0.15);"
        "  background:var(--card-bg)"
        "}"
        "input::placeholder{color:#BDC3C7}"
        /* WiFi SSID 选择列表 */
        ".wifi-list{list-style:none;padding:0}"
        ".wifi-list li{margin-bottom:8px}"
        ".wifi-list label{"
        "  display:flex;align-items:center;gap:10px;padding:12px 14px;"
        "  background:var(--bg);border:2px solid var(--border);border-radius:8px;"
        "  cursor:pointer;transition:all 0.2s;font-size:14px;text-transform:none;letter-spacing:0;"
        "  font-weight:400;color:var(--text);margin-bottom:0"
        "}"
        ".wifi-list label:has(input:checked){"
        "  border-color:var(--primary);background:rgba(74,144,217,0.05)"
        "}"
        ".wifi-list input[type=radio]{accent-color:var(--primary);width:18px;height:18px}"
        /* 提交按钮 */
        "button[type=submit]{"
        "  width:100%;height:50px;margin-top:8px;"
        "  border:none;border-radius:var(--radius);font-size:17px;font-weight:600;"
        "  color:#fff;background:var(--primary);"
        "  cursor:pointer;transition:background 0.2s,transform 0.1s;letter-spacing:1px"
        "}"
        "button[type=submit]:active{transform:scale(0.98);background:#357ABD}"
        "button[type=submit]:hover{background:#3D7EC0}"
        /* 帮助按钮 (浮动) */
        ".help-float{"
        "  position:fixed;right:16px;bottom:24px;z-index:50;"
        "  width:48px;height:48px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:20px;font-weight:700;display:flex;align-items:center;justify-content:center;"
        "  cursor:pointer;box-shadow:0 4px 16px rgba(74,144,217,0.35);"
        "  border:none;animation:helpPulse 1.6s ease-in-out infinite"
        "}"
        "@keyframes helpPulse{0%,100%{box-shadow:0 4px 16px rgba(74,144,217,0.35)}"
        "50%{box-shadow:0 4px 28px rgba(74,144,217,0.6)}}"
        /* 引导弹窗 */
        ".modal-mask{"
        "  position:fixed;inset:0;background:rgba(0,0,0,0.45);"
        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
        "}"
        ".modal-mask.open{opacity:1;visibility:visible}"
        ".modal{"
        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
        "  transform:scale(0.85) translateY(20px);opacity:0;"
        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
        "}"
        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--border)}"
        ".mod-icon{width:40px;height:40px;border-radius:12px;background:rgba(74,144,217,0.1);"
        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--bg);"
        "  color:var(--text-light);font-size:16px;display:flex;align-items:center;justify-content:center;"
        "  cursor:pointer;border:none;margin-left:auto}"
        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--bg)}"
        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--border)}"
        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
        ".mod-btn .pri{background:var(--primary);color:#fff}"
        ".mod-btn .ghost{background:var(--bg);color:var(--text-light)}"
        "</style>"
    );


    _wm.setTitle("观照-五劳无感 · 设备配网");
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#12a889'>"
        "<style>"
        ":root{--primary:#12a889;--primary-light:#35c9aa;--primary-dark:#08745f;"
        "--bg:#f3faf8;--card:#fff;--card-green:#e9fff8;"
        "--text:#15201d;--text2:#4e6660;--text3:#8aa09a;"
        "--border:#dcebe7;--hover:#eef8f5;--divider:#edf5f2;--radius:16px}"
        "*{margin:0;padding:0;box-sizing:border-box}"
        "body{"
        "  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
        "  background:var(--bg);min-height:100vh;padding:12px 16px 32px;"
        "  color:var(--text);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased"
        "}"
        /* 隐藏 WiFiManager 默认 msg (我们用自己的 header) */
        ".msg{display:none}"
        ".msg:last-of-type{display:block;background:transparent!important;box-shadow:none!important;"
        "  text-align:center;font-size:11px;color:var(--text3);padding:16px 0 0}"
        /* 表单卡片 */
        "form{"
        "  max-width:420px;margin:0 auto;position:relative;z-index:1;"
        "  background:var(--card);border-radius:var(--radius);padding:20px;"
        "  box-shadow:0 2px 12px rgba(0,0,0,0.06)"
        "}"
        "label{"
        "  display:block;font-size:13px;font-weight:500;color:var(--text2);"
        "  margin:14px 0 6px"
        "}"
        "label:first-of-type{margin-top:0}"
        "input[type=text],input[type=password],input[type=number]{"
        "  width:100%;height:44px;padding:0 14px;font-size:14px;"
        "  border:none;border-radius:10px;color:var(--text);"
        "  background:var(--hover);outline:none;transition:all 0.2s;"
        "  -webkit-appearance:none"
        "}"
        "input:focus{background:#fff;box-shadow:inset 0 0 0 2px var(--primary)}"
        "input::placeholder{color:var(--text3)}"
        "button[type=submit]{"
        "  width:100%;height:48px;margin-top:20px;"
        "  border:none;border-radius:24px;font-size:15px;font-weight:600;"
        "  color:#fff;background:var(--primary);"
        "  box-shadow:0 2px 6px rgba(18,168,137,0.2);"
        "  cursor:pointer;transition:all 0.15s"
        "}"
        "button:active{transform:scale(0.98);opacity:0.92}"
        /* 引导弹窗 */
        ".modal-mask{"
        "  position:fixed;inset:0;background:rgba(15,31,28,0.55);"
        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
        "}"
        ".modal-mask.open{opacity:1;visibility:visible}"
        ".modal{"
        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
        "  transform:scale(0.85) translateY(20px);opacity:0;"
        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
        "}"
        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--divider)}"
        ".mod-icon{width:40px;height:40px;border-radius:12px;background:var(--card-green);"
        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--hover);"
        "  color:var(--text3);font-size:16px;display:flex;align-items:center;justify-content:center;cursor:pointer}"
        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--hover)}"
        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--divider)}"
        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
        ".mod-btn .pri{background:var(--primary);color:#fff}"
        ".mod-btn .ghost{background:var(--hover);color:var(--text2)}"
        "@media(prefers-color-scheme:dark){"
        "  body{background:#0f1f1c;color:#e6f0ee}"
        "  form,.modal{background:#182b27;box-shadow:0 2px 12px rgba(0,0,0,0.4)}"
        "  input[type=text],input[type=password],input[type=number]{background:#1f3530;color:#e6f0ee}"
        "  input:focus{background:#243d38;box-shadow:inset 0 0 0 2px var(--primary-light)}"
        "  label{color:#a8bdb8}"
        "  button[type=submit]{box-shadow:0 2px 6px rgba(18,168,137,0.35)}"
        "}"
        "</style>"
    );

    _wm.setTitle("健康监测系统 - 配网设置");
    _wm.setCustomHeadElement(
        "<meta name='theme-color' content='#4A90D9'>"
        "<style>"
        /* ======== 软件组设计规范 ======== */
        ":root{"
        "--primary:#4A90D9;--danger:#E74C3C;--success:#27AE60;"
        "--bg:#F5F7FA;--card-bg:#FFFFFF;--text:#2C3E50;--text-light:#7F8C8D;"
        "--border:#E1E8ED;--radius:12px"
        "}"
        "*{margin:0;padding:0;box-sizing:border-box}"
        "body{"
        "  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;"
        "  background:var(--bg);min-height:100vh;padding:16px;"
        "  color:var(--text);font-size:14px;line-height:1.5;"
        "  display:flex;flex-direction:column;align-items:center;-webkit-font-smoothing:antialiased"
        "}"
        /* 隐藏 WiFiManager 默认 msg */
        ".msg{display:none}"
        ".msg:last-of-type{display:block;background:transparent!important;box-shadow:none!important;"
        "  text-align:center;font-size:12px;color:var(--text-light);padding:16px 0 0}"
        /* 容器 */
        "form{"
        "  max-width:420px;width:100%;margin:0 auto;position:relative;z-index:1"
        "}"
        /* 卡片分区 — WiFiManager 生成的每个参数组 */
        "form>div,form>p,form>.wifi-select-group{"
        "  background:var(--card-bg);border-radius:var(--radius);"
        "  padding:20px;margin-bottom:12px;"
        "  box-shadow:0 1px 3px rgba(0,0,0,0.08)"
        "}"
        /* 卡片标题 */
        "label{"
        "  display:block;font-size:13px;font-weight:600;color:var(--text-light);"
        "  margin-bottom:6px;text-transform:uppercase;letter-spacing:0.5px"
        "}"
        "label:first-of-type{margin-top:0}"
        /* 输入框 */
        "input[type=text],input[type=password],input[type=number],select{"
        "  width:100%;height:44px;padding:0 14px;font-size:15px;"
        "  border:2px solid var(--border);border-radius:8px;color:var(--text);"
        "  background:var(--bg);outline:none;transition:border-color 0.2s,box-shadow 0.2s;"
        "  -webkit-appearance:none;appearance:none"
        "}"
        "input:focus,select:focus{"
        "  border-color:var(--primary);"
        "  box-shadow:0 0 0 3px rgba(74,144,217,0.15);"
        "  background:var(--card-bg)"
        "}"
        "input::placeholder{color:#BDC3C7}"
        /* WiFi SSID 选择列表 */
        ".wifi-list{list-style:none;padding:0}"
        ".wifi-list li{margin-bottom:8px}"
        ".wifi-list label{"
        "  display:flex;align-items:center;gap:10px;padding:12px 14px;"
        "  background:var(--bg);border:2px solid var(--border);border-radius:8px;"
        "  cursor:pointer;transition:all 0.2s;font-size:14px;text-transform:none;letter-spacing:0;"
        "  font-weight:400;color:var(--text);margin-bottom:0"
        "}"
        ".wifi-list label:has(input:checked){"
        "  border-color:var(--primary);background:rgba(74,144,217,0.05)"
        "}"
        ".wifi-list input[type=radio]{accent-color:var(--primary);width:18px;height:18px}"
        /* 提交按钮 */
        "button[type=submit]{"
        "  width:100%;height:50px;margin-top:8px;"
        "  border:none;border-radius:var(--radius);font-size:17px;font-weight:600;"
        "  color:#fff;background:var(--primary);"
        "  cursor:pointer;transition:background 0.2s,transform 0.1s;letter-spacing:1px"
        "}"
        "button[type=submit]:active{transform:scale(0.98);background:#357ABD}"
        "button[type=submit]:hover{background:#3D7EC0}"
        /* 帮助按钮 (浮动) */
        ".help-float{"
        "  position:fixed;right:16px;bottom:24px;z-index:50;"
        "  width:48px;height:48px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:20px;font-weight:700;display:flex;align-items:center;justify-content:center;"
        "  cursor:pointer;box-shadow:0 4px 16px rgba(74,144,217,0.35);"
        "  border:none;animation:helpPulse 1.6s ease-in-out infinite"
        "}"
        "@keyframes helpPulse{0%,100%{box-shadow:0 4px 16px rgba(74,144,217,0.35)}"
        "50%{box-shadow:0 4px 28px rgba(74,144,217,0.6)}}"
        /* 引导弹窗 */
        ".modal-mask{"
        "  position:fixed;inset:0;background:rgba(0,0,0,0.45);"
        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
        "}"
        ".modal-mask.open{opacity:1;visibility:visible}"
        ".modal{"
        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
        "  transform:scale(0.85) translateY(20px);opacity:0;"
        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
        "}"
        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--border)}"
        ".mod-icon{width:40px;height:40px;border-radius:12px;background:rgba(74,144,217,0.1);"
        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--bg);"
        "  color:var(--text-light);font-size:16px;display:flex;align-items:center;justify-content:center;"
        "  cursor:pointer;border:none;margin-left:auto}"
        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--bg)}"
        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--border)}"
        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
        ".mod-btn .pri{background:var(--primary);color:#fff}"
        ".mod-btn .ghost{background:var(--bg);color:var(--text-light)}"
        "</style>"
    );

_wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)

-        "<meta name='theme-color' content='#12a889'>"

+        "<meta name='theme-color' content='#4A90D9'>"

-        ":root{--primary:#12a889;--primary-light:#35c9aa;--primary-dark:#08745f;"

-        "--bg:#f3faf8;--card:#fff;--card-green:#e9fff8;"

-        "--border:#dcebe7;--hover:#eef8f5;--divider:#edf5f2;--radius:16px}"

+        "--bg:#F5F7FA;--card-bg:#FFFFFF;--text:#2C3E50;--text-light:#7F8C8D;"

+        "--border:#E1E8ED;--radius:12px"

-        "  background:var(--bg);min-height:100vh;padding:12px 16px 32px;"

-        "  color:var(--text);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased"

+        "  background:var(--bg);min-height:100vh;padding:16px;"

+        "  color:var(--text);font-size:14px;line-height:1.5;"

+        "  display:flex;flex-direction:column;align-items:center;-webkit-font-smoothing:antialiased"

-        /* 隐藏 WiFiManager 默认 msg (我们用自己的 header) */

-        "  text-align:center;font-size:11px;color:var(--text3);padding:16px 0 0}"

+        "  text-align:center;font-size:12px;color:var(--text-light);padding:16px 0 0}"

-        "  max-width:420px;margin:0 auto;position:relative;z-index:1;"

-        "  background:var(--card);border-radius:var(--radius);padding:20px;"

+        "  max-width:420px;width:100%;margin:0 auto;position:relative;z-index:1"

+        /* 卡片分区 — WiFiManager 生成的每个参数组 */

+        "form>div,form>p,form>.wifi-select-group{"

+        "  background:var(--card-bg);border-radius:var(--radius);"

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

-        "  display:block;font-size:13px;font-weight:500;color:var(--text2);"

+        "  display:block;font-size:13px;font-weight:600;color:var(--text-light);"

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

-        "input[type=text],input[type=password],input[type=number]{"

-        "  width:100%;height:44px;padding:0 14px;font-size:14px;"

-        "  border:none;border-radius:10px;color:var(--text);"

-        "  background:var(--hover);outline:none;transition:all 0.2s;"

+        "input[type=text],input[type=password],input[type=number],select{"

+        "  width:100%;height:44px;padding:0 14px;font-size:15px;"

+        "  border:2px solid var(--border);border-radius:8px;color:var(--text);"

+        "  background:var(--bg);outline:none;transition:border-color 0.2s,box-shadow 0.2s;"

+        "  -webkit-appearance:none;appearance:none"

-        "input:focus{background:#fff;box-shadow:inset 0 0 0 2px var(--primary)}"

-        "input::placeholder{color:var(--text3)}"

+        "  border-color:var(--primary);"

+        "input::placeholder{color:#BDC3C7}"

+        ".wifi-list{list-style:none;padding:0}"

+        ".wifi-list li{margin-bottom:8px}"

+        "  display:flex;align-items:center;gap:10px;padding:12px 14px;"

+        "  background:var(--bg);border:2px solid var(--border);border-radius:8px;"

+        "  cursor:pointer;transition:all 0.2s;font-size:14px;text-transform:none;letter-spacing:0;"

+        "  font-weight:400;color:var(--text);margin-bottom:0"

+        ".wifi-list label:has(input:checked){"

+        "  border-color:var(--primary);background:rgba(74,144,217,0.05)"

+        ".wifi-list input[type=radio]{accent-color:var(--primary);width:18px;height:18px}"

-        "  width:100%;height:48px;margin-top:20px;"

-        "  border:none;border-radius:24px;font-size:15px;font-weight:600;"

+        "  width:100%;height:50px;margin-top:8px;"

+        "  border:none;border-radius:var(--radius);font-size:17px;font-weight:600;"

-        "  cursor:pointer;transition:all 0.15s"

+        "  cursor:pointer;transition:background 0.2s,transform 0.1s;letter-spacing:1px"

-        "button:active{transform:scale(0.98);opacity:0.92}"

+        "button[type=submit]:active{transform:scale(0.98);background:#357ABD}"

+        "button[type=submit]:hover{background:#3D7EC0}"

+        "  position:fixed;right:16px;bottom:24px;z-index:50;"

+        "  width:48px;height:48px;border-radius:50%;background:var(--primary);color:#fff;"

+        "  font-size:20px;font-weight:700;display:flex;align-items:center;justify-content:center;"

+        "  cursor:pointer;box-shadow:0 4px 16px rgba(74,144,217,0.35);"

+        "  border:none;animation:helpPulse 1.6s ease-in-out infinite"

-        "  position:fixed;inset:0;background:rgba(15,31,28,0.55);"

+        "  position:fixed;inset:0;background:rgba(0,0,0,0.45);"

-        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--divider)}"

-        ".mod-icon{width:40px;height:40px;border-radius:12px;background:var(--card-green);"

+        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--border)}"

+        ".mod-icon{width:40px;height:40px;border-radius:12px;background:rgba(74,144,217,0.1);"

-        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--hover);"

-        "  color:var(--text3);font-size:16px;display:flex;align-items:center;justify-content:center;cursor:pointer}"

+        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--bg);"

+        "  color:var(--text-light);font-size:16px;display:flex;align-items:center;justify-content:center;"

+        "  cursor:pointer;border:none;margin-left:auto}"

-        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--hover)}"

+        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--bg)}"

-        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--divider)}"

+        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--border)}"

-        ".mod-btn .ghost{background:var(--hover);color:var(--text2)}"

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

-        "  body{background:#0f1f1c;color:#e6f0ee}"

-        "  form,.modal{background:#182b27;box-shadow:0 2px 12px rgba(0,0,0,0.4)}"

-        "  input[type=text],input[type=password],input[type=number]{background:#1f3530;color:#e6f0ee}"

-        "  input:focus{background:#243d38;box-shadow:inset 0 0 0 2px var(--primary-light)}"

-        "  button[type=submit]{box-shadow:0 2px 6px rgba(18,168,137,0.35)}"

+        ".mod-btn .ghost{background:var(--bg);color:var(--text-light)}"


Good. Now I need to update the `setCustomMenuHTML` section to match the software team's design. Let me write the HTML that matches `data/index.html` while still working with WiFiManager's form handling.

The custom menu HTML should include:
1. Header matching `data/index.html` (🏥 icon, title, subtitle)
2. Help modal adapted to the blue theme
3. Floating help button
4. Status bar

The WiFi SSID selection, password field, and custom parameters will be generated by WiFiManager.


现在替换 `setCustomMenuHTML` 为软件组的设计：


156	        "}"
157	        "@keyframes helpPulse{0%,100%{box-shadow:0 4px 16px rgba(74,144,217,0.35)}"
158	        "50%{box-shadow:0 4px 28px rgba(74,144,217,0.6)}}"
159	        /* 引导弹窗 */
160	        ".modal-mask{"
161	        "  position:fixed;inset:0;background:rgba(0,0,0,0.45);"
162	        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
163	        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
164	        "}"
165	        ".modal-mask.open{opacity:1;visibility:visible}"
166	        ".modal{"
167	        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
168	        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
169	        "  transform:scale(0.85) translateY(20px);opacity:0;"
170	        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
171	        "}"
172	        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
173	        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--border)}"
174	        ".mod-icon{width:40px;height:40px;border-radius:12px;background:rgba(74,144,217,0.1);"
175	        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
176	        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--bg);"
177	        "  color:var(--text-light);font-size:16px;display:flex;align-items:center;justify-content:center;"
178	        "  cursor:pointer;border:none;margin-left:auto}"
179	        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
180	        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--bg)}"
181	        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
182	        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
183	        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--border)}"
184	        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
185	        ".mod-btn .pri{background:var(--primary);color:#fff}"
186	        ".mod-btn .ghost{background:var(--bg);color:var(--text-light)}"
187	        "</style>"
188	    );
189
190	    // 自定义菜单 HTML — 软件组设计的头部 + 引导弹窗
191	    {
192	        String html = String("")
193	            + "
"
194	            /* 头部 */
195	            + "
"
196	            + "
📡
"
199	            + "

观照-五劳无感 · 设备配网
"
200	            + "
WiFi 联网与参数配置

"
201	            /* 帮助按钮 */
202	            + "
?
"
206	            + "
"
207	            /* 网关信息 */
208	            + "
"
210	            + "📍 当前网关"
211	            + "Gateway
"
212	            + "
"
213	            /* 引导弹窗 */
214	            + "
"
215	            + "
"

"}"
        "@keyframes helpPulse{0%,100%{box-shadow:0 4px 16px rgba(74,144,217,0.35)}"
        "50%{box-shadow:0 4px 28px rgba(74,144,217,0.6)}}"
        /* 引导弹窗 */
        ".modal-mask{"
        "  position:fixed;inset:0;background:rgba(0,0,0,0.45);"
        "  backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;"
        "  opacity:0;visibility:hidden;transition:opacity 0.25s,visibility 0.25s;z-index:100;padding:20px"
        "}"
        ".modal-mask.open{opacity:1;visibility:visible}"
        ".modal{"
        "  width:100%;max-width:360px;background:#fff;border-radius:20px;"
        "  padding:24px 22px 18px;box-shadow:0 12px 40px rgba(0,0,0,0.2);"
        "  transform:scale(0.85) translateY(20px);opacity:0;"
        "  transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),opacity 0.25s;max-height:80vh;overflow-y:auto"
        "}"
        ".modal-mask.open .modal{transform:scale(1) translateY(0);opacity:1}"
        ".mod-hd{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--border)}"
        ".mod-icon{width:40px;height:40px;border-radius:12px;background:rgba(74,144,217,0.1);"
        "  display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}"
        ".mod-close{width:28px;height:28px;border-radius:50%;background:var(--bg);"
        "  color:var(--text-light);font-size:16px;display:flex;align-items:center;justify-content:center;"
        "  cursor:pointer;border:none;margin-left:auto}"
        ".mod-steps{display:flex;flex-direction:column;gap:10px;padding:20px 0 22px}"
        ".step{display:flex;align-items:flex-start;gap:14px;padding:12px;border-radius:12px;background:var(--bg)}"
        ".step-num{width:30px;height:30px;border-radius:50%;background:var(--primary);color:#fff;"
        "  font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}"
        ".mod-btn{display:flex;gap:10px;padding-top:14px;border-top:1px solid var(--border)}"
        ".mod-btn button{flex:1;height:42px;font-size:14px;border:none;border-radius:21px;cursor:pointer;font-weight:600;font-family:inherit}"
        ".mod-btn .pri{background:var(--primary);color:#fff}"
        ".mod-btn .ghost{background:var(--bg);color:var(--text-light)}"
        "</style>"
    );

    // 自定义菜单 HTML — 软件组设计的头部 + 引导弹窗
    {
        String html = String("")
            + "
"
            /* 头部 */
            + "
"
            + "
📡
"
            + "

观照-五劳无感 · 设备配网
"
            + "
WiFi 联网与参数配置

"
            /* 帮助按钮 */
            + "
?
"
            + "
"
            /* 网关信息 */
            + "
"
            + "📍 当前网关"
            + "Gateway
"
            + "
"
            /* 引导弹窗 */
            + "
"
            + "
"


"D:\PlatformIO1\Projects\硬件配网.txt"这是文件地址


The user is pointing me to a file `D:\PlatformIO1\Projects\硬件配网.txt` which contains the software team's hardware provisioning page design. Let me read this file first - it likely contains the exact HTML/CSS that the software team designed for the provisioning page.


软件组给了设计文件！让我先看看：