The user wants to go back to the "working" version before the LED timing and AP mode changes. The changes that were made for the WebServer + LittleFS approach are in wifi_manager.cpp, wifi_manager.h, main.cpp, platformio.ini, and button_manager.cpp.

The user wants to keep the WebServer + LittleFS approach (方案一) but go back to before all the LED timing changes, 60s timeout, button callback simplification, etc.

The simplest approach is to revert only the files that were changed in this session (the LED/AP timing changes). But since all the changes are mingled in the same files...

Let me think about what the "stable" version was. From the conversation summary, the final working version before the LED/AP timing issues was when:
- WebServer + LittleFS was working
- WiFi provisioning was functional
- The issues were: LED blinking slow after button press, LED going off when connected

The user now has:
- LED timing changes (200ms→250ms, `_ledLastToggle` resets at transitions)
- 60s AP timeout
- Simplified button callback
- Debug prints in button_manager
- monitor_dtr/monitor_rts in platformio.ini

What the user wants to revert: the LED timing mess, the 60s timeout, the button callback changes.

I think the safest approach is to `git stash` the current state, then `git checkout` to restore the commited version, and then re-apply only the WebServer + LittleFS changes (方案一 core functionality) without the LED/AP timing modifications.

But wait - the initial commit already had WiFiManager? Let me check what was in the initial commit.

Actually, looking at the git status from the conversation start, the modified files were already modified when the conversation started:
```
M include/config.h
 M include/display_manager.h
 M include/radar_manager.h
 M include/wifi_manager.h
 M platformio.ini
 M src/button_manager.cpp
 M src/display_manager.cpp
 M src/main.cpp
 M src/mqtt_manager.cpp
 M src/radar_manager.cpp
 M src/sensor_manager.cpp
 M src/wifi_manager.cpp
```

So the initial commit `49e3abb feat: init` is the state before ANY of the work. The working tree has all the changes.

The user wants to revert to before the LED/AP timing fiddling. The safest approach is to restore specific files to their state in the initial commit, but that would lose the WebServer + LittleFS work too.

Better approach: I should ask what exactly they want to keep. But the user seems frustrated and just wants to go back.

Let me take a different approach. Let me `git stash` the current changes, then re-apply only the core WebServer + LittleFS code (without LED changes). But I need to know what the "good" state was.

From the conversation summary, the "good" state had:
- wifi_manager.cpp with WebServer + LittleFS approach
- Working provisioning at 192.168.4.1
- LED: AP mode 300ms blink, disconnected 1000ms blink, connected steady
- Button callback in main.cpp that called startConfigPortal() with MQTT reconnect

The problems that emerged after were:
- LED blinking slow after button press
- LED going off when connected
- 60s timeout not existing yet
- Page button not working (GPIO46)
- Alarm LED and WiFi LED blinking together

I think the best approach is to revert wifi_manager.cpp and main.cpp to the pre-LED-experiment state from the conversation summary. Let me check what that looked like.

From the summary, the final working version had:

wifi_manager.cpp LED logic:
```cpp
// LED: 配网模式快闪, 未连接慢闪, 已连接常亮
if (_apMode) {
    if (millis() - _ledLastToggle > 300) {
        _ledLastToggle = millis(); _ledState = !_ledState;
        digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
    }
} else if (!_connected) {
    if (millis() - _ledLastToggle > 1000) {
        _ledLastToggle = millis(); _ledState = !_ledState;
        digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
    }
} else {
    digitalWrite(LED_WIFI_PIN, HIGH);
}
```

And the button callback in main.cpp with the full MQTT reconnect logic.

So I need to:
1. Revert wifi_manager.cpp to remove: _ledLastToggle resets at transitions, 60s timeout, _apStartTime, LED interval changes
2. Revert main.cpp button callback back to the full version
3. Remove debug from button_manager.cpp
4. Keep platformio.ini monitor_dtr/rts (that's a good change)

Actually, let me just `git checkout` specific files from the initial commit and then re-apply the clean version. But that loses everything...

OK, the simplest approach: I'll rewrite wifi_manager.cpp to the clean version (before LED timing experiments) and restore main.cpp's button callback. Let me read the current files and carefully revert just the problematic changes.

Actually, I think the user is just frustrated with all the back-and-forth changes. Let me take a practical approach:

1. `git stash` current changes
2. Restore to initial commit state
3. Re-apply the WebServer + LittleFS approach cleanly

But the initial commit might not have the WebServer + LittleFS code at all - it might have the original WiFiManager-based code.

Let me check what's in the initial commit for wifi_manager files.


Bash

cd "d:\PlatformIO1\Projects\Healthv2.0" && git show 49e3abb:src/wifi_manager.cpp | head -5

Show what wifi_manager files looked like at init commit


现在都是问题，我们换回刚才配网模式没改的版本


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


Let me check what the initial commit had for wifi_manager files. The initial commit was the WiFiManager-based code, not the WebServer + LittleFS approach. So I can't just revert to the initial commit - that would lose all the WebServer + LittleFS work.

The user wants to keep the WebServer + LittleFS core functionality but remove the recent LED timing / AP mode experiments. Let me take a different approach: I'll write a clean version of wifi_manager.cpp that has:
- WebServer + LittleFS (方案一)
- Simple, clean LED logic (no timing hacks)
- Simple AP start/stop
- No 60s timeout
- No _apStartTime tracking
- Clean _ledLastToggle management

And restore main.cpp button callback to the working version.

Let me write both files clean. Actually, let me first stash, then write clean versions.

Wait, I should be more careful. The user is frustrated and just wants to go back. Let me stash the current state, then check out the init commit files, and manually re-apply only the WebServer + LittleFS core (which was the major rewrite).

Actually, the simplest approach: I'll write clean files for wifi_manager.cpp, wifi_manager.h, and main.cpp that have the WebServer + LittleFS approach but without all the LED timing experiments.

Let me do that. I'll write clean, well-tested versions.

For wifi_manager.cpp, the clean version should have:
- WebServer + LittleFS
- Non-blocking WiFi connection
- Simple LED: AP=300ms blink, disconnected=1000ms blink, connected=HIGH
- No 60s timeout
- No _apStartTime
- No _ledLastToggle reset hacks - just simple state-based LED

For the LED, the simplest correct approach is to track the mode and reset the timer when mode changes:

```cpp
uint8_t ledMode = _apMode ? 2 : (_connected ? 1 : 0);
if (ledMode != _lastLedMode) {
    _ledLastToggle = millis();
    _ledState = true;  // start with LED ON
    _lastLedMode = ledMode;
}

if (ledMode == 2) {  // AP mode fast blink
    if (millis() - _ledLastToggle > 300) {
        _ledLastToggle = millis(); _ledState = !_ledState;
        digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
    }
} else if (ledMode == 0) {  // Disconnected slow blink
    if (millis() - _ledLastToggle > 1000) {
        _ledLastToggle = millis(); _ledState = !_ledState;
        digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
    }
} else {  // Connected - steady ON
    digitalWrite(LED_WIFI_PIN, HIGH);
}
```

This is clean, handles transitions properly, and doesn't flicker.

For wifi_manager.h, remove `_apStartTime`.

Let me write it all now. This will be a clean rewrite of the relevant parts.


先 `git stash` 备份当前状态，然后写一个干净版本：


cd "d:\PlatformIO1\Projects\Healthv2.0" && git stash push -m "backup: LED timing experiments" -- include/wifi_manager.h src/wifi_manager.cpp src/main.cpp src/button_manager.cpp platformio.ini

Stash current WiFi/LED changes for safety


warning: in the working copy of 'include/wifi_manager.h', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'src/wifi_manager.cpp', LF will be replaced by CRLF the next time Git touches it
Saved working directory and index state On main: backup: LED timing experiments
warning: in the working copy of 'include/wifi_manager.h', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'src/wifi_manager.cpp', LF will be replaced by CRLF the next time Git touches it


51	// ============================================================
52	// 单按钮状态机
53	// ============================================================
54
55	void ButtonManager::_processButton(ButtonState& btn, bool hasShort, bool hasLong) {
56	    bool raw = _readPin(btn.pin);  // LOW = 按下
57
58	    if (raw && !btn.pressed) {
59	        // 按下沿
60	        btn.pressed = true;
61	        btn.pressTime = millis();
62	        btn.longTriggered = false;
63	    }
64	    else if (!raw && btn.pressed) {
65	        // 松开沿
66	        btn.pressed = false;
67	        uint32_t holdTime = millis() - btn.pressTime;
68
69	        if (holdTime < BTN_DEBOUNCE_MS) {
70	            return;  // 抖动脉冲，忽略
71	        }
72
73	        if (hasLong && holdTime >= BTN_LONG_PRESS_MS && !btn.longTriggered) {
74	            // 长按触发
75	            btn.longTriggered = true;
76	            if (_cbProvision) _cbProvision();
77	        }
78	        else if (hasShort && holdTime >= BTN_DEBOUNCE_MS && holdTime < BTN_LONG_PRESS_MS) {
79	            // 短按触发
80	            if (btn.pin == BTN_ALARM_DISMISS_PIN && _cbDismiss) _cbDismiss();
81	            if (btn.pin == BTN_PAGE_SWITCH_PIN   && _cbPage)    _cbPage();
82	        }
83	    }
84	}
85
86	bool ButtonManager::_readPin(uint8_t pin) {
87	    return digitalRead(pin) == LOW;  // INPUT_PULLUP: LOW = 按下


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	class WiFiMgmt {
34	public:
35	    WiFiMgmt();
36	    ~WiFiMgmt();
37
38	    // 单例指针 (用于静态回调)
39	    static WiFiMgmt* _instance;
40
41	    /**
42	     * @brief 初始化 WiFi 管理器
43	     * @return true=成功
44	     */
45	    bool begin();
46
47	    /**
48	     * @brief 主循环, 处理配网流程和按钮检测
49	     * @return true=WiFi 已连接
50	     */
51	    bool loop();
52
53	    /**
54	     * @brief WiFi 是否已连接
55	     */
56	    bool isConnected() const;
57
58	    /**
59	     * @brief 获取设备配置
60	     */
61	    DeviceConfig getConfig() const;
62
63	    /**
64	     * @brief 获取设备 ID (用于 MQTT client_id)
65	     */
66	    const char* getDeviceId() const;
67
68	    /**
69	     * @brief 获取环境备注
70	     */
71	    const char* getEnvNote() const;
72
73	    /**
74	     * @brief 强制进入配网模式
75	     */
76	    void startConfigPortal();
77
78	    /**
79	     * @brief 清除所有配置并重启
80	     */
81	    void resetSettings();
82
83	private:
84	    /**
85	     * @brief 尝试连接 WiFi
86	     */
87	    bool _connectWiFi();
88
89	    /**
90	     * @brief 设置 LED 状态
91	     * @param mode 0=慢闪(未连接) 1=快闪(配网) 2=常亮(已连接)
92	     */
93	    void _setLEDMode(uint8_t mode);
94
95	    /**
96	     * @brief 检测按钮长按
97	     */
98	    bool _checkButtonLongPress();
99
100	    /**
101	     * @brief 加载/保存配置到 LittleFS
102	     */
103	    bool _loadConfig();
104	    bool _saveConfig();
105
106	    /**
107	     * @brief 保存 WiFi 参数回调 (WiFiManager 的回调)
108	     */
109	    static void _saveParamsCallback();
110	    static void _apCallback(WiFiManager* wm);
111
112	    WiFiManager     _wm;
113	    DeviceConfig    _config;
114	    bool            _connected;
115	    uint32_t        _lastCheckTime;
116	    uint32_t        _buttonPressTime;
117	    bool            _buttonPressed;
118
119	    // LED 闪烁状态
120	    uint32_t        _ledLastToggle;
121	    bool            _ledState;
122
123	    // WiFiManager 自定义参数
124	    WiFiManagerParameter* _param_mqtt_server;
125	    WiFiManagerParameter* _param_mqtt_port;
126	    WiFiManagerParameter* _param_mqtt_user;
127	    WiFiManagerParameter* _param_mqtt_pass;
128	    WiFiManagerParameter* _param_device_id;
129	    WiFiManagerParameter* _param_env_note;
130	};
131
132	#endif // WIFI_MANAGER_H


1	/**
2	 * @file main.cpp
3	 * @brief 五劳无感康养监测系统 - 主程序 (协议 V0.08)
4	 *
5	 * ESP32-S3 + R60ABD1 60G 毫米波雷达 + DHT11 + 水浸
6	 * 本地紧急报警 (离线可用) + 服务端告警联动
7	 */
8
9	#include <Arduino.h>
10	#include <WiFi.h>
11	#include "config.h"
12	#include "radar_protocol.h"
13	#include "radar_manager.h"
14	#include "wifi_manager.h"
15	#include "mqtt_manager.h"
16	#include "sensor_manager.h"
17	#include "alarm_manager.h"
18	#include "display_manager.h"
19	#include "button_manager.h"
20
21	// ============================================================
22	// 全局对象
23	// ============================================================
24
25	RadarManager   g_radar;
26	WiFiMgmt       g_wifi;
27	MQTTManager    g_mqtt;
28	SensorManager  g_sensor;
29	AlarmManager   g_alarm;
30	DisplayManager g_display;
31	ButtonManager  g_buttons;
32
33	// 上报控制
34	uint32_t g_lastReportTime  = 0;
35	uint32_t g_sequence        = 1;       // 上报序号
36	uint32_t g_bootTime        = 0;       // 启动时刻
37
38	// 本地报警跟踪 (防重复触发)
39	bool g_localFallActive       = false;
40	bool g_localWaterActive      = false;
41	bool g_localHeartAbnActive   = false;
42	bool g_localBreathAbnActive  = false;
43
44	// ============================================================
45	// 时间同步
46	// ============================================================
47
48	void syncTime() {
49	    Serial.println(F("[时间] 正在同步时间..."));
50	    configTime(NTP_GMT_OFFSET_SEC, NTP_DAYLIGHT_OFFSET, NTP_SERVER, "pool.ntp.org");
51
52	    struct tm timeinfo;
53	    int retry = 0;
54	    while (!getLocalTime(&timeinfo) && retry < 20) {
55	        delay(500);
56	        Serial.print(".");
57	        retry++;
58	    }
59
60	    if (retry < 20) {
61	        Serial.println();
62	        Serial.printf("[时间] ✅ 时间同步成功: %04d-%02d-%02d %02d:%02d:%02d\n",
63	                      timeinfo.tm_year + 1900, timeinfo.tm_mon + 1,
64	                      timeinfo.tm_mday, timeinfo.tm_hour,
65	                      timeinfo.tm_min, timeinfo.tm_sec);
66	    } else {
67	        Serial.println(F("\n[时间] ⚠ 时间同步失败, 使用默认时间"));
68	    }
69	}
70
71	// ============================================================
72	// MQTT 告警指令回调 (协议规范 §5.1, §5.2)
73	// ============================================================
74
75	void handleAlarmCommand(const AlarmCommand& cmd) {
76	    if (strcmp(cmd.type, "alarm_restore") == 0) {
77	        // 告警恢复
78	        g_alarm.executeRestore(cmd);
79
80	        // 清除对应本地告警状态
81	        if (strcmp(cmd.alarmType, "fall") == 0)           g_localFallActive = false;
82	        else if (strcmp(cmd.alarmType, "water_leak") == 0) g_localWaterActive = false;
83	        else if (strcmp(cmd.alarmType, "heart_abnormal") == 0)  g_localHeartAbnActive = false;
84	        else if (strcmp(cmd.alarmType, "breath_abnormal") == 0) g_localBreathAbnActive = false;
85	    } else {
86	        // 告警触发
87	        g_alarm.executeAlarm(cmd);
88	    }
89
90	    // 回复响应 (§4.5)
91	    g_mqtt.publishCmdResponse(cmd.cmdId, cmd.type, cmd.alarmType,
92	                              true, "0", "");
93	}
94
95	// ============================================================
96	// MQTT 控制指令回调 (§5.3)
97	// ============================================================
98
99	void handleControlCommand(const ControlCommand& cmd) {
100	    Serial.printf("[控制] 收到控制指令: %s\n", cmd.controlType);
101
102	    bool success = true;
103	    const char* errCode = "0";
104	    const char* errMsg  = "";
105
106	    if (strcmp(cmd.controlType, "buzzer_test") == 0) {
107	        uint32_t dur = cmd.params_duration > 0 ? cmd.params_duration : 3;
108	        g_alarm.buzzerTest(dur);
109	    }
110	    else if (strcmp(cmd.controlType, "device_restart") == 0) {
111	        Serial.println(F("[控制] 🔄 远程重启..."));
112	        g_mqtt.publishCmdResponse(cmd.cmdId, "control", "", true, "0", "");
113	        g_mqtt.publishOffline();
114	        delay(500);
115	        ESP.restart();
116	    }
117	    else if (strcmp(cmd.controlType, "factory_reset") == 0) {
118	        Serial.println(F("[控制] 🔄 恢复出厂设置..."));
119	        g_wifi.resetSettings();
120	    }
121	    else if (strcmp(cmd.controlType, "enter_provision") == 0) {
122	        Serial.println(F("[控制] 📶 进入配网模式"));
123	        g_wifi.startConfigPortal();
124	    }
125	    else {
126	        success = false;
127	        errCode = "2001";
128	        errMsg  = "不支持的指令类型";
129	    }
130
131	    g_mqtt.publishCmdResponse(cmd.cmdId, "control", "",
132	                              success, errCode, errMsg);
133	}
134
135	// ============================================================
136	// MQTT 配网指令回调 (§5.4)
137	// ============================================================
138
139	void handleProvisionCommand(const ProvisionCommand& cmd) {
140	    Serial.printf("[配网] 📶 收到配网指令: SSID=%s\n", cmd.wifi_ssid);
141
142	    if (strlen(cmd.wifi_ssid) == 0) {
143	        g_mqtt.publishProvisionResponse(cmd.cmdId, false,
144	                                        "1002", "未提供WiFi SSID");
145	        return;
146	    }
147
148	    // 连接新 WiFi
149	    g_mqtt.publishProvisionStatus("wifi_connecting", "");
150
151	    WiFi.begin(cmd.wifi_ssid, cmd.wifi_password);
152	    int retry = 0;
153	    while (WiFi.status() != WL_CONNECTED && retry < 30) {
154	        delay(500);
155	        Serial.print(".");
156	        retry++;
157	    }
158
159	    if (WiFi.status() == WL_CONNECTED) {
160	        Serial.printf("\n[配网] ✅ WiFi 已连接, IP=%s\n",
161	                      WiFi.localIP().toString().c_str());
162
163	        // 更新 MQTT 服务器配置
164	        if (strlen(cmd.mqtt_broker) > 0) {
165	            g_mqtt.setServer(cmd.mqtt_broker, cmd.mqtt_port,
166	                           GATEID_DEFAULT, GATEID_DEFAULT); // 正式账号 = gateid
167	        }
168
169	        char signal[8];
170	        snprintf(signal, sizeof(signal), "%d", WiFi.RSSI());
171	        g_mqtt.publishProvisionResponse(cmd.cmdId, true, "0", "", signal);
172
173	        // 重连 MQTT
174	        g_mqtt.disconnect();
175	        g_mqtt.connect();
176	        g_mqtt.publishOnline();
177	    } else {
178	        Serial.println(F("\n[配网] ❌ WiFi 连接失败"));
179	        g_mqtt.publishProvisionStatus("wifi_failed", "");
180	        g_mqtt.publishProvisionResponse(cmd.cmdId, false,
181	                                        "1003", "WiFi连接超时");
182	    }
183	}
184
185	// ============================================================
186	// 构建遥测数据
187	// ============================================================
188
189	TelemetryData buildTelemetry(const RadarRawData& radar) {
190	    TelemetryData t;
191	    memset(&t, 0, sizeof(t));
192
193	    // 设备标识
194	    strncpy(t.saleid,    SALEID_DEFAULT,   sizeof(t.saleid) - 1);
195	    strncpy(t.gateid,    g_mqtt.getGateId(), sizeof(t.gateid) - 1);
196	    strncpy(t.meterId,   METER_ID_DEFAULT, sizeof(t.meterId) - 1);
197	    strncpy(t.meterName, METER_NAME_DEFAULT, sizeof(t.meterName) - 1);
198
199	    // DHT11
200	    t.temp     = g_sensor.getTemp();
201	    t.humi     = g_sensor.getHumi();
202	    t.dhtValid = g_sensor.isDhtValid();
203
204	    // 雷达
205	    t.presence   = radar.presence;
206	    t.motion     = radar.motion;
207	    t.fall       = radar.fall;
208	    t.resident   = radar.resident;
209	    t.heartRate  = radar.heartRate;
210	    t.breathRate = radar.breathRate;
211	    t.body       = radar.body;
212	    t.radarValid = radar.radarValid;
213	    t.vitalValid = radar.vitalValid;
214
215	    // 水浸
216	    t.rainDO    = g_sensor.getRainDO();
217	    t.rainValid = g_sensor.isRainValid();
218
219	    // 设备状态
220	    t.wifiReady  = g_wifi.isConnected() && g_mqtt.isConnected();
221	    t.uptimeSec  = (millis() - g_bootTime) / 1000;
222	    t.buzzerState = g_alarm.isBuzzerOn();
223	    t.lastUpdate = millis();
224
225	    return t;
226	}
227
228	// ============================================================
229	// 本地报警检查 (离线可用)
230	// ============================================================
231
232	void checkLocalAlarms(const RadarRawData& radar) {
233	    uint32_t now = millis();
234
235	    // --- 跌倒检测 (本地紧急报警, 立即触发) ---
236	    if (radar.fall && radar.radarValid && !g_localFallActive) {
237	        g_localFallActive = true;

... [226 lines truncated] ...


1	/**
2	 * @file wifi_manager.cpp
3	 * @brief WiFi 配网管理器实现
4	 */
5
6	#include "wifi_manager.h"
7
8	// 单例指针
9	WiFiMgmt* WiFiMgmt::_instance = nullptr;
10
11	// ============================================================
12	// 构造函数 & 析构函数
13	// ============================================================
14
15	WiFiMgmt::WiFiMgmt()
16	    : _connected(false)
17	    , _lastCheckTime(0)
18	    , _buttonPressTime(0)
19	    , _buttonPressed(false)
20	    , _ledLastToggle(0)
21	    , _ledState(false)
22	    , _param_mqtt_server(nullptr)
23	    , _param_mqtt_port(nullptr)
24	    , _param_mqtt_user(nullptr)
25	    , _param_mqtt_pass(nullptr)
26	    , _param_device_id(nullptr)
27	    , _param_env_note(nullptr)
28	{
29	    memset(&_config, 0, sizeof(_config));
30	    _instance = this;  // 设置单例
31	}
32
33	WiFiMgmt::~WiFiMgmt() {
34	    _instance = nullptr;
35	}
36
37	// ============================================================
38	// 初始化
39	// ============================================================
40
41	bool WiFiMgmt::begin() {
42	    Serial.println(F("\n========================================"));
43	    Serial.println(F("  WiFi 配网管理器 初始化"));
44	    Serial.println(F("========================================"));
45
46	    // 初始化引脚
47	    pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
48	    pinMode(LED_WIFI_PIN, OUTPUT);
49	    digitalWrite(LED_WIFI_PIN, LOW);
50
51	    // 初始化 LittleFS
52	    if (!LittleFS.begin(true)) {
53	        Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));
54	        return false;
55	    }
56	    Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));
57
58	    // 加载配置
59	    if (!_loadConfig()) {
60	        Serial.println(F("[WiFi] 使用默认配置"));
61	        // 设置默认值
62	        strncpy(_config.mqtt_server, MQTT_DEFAULT_SERVER,   sizeof(_config.mqtt_server) - 1);
63	        strncpy(_config.mqtt_port,   MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
64	        strncpy(_config.mqtt_user,   MQTT_DEFAULT_USER,     sizeof(_config.mqtt_user) - 1);
65	        strncpy(_config.mqtt_pass,   MQTT_DEFAULT_PASS,     sizeof(_config.mqtt_pass) - 1);
66	        strncpy(_config.device_id,   MQTT_DEFAULT_DEVICE,   sizeof(_config.device_id) - 1);
67	        strncpy(_config.env_note,    MQTT_DEFAULT_ENV,      sizeof(_config.env_note) - 1);
68	    }
69
70	    // 配置 WiFiManager
71	    _wm.setDebugOutput(false);  // 关闭调试输出 (避免干扰)
72	    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
73	    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
74	    _wm.setTitle("🏥 五劳无感康养监测 - 设备配网");  // 配网页面的中文标题
75	    _wm.setCustomHeadElement(
76	        "<style>"
77	        "body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"
78	        ".msg .info { font-size: 14px; }"
79	        "</style>"
80	    );
81	    WiFi.setAutoReconnect(true);  // ESP32 自动重连
82
83	    // 注册保存回调 (必须使用静态函数, 不能使用 lambda)
84	    _wm.setSaveConfigCallback(_saveParamsCallback);
85
86	    // 创建自定义参数
87	    _param_mqtt_server = new WiFiManagerParameter(
88	        "mqtt_server", "MQTT 服务器地址",
89	        _config.mqtt_server, 40);
90	    _param_mqtt_port = new WiFiManagerParameter(
91	        "mqtt_port", "MQTT 端口",
92	        _config.mqtt_port, 6);
93	    _param_mqtt_user = new WiFiManagerParameter(
94	        "mqtt_user", "MQTT 用户名",
95	        _config.mqtt_user, 20);
96	    _param_mqtt_pass = new WiFiManagerParameter(
97	        "mqtt_pass", "MQTT 密码",
98	        _config.mqtt_pass, 20);
99	    _param_device_id = new WiFiManagerParameter(
100	        "device_id", "设备名称",
101	        _config.device_id, 20);
102	    _param_env_note = new WiFiManagerParameter(
103	        "env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",
104	        _config.env_note, 20);
105
106	    // 注册自定义参数
107	    _wm.addParameter(_param_mqtt_server);
108	    _wm.addParameter(_param_mqtt_port);
109	    _wm.addParameter(_param_mqtt_user);
110	    _wm.addParameter(_param_mqtt_pass);
111	    _wm.addParameter(_param_device_id);
112	    _wm.addParameter(_param_env_note);
113
114	    // 设置 AP 模式回调 (静态函数)
115	    _wm.setAPCallback(_apCallback);
116
117	    // 尝试连接 WiFi
118	    _connected = _connectWiFi();
119
120	    if (!_connected) {
121	        // 如果自动连接失败, 启动配网页面
122	        Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
123	        _setLEDMode(1);  // 快闪 = 配网模式
124	        _connected = _connectWiFi();  // 内部会启动配网页面
125	    }
126
127	    if (_connected) {
128	        _setLEDMode(2);  // 常亮 = 已连接
129	    }
130
131	    _lastCheckTime = millis();
132	    return _connected;
133	}
134
135	// ============================================================
136	// 主循环
137	// ============================================================
138
139	bool WiFiMgmt::loop() {
140	    // 检测按钮长按
141	    if (_checkButtonLongPress()) {
142	        Serial.println(F("[WiFi] ⚠  检测到长按, 清除配置并重启..."));
143	        resetSettings();
144	        return false;
145	    }
146
147	    // 检查 WiFi 状态
148	    if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
149	        _lastCheckTime = millis();
150
151	        if (WiFi.status() != WL_CONNECTED) {
152	            _connected = false;
153	            _setLEDMode(0);  // 慢闪 = 未连接
154	            Serial.println(F("[WiFi] ⚠  WiFi 断开, 尝试重连..."));
155	            _connectWiFi();
156	        } else {
157	            _connected = true;
158	            _setLEDMode(2);  // 常亮
159	        }
160	    }
161
162	    // LED 闪烁处理
163	    if (!_connected) {
164	        uint32_t interval = (_wm.getConfigPortalActive()) ? 300 : 1000;
165	        if (millis() - _ledLastToggle > interval) {
166	            _ledLastToggle = millis();
167	            _ledState = !_ledState;
168	            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
169	        }
170	    }
171
172	    return _connected;
173	}
174
175	// ============================================================
176	// WiFi 连接
177	// ============================================================
178
179	bool WiFiMgmt::_connectWiFi() {
180	    // 检查是否有已保存的配置
181	    // autoConnect 会先尝试连接, 失败后启动 AP 配网页面
182	    _connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);
183
184	    if (_connected) {
185	        Serial.println(F("[WiFi] ✅ 已连接!"));
186	        Serial.printf("  WiFi名称: %s\n", WiFi.SSID().c_str());
187	        Serial.printf("  IP地址:   %s\n", WiFi.localIP().toString().c_str());
188	        Serial.printf("  MAC地址:  %s\n", WiFi.macAddress().c_str());
189
190	        // 重新加载配置 (确保获取最新值)
191	        // autoConnect 过程中如果有保存, 回调已写入 LittleFS
192	        _loadConfig();
193
194	        // 同步自定义参数 (如果配置是从 LittleFS 加载的)
195	        _param_mqtt_server->setValue(_config.mqtt_server, sizeof(_config.mqtt_server));
196	        _param_mqtt_port->setValue(_config.mqtt_port, sizeof(_config.mqtt_port));
197	        _param_mqtt_user->setValue(_config.mqtt_user, sizeof(_config.mqtt_user));
198	        _param_mqtt_pass->setValue(_config.mqtt_pass, sizeof(_config.mqtt_pass));
199	        _param_device_id->setValue(_config.device_id, sizeof(_config.device_id));
200	        _param_env_note->setValue(_config.env_note, sizeof(_config.env_note));
201
202	        Serial.printf("  设备名: %s\n", _config.device_id);
203	        Serial.printf("  环境:   %s\n", _config.env_note);
204	        Serial.printf("  MQTT服务器: %s:%s\n", _config.mqtt_server, _config.mqtt_port);
205	    }
206
207	    return _connected;
208	}
209
210	// ============================================================
211	// 配网页面
212	// ============================================================
213
214	void WiFiMgmt::startConfigPortal() {
215	    Serial.println(F("[WiFi] 🔧 手动启动配网页面..."));
216	    _setLEDMode(1);
217	    _wm.startConfigPortal(WIFI_AP_SSID, WIFI_AP_PASSWORD);
218	    _connected = (WiFi.status() == WL_CONNECTED);
219	    if (_connected) {
220	        _setLEDMode(2);
221	    }
222	}
223
224	// ============================================================
225	// 重置设置
226	// ============================================================
227
228	void WiFiMgmt::resetSettings() {
229	    Serial.println(F("[WiFi] 清除所有配置..."));
230
231	    // 删除 LittleFS 中的配置文件
232	    if (LittleFS.exists(CONFIG_FILE_WIFI)) LittleFS.remove(CONFIG_FILE_WIFI);

... [180 lines truncated] ...


I see - after stashing, the files reverted to the original WiFiManager-based code (the init commit state). The system-reminder shows the files have been reverted to their original WiFiManager-based versions.

The user wants the WebServer + LittleFS version (方案一) back, but without all the LED timing experiments. Let me now write clean versions of:
1. `wifi_manager.h` - WebServer + LittleFS approach, no _apStartTime
2. `wifi_manager.cpp` - Clean LED logic with mode tracking
3. `main.cpp` - Keep the WebServer-compatible button callback

Let me write them clean and complete.


好，stash 之后文件回到了最原始的 WiFiManager 版本。现在我把 **WebServer + LittleFS（方案一）** 重写一份干净的，不带那些 LED 实验：


Write

/**
 * @file wifi_manager.h
 * @brief WiFi 配网 — WebServer + LittleFS (方案一)
 */
#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.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];
    char wifi_ssid[33];
    char wifi_pass[65];
};

typedef void (*ProvisionStateCallback)(const char* state);

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

    static WiFiMgmt* _instance;

    void onProvisionState(ProvisionStateCallback cb);
    bool begin();
    bool loop();
    bool isConnected() const;
    DeviceConfig getConfig() const;
    const char* getDeviceId() const;
    const char* getEnvNote() const;
    bool isFreshProvision() const { return _freshProvision; }
    void clearFreshProvision() { _freshProvision = false; }
    void startConfigPortal();
    void resetSettings();

private:
    bool _checkButtonLongPress();
    bool _loadConfig();
    bool _saveConfig();
    void _emitProvisionState(const char* state);

    void _startAP();
    void _stopAP();
    void _tryConnect();          // 非阻塞 WiFi.begin
    void _handleRoot();
    void _handleWiFiScan();
    void _handleConfig();
    void _handleStatus();
    void _handleNotFound();

    WebServer    _server;
    DNSServer    _dns;
    DeviceConfig _config;
    bool         _connected;
    bool         _freshProvision;
    bool         _apMode;
    bool         _pendingConnect;
    uint32_t     _connectStartTime;
    uint32_t     _lastCheckTime;
    uint32_t     _buttonPressTime;
    bool         _buttonPressed;
    uint32_t     _ledLastToggle;
    bool         _ledState;
    uint8_t      _lastLedMode;       // 0=断开 1=已连接 2=AP配网
    ProvisionStateCallback _provisionStateCb;
};

#endif


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)

/**
 * @file wifi_manager.h
 * @brief WiFi 配网 — WebServer + LittleFS (方案一)
 */
#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.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];
    char wifi_ssid[33];
    char wifi_pass[65];
};

typedef void (*ProvisionStateCallback)(const char* state);

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

    static WiFiMgmt* _instance;

    void onProvisionState(ProvisionStateCallback cb);
    bool begin();
    bool loop();
    bool isConnected() const;
    DeviceConfig getConfig() const;
    const char* getDeviceId() const;
    const char* getEnvNote() const;
    bool isFreshProvision() const { return _freshProvision; }
    void clearFreshProvision() { _freshProvision = false; }
    void startConfigPortal();
    void resetSettings();

private:
    bool _checkButtonLongPress();
    bool _loadConfig();
    bool _saveConfig();
    void _emitProvisionState(const char* state);

    void _startAP();
    void _stopAP();
    void _tryConnect();          // 非阻塞 WiFi.begin
    void _handleRoot();
    void _handleWiFiScan();
    void _handleConfig();
    void _handleStatus();
    void _handleNotFound();

    WebServer    _server;
    DNSServer    _dns;
    DeviceConfig _config;
    bool         _connected;
    bool         _freshProvision;
    bool         _apMode;
    bool         _pendingConnect;
    uint32_t     _connectStartTime;
    uint32_t     _lastCheckTime;
    uint32_t     _buttonPressTime;
    bool         _buttonPressed;
    uint32_t     _ledLastToggle;
    bool         _ledState;
    uint8_t      _lastLedMode;       // 0=断开 1=已连接 2=AP配网
    ProvisionStateCallback _provisionStateCb;
};

#endif

- *  - 自动连接已保存的 WiFi

- *  - 配网失败/首次启动 → AP 模式 + 强制门户页面

- *  - 自定义参数: MQTT 服务器/端口/用户/密码、设备名称、环境备注

- *  - 长按按钮重置配置

+ * @brief WiFi 配网 — WebServer + LittleFS (方案一)

-    char env_note[20];      // 环境备注 (如: "主卧", "客厅", "老人房")

+typedef void (*ProvisionStateCallback)(const char* state);

-    // 单例指针 (用于静态回调)

+    void onProvisionState(ProvisionStateCallback cb);

-     * @brief 主循环, 处理配网流程和按钮检测

-     * @brief 获取设备 ID (用于 MQTT client_id)

-     * @brief 强制进入配网模式

+    bool isFreshProvision() const { return _freshProvision; }

+    void clearFreshProvision() { _freshProvision = false; }

-     * @brief 清除所有配置并重启

-     * @param mode 0=慢闪(未连接) 1=快闪(配网) 2=常亮(已连接)

+    void _emitProvisionState(const char* state);

-     * @brief 保存 WiFi 参数回调 (WiFiManager 的回调)

-    static void _apCallback(WiFiManager* wm);

+    void _tryConnect();          // 非阻塞 WiFi.begin

-    WiFiManagerParameter* _param_mqtt_server;

-    WiFiManagerParameter* _param_mqtt_port;

-    WiFiManagerParameter* _param_mqtt_user;

-    WiFiManagerParameter* _param_mqtt_pass;

-    WiFiManagerParameter* _param_device_id;

-    WiFiManagerParameter* _param_env_note;

+    uint8_t      _lastLedMode;       // 0=断开 1=已连接 2=AP配网

+    ProvisionStateCallback _provisionStateCb;

/**
 * @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=快闪(配网) 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;
};

#endif // WIFI_MANAGER_H


Now write the clean wifi_manager.cpp with proper LED mode tracking:


/**
 * @file wifi_manager.cpp
 * @brief WiFi 配网 — WebServer + LittleFS (方案一)
 */
#include "wifi_manager.h"

WiFiMgmt* WiFiMgmt::_instance = nullptr;

WiFiMgmt::WiFiMgmt()
    : _server(80)
    , _connected(false), _freshProvision(false), _apMode(false)
    , _pendingConnect(false), _connectStartTime(0)
    , _lastCheckTime(0), _buttonPressTime(0), _buttonPressed(false)
    , _ledLastToggle(0), _ledState(false), _lastLedMode(0)
    , _provisionStateCb(nullptr)
{
    memset(&_config, 0, sizeof(_config));
    _instance = this;
}

WiFiMgmt::~WiFiMgmt() { _stopAP(); _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);

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

    WiFi.setAutoReconnect(true);

    // 有已保存 WiFi → 后台连接(不等待); 没保存 → 直接 AP
    if (strlen(_config.wifi_ssid) > 0) {
        _tryConnect();
    } else {
        _startAP();
    }

    _lastCheckTime = millis();
    return _connected;
}

// ============================================================
// 主循环
// ============================================================
bool WiFiMgmt::loop() {
    if (_checkButtonLongPress()) { resetSettings(); return false; }

    if (_apMode) { _dns.processNextRequest(); }
    _server.handleClient();  // WebServer 常驻

    // 异步连接: 等结果
    if (_pendingConnect) {
        if (WiFi.status() == WL_CONNECTED) {
            _pendingConnect = false; _connected = true;
            _emitProvisionState("success");
            Serial.printf("[WiFi] ✅ 配网成功: %s\n", WiFi.localIP().toString().c_str());
            _stopAP();
        } else if (millis() - _connectStartTime > 15000) {
            _pendingConnect = false;
            Serial.println(F("[WiFi] ❌ 超时"));
            _startAP();
        }
    }

    // 后台重连成功 (AP 模式期间不关 AP)
    if (!_pendingConnect && !_connected && WiFi.status() == WL_CONNECTED) {
        _connected = true;
        _emitProvisionState("success");
        Serial.printf("[WiFi] ✅ 已连接: %s\n", WiFi.localIP().toString().c_str());
    }

    // 断开检测
    if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
        _lastCheckTime = millis();
        if (WiFi.status() != WL_CONNECTED && _connected) {
            _connected = false;
            Serial.println(F("[WiFi] ⚠ WiFi 断开"));
        }
    }

    // ============================================================
    // LED: 模式切换时重置定时器, 防止闪灭
    // ============================================================
    uint8_t mode = _apMode ? 2 : (_connected ? 1 : 0);
    if (mode != _lastLedMode) {
        _ledLastToggle = millis();
        _ledState = true;   // 切模式时先保持亮
        _lastLedMode = mode;
    }

    if (mode == 2) {           // AP 配网: 快闪 300ms
        if (millis() - _ledLastToggle > 300) {
            _ledLastToggle = millis(); _ledState = !_ledState;
            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
        }
    } else if (mode == 0) {    // 未连接: 慢闪 1000ms
        if (millis() - _ledLastToggle > 1000) {
            _ledLastToggle = millis(); _ledState = !_ledState;
            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
        }
    } else {                   // 已连接: 常亮
        digitalWrite(LED_WIFI_PIN, HIGH);
    }

    return _connected;
}

// ============================================================
// WiFi 连接 (非阻塞)
// ============================================================
void WiFiMgmt::_tryConnect() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(_config.wifi_ssid, _config.wifi_pass);
    _pendingConnect = true;
    _connectStartTime = millis();
    Serial.printf("[WiFi] 🔄 连接: %s ...\n", _config.wifi_ssid);
}

// ============================================================
// AP 模式
// ============================================================
void WiFiMgmt::_startAP() {
    if (_apMode) return;
    _apMode = true;
    _emitProvisionState("ready");

    WiFi.mode(WIFI_AP_STA);
    WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD);
    Serial.printf("[WiFi] 🔧 AP: %s IP:192.168.4.1\n", WIFI_AP_SSID);

    _dns.start(53, "*", IPAddress(192, 168, 4, 1));

    static bool routesSet = false;
    if (!routesSet) {
        _server.on("/",              HTTP_GET,  [this](){ _handleRoot(); });
        _server.on("/api/wifi/scan", HTTP_GET,  [this](){ _handleWiFiScan(); });
        _server.on("/api/config",    HTTP_POST, [this](){ _handleConfig(); });
        _server.on("/api/status",    HTTP_GET,  [this](){ _handleStatus(); });
        _server.onNotFound([this](){ _handleNotFound(); });
        _server.begin();
        routesSet = true;
        Serial.println(F("[WiFi]    WebServer 已启动"));
    }
}

void WiFiMgmt::_stopAP() {
    if (!_apMode) return;
    _dns.stop();
    _apMode = false;
    Serial.println(F("[WiFi]    AP 休眠 (WebServer 常驻)"));
}

// ============================================================
// Handlers
// ============================================================
void WiFiMgmt::_handleRoot() {
    File f = LittleFS.open("/index.html", "r");
    if (!f) { _server.send(404, "text/plain", "no index.html"); return; }
    _server.streamFile(f, "text/html"); f.close();
}

void WiFiMgmt::_handleWiFiScan() {
    int n = WiFi.scanNetworks();
    JsonDocument doc; JsonArray arr = doc.to<JsonArray>();
    for (int i = 0; i < n; i++) {
        JsonObject w = arr.add<JsonObject>();
        w["ssid"] = WiFi.SSID(i); w["rssi"] = WiFi.RSSI(i);
        w["enc"]  = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "open" : "wpa2";
    }
    WiFi.scanDelete();
    String json; serializeJson(doc, json);
    _server.send(200, "application/json", json);
}

void WiFiMgmt::_handleConfig() {
    if (!_server.hasArg("plain")) {
        _server.send(400, "application/json", "{\"ok\":false}"); return;
    }
    JsonDocument doc;
    if (deserializeJson(doc, _server.arg("plain"))) {
        _server.send(400, "application/json", "{\"ok\":false}"); return;
    }
    const char* ssid = doc["ssid"] | "";
    if (strlen(ssid) < 1) {
        _server.send(400, "application/json", "{\"ok\":false}"); return;
    }

    strncpy(_config.wifi_ssid, ssid, sizeof(_config.wifi_ssid)-1);
    strncpy(_config.wifi_pass, doc["password"]  | "", sizeof(_config.wifi_pass)-1);
    strncpy(_config.device_id, doc["device_id"] | "", sizeof(_config.device_id)-1);
    strncpy(_config.env_note,  doc["env_note"]  | "", sizeof(_config.env_note)-1);
    _saveConfig(); _freshProvision = true;
    Serial.printf("[WiFi] 💾 已保存: %s\n", ssid);

    _server.send(200, "application/json", "{\"ok\":true}");
    delay(200);

    _stopAP();
    _tryConnect();
}

void WiFiMgmt::_handleStatus() {
    JsonDocument doc;
    doc["gateway"] = _config.device_id;
    doc["device_id"] = _config.device_id;
    doc["env_note"]  = _config.env_note;
    String json; serializeJson(doc, json);
    _server.send(200, "application/json", json);
}

void WiFiMgmt::_handleNotFound() {
    _server.sendHeader("Location", "http://192.168.4.1/", true);
    _server.send(302, "text/plain", "");
}

// ============================================================
// 手动配网 / 重置
// ============================================================
void WiFiMgmt::startConfigPortal() {
    Serial.println(F("[WiFi] 🔧 手动配网按钮触发!"));
    _startAP();
}

void WiFiMgmt::resetSettings() {
    _stopAP();
    LittleFS.remove(CONFIG_FILE_DEVICE);
    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();
}

// ============================================================
// 长按检测 (CONFIG_BUTTON_PIN = GPIO0)
// ============================================================
bool WiFiMgmt::_checkButtonLongPress() {
    bool p = (digitalRead(CONFIG_BUTTON_PIN) == LOW);
    if (p && !_buttonPressed) { _buttonPressed = true; _buttonPressTime = millis(); }
    else if (!p && _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 f = LittleFS.open(CONFIG_FILE_DEVICE, "r");
    if (!f) return false;
    JsonDocument doc;
    if (deserializeJson(doc, f)) { f.close(); return false; }
    f.close();
    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);
    strncpy(_config.wifi_ssid,   doc["wifi_ssid"]   | "",                    sizeof(_config.wifi_ssid)-1);
    strncpy(_config.wifi_pass,   doc["wifi_pass"]   | "",                    sizeof(_config.wifi_pass)-1);
    Serial.println(F("[WiFi] ✅ 配置已加载")); 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;
    doc["wifi_ssid"]=_config.wifi_ssid; doc["wifi_pass"]=_config.wifi_pass;
    File f = LittleFS.open(CONFIG_FILE_DEVICE, "w");
    if (!f) return false;
    serializeJson(doc, f); f.close(); return true;
}

// ============================================================
// 回调 / Getter
// ============================================================
void WiFiMgmt::onProvisionState(ProvisionStateCallback cb) { _provisionStateCb = cb; }
void WiFiMgmt::_emitProvisionState(const char* state) {
    Serial.printf("[WiFi] 📡 %s\n", state);
    if (_provisionStateCb) _provisionStateCb(state);
}
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; }


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)

/**
 * @file wifi_manager.cpp
 * @brief WiFi 配网 — WebServer + LittleFS (方案一)
 */
#include "wifi_manager.h"

WiFiMgmt* WiFiMgmt::_instance = nullptr;

WiFiMgmt::WiFiMgmt()
    : _server(80)
    , _connected(false), _freshProvision(false), _apMode(false)
    , _pendingConnect(false), _connectStartTime(0)
    , _lastCheckTime(0), _buttonPressTime(0), _buttonPressed(false)
    , _ledLastToggle(0), _ledState(false), _lastLedMode(0)
    , _provisionStateCb(nullptr)
{
    memset(&_config, 0, sizeof(_config));
    _instance = this;
}

WiFiMgmt::~WiFiMgmt() { _stopAP(); _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);

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

    WiFi.setAutoReconnect(true);

    // 有已保存 WiFi → 后台连接(不等待); 没保存 → 直接 AP
    if (strlen(_config.wifi_ssid) > 0) {
        _tryConnect();
    } else {
        _startAP();
    }

    _lastCheckTime = millis();
    return _connected;
}

// ============================================================
// 主循环
// ============================================================
bool WiFiMgmt::loop() {
    if (_checkButtonLongPress()) { resetSettings(); return false; }

    if (_apMode) { _dns.processNextRequest(); }
    _server.handleClient();  // WebServer 常驻

    // 异步连接: 等结果
    if (_pendingConnect) {
        if (WiFi.status() == WL_CONNECTED) {
            _pendingConnect = false; _connected = true;
            _emitProvisionState("success");
            Serial.printf("[WiFi] ✅ 配网成功: %s\n", WiFi.localIP().toString().c_str());
            _stopAP();
        } else if (millis() - _connectStartTime > 15000) {
            _pendingConnect = false;
            Serial.println(F("[WiFi] ❌ 超时"));
            _startAP();
        }
    }

    // 后台重连成功 (AP 模式期间不关 AP)
    if (!_pendingConnect && !_connected && WiFi.status() == WL_CONNECTED) {
        _connected = true;
        _emitProvisionState("success");
        Serial.printf("[WiFi] ✅ 已连接: %s\n", WiFi.localIP().toString().c_str());
    }

    // 断开检测
    if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
        _lastCheckTime = millis();
        if (WiFi.status() != WL_CONNECTED && _connected) {
            _connected = false;
            Serial.println(F("[WiFi] ⚠ WiFi 断开"));
        }
    }

    // ============================================================
    // LED: 模式切换时重置定时器, 防止闪灭
    // ============================================================
    uint8_t mode = _apMode ? 2 : (_connected ? 1 : 0);
    if (mode != _lastLedMode) {
        _ledLastToggle = millis();
        _ledState = true;   // 切模式时先保持亮
        _lastLedMode = mode;
    }

    if (mode == 2) {           // AP 配网: 快闪 300ms
        if (millis() - _ledLastToggle > 300) {
            _ledLastToggle = millis(); _ledState = !_ledState;
            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
        }
    } else if (mode == 0) {    // 未连接: 慢闪 1000ms
        if (millis() - _ledLastToggle > 1000) {
            _ledLastToggle = millis(); _ledState = !_ledState;
            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
        }
    } else {                   // 已连接: 常亮
        digitalWrite(LED_WIFI_PIN, HIGH);
    }

    return _connected;
}

// ============================================================
// WiFi 连接 (非阻塞)
// ============================================================
void WiFiMgmt::_tryConnect() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(_config.wifi_ssid, _config.wifi_pass);
    _pendingConnect = true;
    _connectStartTime = millis();
    Serial.printf("[WiFi] 🔄 连接: %s ...\n", _config.wifi_ssid);
}

// ============================================================
// AP 模式
// ============================================================
void WiFiMgmt::_startAP() {
    if (_apMode) return;
    _apMode = true;
    _emitProvisionState("ready");

    WiFi.mode(WIFI_AP_STA);
    WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD);
    Serial.printf("[WiFi] 🔧 AP: %s IP:192.168.4.1\n", WIFI_AP_SSID);

    _dns.start(53, "*", IPAddress(192, 168, 4, 1));

    static bool routesSet = false;
    if (!routesSet) {
        _server.on("/",              HTTP_GET,  [this](){ _handleRoot(); });
        _server.on("/api/wifi/scan", HTTP_GET,  [this](){ _handleWiFiScan(); });
        _server.on("/api/config",    HTTP_POST, [this](){ _handleConfig(); });
        _server.on("/api/status",    HTTP_GET,  [this](){ _handleStatus(); });
        _server.onNotFound([this](){ _handleNotFound(); });
        _server.begin();
        routesSet = true;
        Serial.println(F("[WiFi]    WebServer 已启动"));
    }
}

void WiFiMgmt::_stopAP() {
    if (!_apMode) return;
    _dns.stop();
    _apMode = false;
    Serial.println(F("[WiFi]    AP 休眠 (WebServer 常驻)"));
}

// ============================================================
// Handlers
// ============================================================
void WiFiMgmt::_handleRoot() {
    File f = LittleFS.open("/index.html", "r");
    if (!f) { _server.send(404, "text/plain", "no index.html"); return; }
    _server.streamFile(f, "text/html"); f.close();
}

void WiFiMgmt::_handleWiFiScan() {
    int n = WiFi.scanNetworks();
    JsonDocument doc; JsonArray arr = doc.to<JsonArray>();
    for (int i = 0; i < n; i++) {
        JsonObject w = arr.add<JsonObject>();
        w["ssid"] = WiFi.SSID(i); w["rssi"] = WiFi.RSSI(i);
        w["enc"]  = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "open" : "wpa2";
    }
    WiFi.scanDelete();
    String json; serializeJson(doc, json);
    _server.send(200, "application/json", json);
}

void WiFiMgmt::_handleConfig() {
    if (!_server.hasArg("plain")) {
        _server.send(400, "application/json", "{\"ok\":false}"); return;
    }
    JsonDocument doc;
    if (deserializeJson(doc, _server.arg("plain"))) {
        _server.send(400, "application/json", "{\"ok\":false}"); return;
    }
    const char* ssid = doc["ssid"] | "";
    if (strlen(ssid) < 1) {
        _server.send(400, "application/json", "{\"ok\":false}"); return;
    }

    strncpy(_config.wifi_ssid, ssid, sizeof(_config.wifi_ssid)-1);
    strncpy(_config.wifi_pass, doc["password"]  | "", sizeof(_config.wifi_pass)-1);
    strncpy(_config.device_id, doc["device_id"] | "", sizeof(_config.device_id)-1);
    strncpy(_config.env_note,  doc["env_note"]  | "", sizeof(_config.env_note)-1);
    _saveConfig(); _freshProvision = true;
    Serial.printf("[WiFi] 💾 已保存: %s\n", ssid);

    _server.send(200, "application/json", "{\"ok\":true}");
    delay(200);

    _stopAP();
    _tryConnect();
}

void WiFiMgmt::_handleStatus() {
    JsonDocument doc;
    doc["gateway"] = _config.device_id;
    doc["device_id"] = _config.device_id;
    doc["env_note"]  = _config.env_note;
    String json; serializeJson(doc, json);
    _server.send(200, "application/json", json);
}

void WiFiMgmt::_handleNotFound() {
    _server.sendHeader("Location", "http://192.168.4.1/", true);
    _server.send(302, "text/plain", "");
}

// ============================================================
// 手动配网 / 重置
// ============================================================
void WiFiMgmt::startConfigPortal() {
    Serial.println(F("[WiFi] 🔧 手动配网按钮触发!"));
    _startAP();
}

void WiFiMgmt::resetSettings() {
    _stopAP();
    LittleFS.remove(CONFIG_FILE_DEVICE);
    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();
}

// ============================================================
// 长按检测 (CONFIG_BUTTON_PIN = GPIO0)
// ============================================================
bool WiFiMgmt::_checkButtonLongPress() {
    bool p = (digitalRead(CONFIG_BUTTON_PIN) == LOW);
    if (p && !_buttonPressed) { _buttonPressed = true; _buttonPressTime = millis(); }
    else if (!p && _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 f = LittleFS.open(CONFIG_FILE_DEVICE, "r");
    if (!f) return false;
    JsonDocument doc;
    if (deserializeJson(doc, f)) { f.close(); return false; }
    f.close();
    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);
    strncpy(_config.wifi_ssid,   doc["wifi_ssid"]   | "",                    sizeof(_config.wifi_ssid)-1);
    strncpy(_config.wifi_pass,   doc["wifi_pass"]   | "",                    sizeof(_config.wifi_pass)-1);
    Serial.println(F("[WiFi] ✅ 配置已加载")); 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;
    doc["wifi_ssid"]=_config.wifi_ssid; doc["wifi_pass"]=_config.wifi_pass;
    File f = LittleFS.open(CONFIG_FILE_DEVICE, "w");
    if (!f) return false;
    serializeJson(doc, f); f.close(); return true;
}

// ============================================================
// 回调 / Getter
// ============================================================
void WiFiMgmt::onProvisionState(ProvisionStateCallback cb) { _provisionStateCb = cb; }
void WiFiMgmt::_emitProvisionState(const char* state) {
    Serial.printf("[WiFi] 📡 %s\n", state);
    if (_provisionStateCb) _provisionStateCb(state);
}
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; }

+ * @brief WiFi 配网 — WebServer + LittleFS (方案一)

WiFiMgmt* WiFiMgmt::_instance = nullptr;

-// 构造函数 & 析构函数

+    , _connected(false), _freshProvision(false), _apMode(false)

+    , _pendingConnect(false), _connectStartTime(0)

+    , _lastCheckTime(0), _buttonPressTime(0), _buttonPressed(false)

+    , _ledLastToggle(0), _ledState(false), _lastLedMode(0)

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

+// 初始化 — 不阻塞

-    // 初始化引脚

-        Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));

+    if (!LittleFS.begin(true)) { Serial.println(F("[WiFi] ❌ LittleFS")); return false; }

Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));

-    // 加载配置

-        // 设置默认值

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

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

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

-    _wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);

-    _wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);

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

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

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

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

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

-    _wm.setSaveConfigCallback(_saveParamsCallback);

-    // 创建自定义参数

-    _param_mqtt_server = new WiFiManagerParameter(

-    _param_mqtt_port = new WiFiManagerParameter(

-    _param_mqtt_user = new WiFiManagerParameter(

-    _param_mqtt_pass = new WiFiManagerParameter(

-    _param_device_id = new WiFiManagerParameter(

-    _param_env_note = new WiFiManagerParameter(

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

-    // 注册自定义参数

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

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

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

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

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

+    // 有已保存 WiFi → 后台连接(不等待); 没保存 → 直接 AP

+    if (strlen(_config.wifi_ssid) > 0) {

-    // 检测按钮长按

-        Serial.println(F("[WiFi] ⚠  检测到长按, 清除配置并重启..."));

+    if (_checkButtonLongPress()) { resetSettings(); return false; }

+    if (_apMode) { _dns.processNextRequest(); }

+    _server.handleClient();  // WebServer 常驻

+    // 异步连接: 等结果

+        if (WiFi.status() == WL_CONNECTED) {

+            _pendingConnect = false; _connected = true;

+            _emitProvisionState("success");

+            Serial.printf("[WiFi] ✅ 配网成功: %s\n", WiFi.localIP().toString().c_str());

+        } else if (millis() - _connectStartTime > 15000) {

+            Serial.println(F("[WiFi] ❌ 超时"));

+    // 后台重连成功 (AP 模式期间不关 AP)

+    if (!_pendingConnect && !_connected && WiFi.status() == WL_CONNECTED) {

+        _emitProvisionState("success");

+        Serial.printf("[WiFi] ✅ 已连接: %s\n", WiFi.localIP().toString().c_str());

+    // 断开检测

if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {

-        if (WiFi.status() != WL_CONNECTED) {

+        if (WiFi.status() != WL_CONNECTED && _connected) {

-            _setLEDMode(0);  // 慢闪 = 未连接

-            Serial.println(F("[WiFi] ⚠  WiFi 断开, 尝试重连..."));

+            Serial.println(F("[WiFi] ⚠ WiFi 断开"));

-        uint32_t interval = (_wm.getConfigPortalActive()) ? 300 : 1000;

-        if (millis() - _ledLastToggle > interval) {

+    // LED: 模式切换时重置定时器, 防止闪灭

+    uint8_t mode = _apMode ? 2 : (_connected ? 1 : 0);

+        _ledState = true;   // 切模式时先保持亮

+    if (mode == 2) {           // AP 配网: 快闪 300ms

+        if (millis() - _ledLastToggle > 300) {

+            _ledLastToggle = millis(); _ledState = !_ledState;

digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);

+    } else if (mode == 0) {    // 未连接: 慢闪 1000ms

+        if (millis() - _ledLastToggle > 1000) {

+            digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);

+    } else {                   // 已连接: 常亮

+        digitalWrite(LED_WIFI_PIN, HIGH);

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

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

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

-        Serial.printf("  MAC地址:  %s\n", WiFi.macAddress().c_str());

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

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

-        // 同步自定义参数 (如果配置是从 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);

+    WiFi.begin(_config.wifi_ssid, _config.wifi_pass);

+    Serial.printf("[WiFi] 🔄 连接: %s ...\n", _config.wifi_ssid);

-    Serial.println(F("[WiFi] 🔧 手动启动配网页面..."));

-    _wm.startConfigPortal(WIFI_AP_SSID, WIFI_AP_PASSWORD);

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

+    WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD);

+    Serial.printf("[WiFi] 🔧 AP: %s IP:192.168.4.1\n", WIFI_AP_SSID);

-    Serial.println(F("[WiFi] 清除所有配置..."));

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

-        digitalWrite(LED_WIFI_PIN, !digitalRead(LED_WIFI_PIN));

+        _server.on("/",              HTTP_GET,  [this](){ _handleRoot(); });

+        _server.on("/api/wifi/scan", HTTP_GET,  [this](){ _handleWiFiScan(); });

+        _server.on("/api/config",    HTTP_POST, [this](){ _handleConfig(); });

+        _server.on("/api/status",    HTTP_GET,  [this](){ _handleStatus(); });

+        _server.onNotFound([this](){ _handleNotFound(); });

+        Serial.println(F("[WiFi]    WebServer 已启动"));

-    Serial.println(F("[WiFi] 🔄 正在重启..."));

+    Serial.println(F("[WiFi]    AP 休眠 (WebServer 常驻)"));

+    File f = LittleFS.open("/index.html", "r");

+    if (!f) { _server.send(404, "text/plain", "no index.html"); return; }

+    _server.streamFile(f, "text/html"); f.close();

-    bool pressed = (digitalRead(CONFIG_BUTTON_PIN) == LOW);

-    } else if (!pressed && _buttonPressed) {

-        if (millis() - _buttonPressTime >= BUTTON_LONG_PRESS_MS) {

+    JsonDocument doc; JsonArray arr = doc.to<JsonArray>();

+        JsonObject w = arr.add<JsonObject>();

+        w["ssid"] = WiFi.SSID(i); w["rssi"] = WiFi.RSSI(i);

+        w["enc"]  = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "open" : "wpa2";

+    String json; serializeJson(doc, json);

+    _server.send(200, "application/json", json);

-    if (!LittleFS.exists(CONFIG_FILE_DEVICE)) {

+        _server.send(400, "application/json", "{\"ok\":false}"); return;

-    File file = LittleFS.open(CONFIG_FILE_DEVICE, "r");

-    DeserializationError err = deserializeJson(doc, file);

-        Serial.printf("[WiFi] JSON 解析失败: %s\n", err.c_str());

+    if (deserializeJson(doc, _server.arg("plain"))) {

+    const char* ssid = doc["ssid"] | "";

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

+    strncpy(_config.wifi_ssid, ssid, sizeof(_config.wifi_ssid)-1);

+    strncpy(_config.wifi_pass, doc["password"]  | "", sizeof(_config.wifi_pass)-1);

+    strncpy(_config.device_id, doc["device_id"] | "", sizeof(_config.device_id)-1);

+    strncpy(_config.env_note,  doc["env_note"]  | "", sizeof(_config.env_note)-1);

+    _saveConfig(); _freshProvision = true;

+    Serial.printf("[WiFi] 💾 已保存: %s\n", ssid);

-    Serial.println(F("[WiFi] ✅ 设备配置已加载"));

-    Serial.printf("  设备名: %s, 环境: %s\n", _config.device_id, _config.env_note);

+    _server.send(200, "application/json", "{\"ok\":true}");

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

+    doc["gateway"] = _config.device_id;

+    doc["device_id"] = _config.device_id;

+    doc["env_note"]  = _config.env_note;

+    String json; serializeJson(doc, json);

+    _server.send(200, "application/json", json);

-    File file = LittleFS.open(CONFIG_FILE_DEVICE, "w");

-        Serial.println(F("[WiFi] ❌ 无法创建配置文件"));

-    if (serializeJson(doc, file) == 0) {

-        Serial.println(F("[WiFi] ❌ 配置写入失败"));

-    Serial.println(F("[WiFi] ✅ 设备配置已保存"));

+    _server.sendHeader("Location", "http://192.168.4.1/", true);

+    _server.send(302, "text/plain", "");

+// 手动配网 / 重置

-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 即可打开配网页面 (自动弹出)"));

+    Serial.println(F("[WiFi] 🔧 手动配网按钮触发!"));

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

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

-    Serial.println(F("[WiFi] 📋 新配置:"));

-                  _instance->_config.mqtt_server,

-                  _instance->_config.mqtt_port);

-    Serial.printf("  设备: %s\n", _instance->_config.device_id);

-    Serial.printf("  环境: %s\n", _instance->_config.env_note);

+    LittleFS.remove(CONFIG_FILE_DEVICE);

+    for (int i = 0; i < 10; i++) { digitalWrite(LED_WIFI_PIN, !digitalRead(LED_WIFI_PIN)); delay(100); }

+    Serial.println(F("[WiFi] 🔄 重启")); delay(500);

-    return _connected && (WiFi.status() == WL_CONNECTED);

+    bool p = (digitalRead(CONFIG_BUTTON_PIN) == LOW);

+    if (p && !_buttonPressed) { _buttonPressed = true; _buttonPressTime = millis(); }

+        if (millis() - _buttonPressTime >= BUTTON_LONG_PRESS_MS) return true;

-DeviceConfig WiFiMgmt::getConfig() const {

+    if (!LittleFS.exists(CONFIG_FILE_DEVICE)) return false;

+    File f = LittleFS.open(CONFIG_FILE_DEVICE, "r");

+    if (deserializeJson(doc, f)) { f.close(); 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);

+    strncpy(_config.wifi_ssid,   doc["wifi_ssid"]   | "",                    sizeof(_config.wifi_ssid)-1);

+    strncpy(_config.wifi_pass,   doc["wifi_pass"]   | "",                    sizeof(_config.wifi_pass)-1);

+    Serial.println(F("[WiFi] ✅ 配置已加载")); return true;

-const char* WiFiMgmt::getDeviceId() const {

+    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;

+    doc["wifi_ssid"]=_config.wifi_ssid; doc["wifi_pass"]=_config.wifi_pass;

+    File f = LittleFS.open(CONFIG_FILE_DEVICE, "w");

+    serializeJson(doc, f); f.close(); return true;

-const char* WiFiMgmt::getEnvNote() const {

-void WiFiMgmt::_setLEDMode(uint8_t mode) {

-        digitalWrite(LED_WIFI_PIN, HIGH);

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

+void WiFiMgmt::_emitProvisionState(const char* state) {

+    Serial.printf("[WiFi] 📡 %s\n", state);

+    if (_provisionStateCb) _provisionStateCb(state);

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


Now I need to update main.cpp. The system-reminder shows that after stashing, main.cpp reverted to the original WiFiManager version. I need to update it to work with the WebServer + LittleFS approach.

Key differences:
- Original uses `g_wifi.loop()` which returns bool
- New WebServer approach also has `loop()` returning bool, so that's compatible
- Need to add `onProvisionState`, `isFreshProvision`, `clearFreshProvision` calls
- Need to add `g_buttons.onProvisionLongPress` callback

Let me read the current main.cpp to see what needs to change.

Actually, the system-reminder showed the original main.cpp which has the basic structure. The key changes needed for the WebServer version are:
1. Add provision state callback
2. Add fresh provision handling
3. Add button callback for GPIO14 re-provisioning
4. Add the vital alarm delay logic (30s)
5. Add `g_presenceStartTime`, `g_vitalAlarmReady`, `g_fallStartTime` variables

Let me update main.cpp. I need to read the current version first since the system-reminder truncated it.


现在更新 main.cpp，适配 WebServer 版本：


Wasted call — file unchanged since your last Read. Refer to that earlier tool_result instead.


The system-reminder already showed the content of main.cpp. Let me look at what needs to change from the original WiFiManager version to the WebServer version.

Key changes:
1. Add global variables: `g_presenceStartTime`, `g_vitalAlarmReady`, `g_fallStartTime`
2. In setup(): add button callbacks, provision state callback, fresh provision handling
3. In loop(): add fresh provision check in MQTT reconnect
4. In checkLocalAlarms(): add fall debounce (1s), vital alarm delay (30s)

Let me update the relevant sections. The original main.cpp has a simpler checkLocalAlarms without the fall debounce or vital delay. I need to add those.

Let me make targeted edits.


Edit

// 本地报警跟踪 (防重复触发)
bool g_localFallActive       = false;
bool g_localWaterActive      = false;
bool g_localHeartAbnActive   = false;
bool g_localBreathAbnActive  = false;

// 本地报警跟踪 (防重复触发)
bool g_localFallActive       = false;
bool g_localWaterActive      = false;
bool g_localHeartAbnActive   = false;
bool g_localBreathAbnActive  = false;
uint32_t g_presenceStartTime = 0;       // 首次检测到人的时刻
bool     g_vitalAlarmReady   = false;   // 心率呼吸报警就绪 (有人30秒后)
uint32_t g_fallStartTime     = 0;       // 跌倒开始时刻 (防误判)


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

// 本地报警跟踪 (防重复触发)
bool g_localFallActive       = false;
bool g_localWaterActive      = false;
bool g_localHeartAbnActive   = false;
bool g_localBreathAbnActive  = false;
uint32_t g_presenceStartTime = 0;       // 首次检测到人的时刻
bool     g_vitalAlarmReady   = false;   // 心率呼吸报警就绪 (有人30秒后)
uint32_t g_fallStartTime     = 0;       // 跌倒开始时刻 (防误判)

+uint32_t g_presenceStartTime = 0;       // 首次检测到人的时刻

+bool     g_vitalAlarmReady   = false;   // 心率呼吸报警就绪 (有人30秒后)

+uint32_t g_fallStartTime     = 0;       // 跌倒开始时刻 (防误判)


Now I need to update the setup() function. The original has:
```cpp
g_buttons.begin();
g_buttons.onPageSwitch([]() {
    g_display.nextPage();
});
g_buttons.onAlarmDismiss([]() {
    g_alarm.clearAllLocal();
    g_radar.clearFall();
});
```

I need to add:
```cpp
g_buttons.onProvisionLongPress([]() {
    g_wifi.startConfigPortal();
});

// Provision state callback
g_wifi.onProvisionState([](const char* state) {
    g_mqtt.publishProvisionStatus(state, "");
});
g_wifi.begin();

// Fresh provision + online
if (g_wifi.isConnected()) {
    syncTime();
    // MQTT setup...
    if (g_wifi.isFreshProvision()) {
        g_mqtt.publishProvisionStatus("success", "");
        g_wifi.clearFreshProvision();
    }
}
```

Let me also read the current setup to make precise edits.


355	    });
356
357	    // 5. WiFi (WiFiManager 配网)
358	    g_wifi.begin();
359
360	    // 6. 时间同步
361	    if (g_wifi.isConnected()) {
362	        syncTime();
363	    }
364
365	    // 7. MQTT
366	    DeviceConfig cfg = g_wifi.getConfig();
367	    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
368	                     cfg.mqtt_user, cfg.mqtt_pass);
369	    g_mqtt.begin(cfg.device_id);
370
371	    // 注册 MQTT 回调
372	    g_mqtt.onAlarmCommand(handleAlarmCommand);
373	    g_mqtt.onControlCommand(handleControlCommand);
374	    g_mqtt.onProvisionCommand(handleProvisionCommand);
375
376	    // 连接 MQTT
377	    if (g_wifi.isConnected()) {
378	        g_mqtt.connect();
379	    }
380
381	    // 8. 传感器
382	    g_sensor.begin();
383
384	    // 9. 雷达任务 (核心0)
385	    xTaskCreatePinnedToCore(
386	        radarTask, "RadarTask",
387	        RADAR_TASK_STACK, nullptr,
388	        RADAR_TASK_PRIORITY, nullptr,
389	        RADAR_TASK_CORE
390	    );
391
392	    Serial.println(F("[系统] ✅ 系统就绪"));
393	    Serial.println(F("══════════════════════════════════════════\n"));
394	}
395
396	// ============================================================
397	// 主循环 (核心1)
398	// ============================================================
399
400	void loop() {
401	    uint32_t now = millis();
402
403	    // --- 0. 按钮轮询 ---
404	    g_buttons.loop();
405
406	    // --- 1. WiFi ---
407	    bool wifiOk = g_wifi.loop();
408
409	    // --- 2. MQTT (含自动重连) ---
410	    if (wifiOk) {
411	        g_mqtt.loop();
412
413	        // WiFi 刚连上但 MQTT 还没连
414	        static bool wasDisconnected = true;
415	        if (g_mqtt.isConnected()) {
416	            if (wasDisconnected) {
417	                // 刚恢复连接, 同步时间
418	                syncTime();
419	                wasDisconnected = false;
420	            }
421	        } else {
422	            wasDisconnected = true;
423	        }
424	    }
425
426	    // --- 3. 传感器 (独立，任何传感器故障不影响其他) ---
427	    g_sensor.read();
428
429	    // --- 4. 本地报警检查 (离线也工作) ---
430	    RadarRawData radar = g_radar.getData();
431	    checkLocalAlarms(radar);
432
433	    // --- 传感器故障检测 (每30秒检查一次，避免刷屏) ---
434	    static uint32_t lastFaultCheck = 0;
435	    if (now - lastFaultCheck > 30000) {
436	        lastFaultCheck = now;
437	        if (!g_sensor.isDhtValid() && millis() > 60000) {
438	            Serial.println(F("[系统] ⚠ DHT11 未检测到 (不影响运行)"));
439	        }
440	        if (g_radar.isTimeout()) {
441	            Serial.println(F("[系统] ⚠ 雷达超时无数据 (不影响运行)"));
442	        }
443	    }
444
445	    // --- 5. 报警模块更新 (定时器/闪烁) ---
446	    g_alarm.loop();
447
448	    // --- 6. 遥测上报 (1秒周期) ---
449	    if (now - g_lastReportTime >= REPORT_INTERVAL_MS) {
450	        g_lastReportTime = now;
451
452	        TelemetryData tele = buildTelemetry(radar);
453	        g_mqtt.publishAllPoints(tele, g_sequence);
454	        g_sequence++;

    });

    // 5. WiFi (WiFiManager 配网)
    g_wifi.begin();

    // 6. 时间同步
    if (g_wifi.isConnected()) {
        syncTime();
    }

    // 7. MQTT
    DeviceConfig cfg = g_wifi.getConfig();
    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
                     cfg.mqtt_user, cfg.mqtt_pass);
    g_mqtt.begin(cfg.device_id);

    // 注册 MQTT 回调
    g_mqtt.onAlarmCommand(handleAlarmCommand);
    g_mqtt.onControlCommand(handleControlCommand);
    g_mqtt.onProvisionCommand(handleProvisionCommand);

    // 连接 MQTT
    if (g_wifi.isConnected()) {
        g_mqtt.connect();
    }

    // 8. 传感器
    g_sensor.begin();

    // 9. 雷达任务 (核心0)
    xTaskCreatePinnedToCore(
        radarTask, "RadarTask",
        RADAR_TASK_STACK, nullptr,
        RADAR_TASK_PRIORITY, nullptr,
        RADAR_TASK_CORE
    );

    Serial.println(F("[系统] ✅ 系统就绪"));
    Serial.println(F("══════════════════════════════════════════\n"));
}

// ============================================================
// 主循环 (核心1)
// ============================================================

void loop() {
    uint32_t now = millis();

    // --- 0. 按钮轮询 ---
    g_buttons.loop();

    // --- 1. WiFi ---
    bool wifiOk = g_wifi.loop();

    // --- 2. MQTT (含自动重连) ---
    if (wifiOk) {
        g_mqtt.loop();

        // WiFi 刚连上但 MQTT 还没连
        static bool wasDisconnected = true;
        if (g_mqtt.isConnected()) {
            if (wasDisconnected) {
                // 刚恢复连接, 同步时间
                syncTime();
                wasDisconnected = false;
            }
        } else {
            wasDisconnected = true;
        }
    }

    // --- 3. 传感器 (独立，任何传感器故障不影响其他) ---
    g_sensor.read();

    // --- 4. 本地报警检查 (离线也工作) ---
    RadarRawData radar = g_radar.getData();
    checkLocalAlarms(radar);

    // --- 传感器故障检测 (每30秒检查一次，避免刷屏) ---
    static uint32_t lastFaultCheck = 0;
    if (now - lastFaultCheck > 30000) {
        lastFaultCheck = now;
        if (!g_sensor.isDhtValid() && millis() > 60000) {
            Serial.println(F("[系统] ⚠ DHT11 未检测到 (不影响运行)"));
        }
        if (g_radar.isTimeout()) {
            Serial.println(F("[系统] ⚠ 雷达超时无数据 (不影响运行)"));
        }
    }

    // --- 5. 报警模块更新 (定时器/闪烁) ---
    g_alarm.loop();

    // --- 6. 遥测上报 (1秒周期) ---
    if (now - g_lastReportTime >= REPORT_INTERVAL_MS) {
        g_lastReportTime = now;

        TelemetryData tele = buildTelemetry(radar);
        g_mqtt.publishAllPoints(tele, g_sequence);
        g_sequence++;


Now I can see the current setup and loop. I need to update:

1. In setup(): Add button callback for provision, add provision state callback, add fresh provision handling
2. In loop(): Add fresh provision handling in MQTT reconnect

Let me make the edits:


    // 5. WiFi (WiFiManager 配网)
    g_wifi.begin();

    // 6. 时间同步
    if (g_wifi.isConnected()) {
        syncTime();
    }

    // 7. MQTT
    DeviceConfig cfg = g_wifi.getConfig();
    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
                     cfg.mqtt_user, cfg.mqtt_pass);
    g_mqtt.begin(cfg.device_id);

    // 注册 MQTT 回调
    g_mqtt.onAlarmCommand(handleAlarmCommand);
    g_mqtt.onControlCommand(handleControlCommand);
    g_mqtt.onProvisionCommand(handleProvisionCommand);

    // 连接 MQTT
    if (g_wifi.isConnected()) {
        g_mqtt.connect();
    }

    g_buttons.onProvisionLongPress([]() {
        g_wifi.startConfigPortal();
    });

    // 5. WiFi (WebServer + LittleFS 配网)
    g_wifi.onProvisionState([](const char* state) {
        g_mqtt.publishProvisionStatus(state, "");
    });
    g_wifi.begin();

    // 6. 时间同步
    if (g_wifi.isConnected()) {
        syncTime();
    }

    // 7. MQTT
    DeviceConfig cfg = g_wifi.getConfig();
    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
                     cfg.mqtt_user, cfg.mqtt_pass);
    g_mqtt.begin(cfg.device_id);

    // 注册 MQTT 回调
    g_mqtt.onAlarmCommand(handleAlarmCommand);
    g_mqtt.onControlCommand(handleControlCommand);
    g_mqtt.onProvisionCommand(handleProvisionCommand);

    // 连接 MQTT
    if (g_wifi.isConnected()) {
        g_mqtt.connect();
        g_mqtt.publishOnline();
        if (g_wifi.isFreshProvision()) {
            g_mqtt.publishProvisionStatus("success", "");
            g_wifi.clearFreshProvision();
        }
    }


    // 5. WiFi (WiFiManager 配网)
    g_wifi.begin();

    // 6. 时间同步
    if (g_wifi.isConnected()) {
        syncTime();
    }

    // 7. MQTT
    DeviceConfig cfg = g_wifi.getConfig();
    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
                     cfg.mqtt_user, cfg.mqtt_pass);
    g_mqtt.begin(cfg.device_id);

    // 注册 MQTT 回调
    g_mqtt.onAlarmCommand(handleAlarmCommand);
    g_mqtt.onControlCommand(handleControlCommand);
    g_mqtt.onProvisionCommand(handleProvisionCommand);

    // 连接 MQTT
    if (g_wifi.isConnected()) {
        g_mqtt.connect();
    }

    g_buttons.onProvisionLongPress([]() {
        g_wifi.startConfigPortal();
    });

    // 5. WiFi (WebServer + LittleFS 配网)
    g_wifi.onProvisionState([](const char* state) {
        g_mqtt.publishProvisionStatus(state, "");
    });
    g_wifi.begin();

    // 6. 时间同步
    if (g_wifi.isConnected()) {
        syncTime();
    }

    // 7. MQTT
    DeviceConfig cfg = g_wifi.getConfig();
    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
                     cfg.mqtt_user, cfg.mqtt_pass);
    g_mqtt.begin(cfg.device_id);

    // 注册 MQTT 回调
    g_mqtt.onAlarmCommand(handleAlarmCommand);
    g_mqtt.onControlCommand(handleControlCommand);
    g_mqtt.onProvisionCommand(handleProvisionCommand);

    // 连接 MQTT
    if (g_wifi.isConnected()) {
        g_mqtt.connect();
        g_mqtt.publishOnline();
        if (g_wifi.isFreshProvision()) {
            g_mqtt.publishProvisionStatus("success", "");
            g_wifi.clearFreshProvision();
        }
    }

+    g_buttons.onProvisionLongPress([]() {

+    // 5. WiFi (WebServer + LittleFS 配网)

+    g_wifi.onProvisionState([](const char* state) {

+        g_mqtt.publishProvisionStatus(state, "");

+        if (g_wifi.isFreshProvision()) {

+            g_mqtt.publishProvisionStatus("success", "");

+            g_wifi.clearFreshProvision();


Now I need to add the fresh provision handling in the loop() MQTT reconnect section. I also need to add `g_mqtt.publishOnline()` on reconnect.

Let me update the loop's MQTT reconnect section:


更新 loop() 里的 MQTT 重连处理，加上 fresh provision：


        static bool wasDisconnected = true;
        if (g_mqtt.isConnected()) {
            if (wasDisconnected) {
                // 刚恢复连接, 同步时间
                syncTime();
                wasDisconnected = false;
            }
        } else {
            wasDisconnected = true;
        }

        static bool wasDisconnected = true;
        if (g_mqtt.isConnected()) {
            if (wasDisconnected) {
                syncTime();
                g_mqtt.publishOnline();
                if (g_wifi.isFreshProvision()) {
                    g_mqtt.publishProvisionStatus("success", "");
                    g_wifi.clearFreshProvision();
                }
                wasDisconnected = false;
            }
        } else {
            wasDisconnected = true;
        }


现在都是问题，我们换回刚才配网模式没改的版本


        static bool wasDisconnected = true;
        if (g_mqtt.isConnected()) {
            if (wasDisconnected) {
                // 刚恢复连接, 同步时间
                syncTime();
                wasDisconnected = false;
            }
        } else {
            wasDisconnected = true;
        }

        static bool wasDisconnected = true;
        if (g_mqtt.isConnected()) {
            if (wasDisconnected) {
                syncTime();
                g_mqtt.publishOnline();
                if (g_wifi.isFreshProvision()) {
                    g_mqtt.publishProvisionStatus("success", "");
                    g_wifi.clearFreshProvision();
                }
                wasDisconnected = false;
            }
        } else {
            wasDisconnected = true;
        }

-                // 刚恢复连接, 同步时间

+                g_mqtt.publishOnline();

+                if (g_wifi.isFreshProvision()) {

+                    g_mqtt.publishProvisionStatus("success", "");

+                    g_wifi.clearFreshProvision();


Now I need to check the checkLocalAlarms function. The original version in the initial commit might not have the fall debounce (1s) and vital alarm delay (30s) logic. Let me check.

From the system-reminder, the main.cpp was truncated at line 237. Let me read the checkLocalAlarms function.


230
231	// ============================================================
232	// 本地报警检查 (离线可用)
233	// ============================================================
234
235	void checkLocalAlarms(const RadarRawData& radar) {
236	    uint32_t now = millis();
237
238	    // --- 跌倒检测 (本地紧急报警, 立即触发) ---
239	    if (radar.fall && radar.radarValid && !g_localFallActive) {
240	        g_localFallActive = true;
241	        g_alarm.triggerLocal(AlarmManager::ALARM_FALL);
242	    } else if (!radar.fall && g_localFallActive) {
243	        g_localFallActive = false;
244	        g_alarm.clearLocal(AlarmManager::ALARM_FALL);
245	    }
246
247	    // --- 水浸检测 ---
248	    bool waterLeak = !g_sensor.getRainDO() && g_sensor.isRainValid();
249	    if (waterLeak && !g_localWaterActive) {
250	        g_localWaterActive = true;
251	        g_alarm.triggerLocal(AlarmManager::ALARM_WATER_LEAK);
252	    } else if (!waterLeak && g_localWaterActive) {
253	        g_localWaterActive = false;
254	        g_alarm.clearLocal(AlarmManager::ALARM_WATER_LEAK);
255	    }
256
257	    // --- 心率异常 (离线本地判定, 阈值预留后续调整) ---
258	    if (radar.vitalValid && radar.heartRate > 0) {
259	        bool heartAbn = (radar.heartRate < HEART_RATE_MIN || radar.heartRate > HEART_RATE_MAX);
260	        if (heartAbn && !g_localHeartAbnActive) {
261	            g_localHeartAbnActive = true;
262	            g_alarm.triggerLocal(AlarmManager::ALARM_HEART_ABNORMAL);
263	        } else if (!heartAbn && g_localHeartAbnActive) {
264	            g_localHeartAbnActive = false;
265	            g_alarm.clearLocal(AlarmManager::ALARM_HEART_ABNORMAL);
266	        }
267	    }
268
269	    // --- 呼吸异常 ---
270	    if (radar.vitalValid && radar.breathRate > 0) {
271	        bool breathAbn = (radar.breathRate < BREATH_RATE_MIN || radar.breathRate > BREATH_RATE_MAX);
272	        if (breathAbn && !g_localBreathAbnActive) {
273	            g_localBreathAbnActive = true;
274	            g_alarm.triggerLocal(AlarmManager::ALARM_BREATH_ABNORMAL);
275	        } else if (!breathAbn && g_localBreathAbnActive) {
276	            g_localBreathAbnActive = false;
277	            g_alarm.clearLocal(AlarmManager::ALARM_BREATH_ABNORMAL);
278	        }
279	    }
280	}
281
282	// ============================================================
283	// 调试输出
284	// ============================================================
285
286	void printDebug(const TelemetryData& t) {
287	    static uint32_t lastPrint = 0;
288	    if (millis() - lastPrint < 5000) return;
289	    lastPrint = millis();
290
291	    Serial.println(F("─── 遥测数据 ───"));
292	    Serial.printf("  环境: T=%.1f°C H=%.1f%% (温湿度:%d)\n",
293	                  t.temp, t.humi, t.dhtValid);
294	    Serial.printf("  存在:%d 运动:%d 跌倒:%d 驻留:%d (雷达:%d)\n",
295	                  t.presence, t.motion, t.fall, t.resident, t.radarValid);
296	    Serial.printf("  心率:%d 呼吸:%d (体征:%d)\n",
297	                  t.heartRate, t.breathRate, t.vitalValid);
298	    Serial.printf("  水浸:%d (水浸有效:%d) | WiFi:%d 蜂鸣器:%d\n",
299	                  t.rainDO, t.rainValid, t.wifiReady, t.buzzerState);
300	    Serial.printf("  运行:%ds 序号:%d\n", t.uptimeSec, g_sequence);
301	    Serial.println(F("─────────────────"));
302	}
303
304	// ============================================================
305	// 雷达任务 - 核心0 专用
306	// ============================================================
307
308	void radarTask(void* param) {
309	    Serial.println(F("[任务] 📡 雷达任务启动 (核心0)"));

// ============================================================
// 本地报警检查 (离线可用)
// ============================================================

void checkLocalAlarms(const RadarRawData& radar) {
    uint32_t now = millis();

    // --- 跌倒检测 (本地紧急报警, 立即触发) ---
    if (radar.fall && radar.radarValid && !g_localFallActive) {
        g_localFallActive = true;
        g_alarm.triggerLocal(AlarmManager::ALARM_FALL);
    } else if (!radar.fall && g_localFallActive) {
        g_localFallActive = false;
        g_alarm.clearLocal(AlarmManager::ALARM_FALL);
    }

    // --- 水浸检测 ---
    bool waterLeak = !g_sensor.getRainDO() && g_sensor.isRainValid();
    if (waterLeak && !g_localWaterActive) {
        g_localWaterActive = true;
        g_alarm.triggerLocal(AlarmManager::ALARM_WATER_LEAK);
    } else if (!waterLeak && g_localWaterActive) {
        g_localWaterActive = false;
        g_alarm.clearLocal(AlarmManager::ALARM_WATER_LEAK);
    }

    // --- 心率异常 (离线本地判定, 阈值预留后续调整) ---
    if (radar.vitalValid && radar.heartRate > 0) {
        bool heartAbn = (radar.heartRate < HEART_RATE_MIN || radar.heartRate > HEART_RATE_MAX);
        if (heartAbn && !g_localHeartAbnActive) {
            g_localHeartAbnActive = true;
            g_alarm.triggerLocal(AlarmManager::ALARM_HEART_ABNORMAL);
        } else if (!heartAbn && g_localHeartAbnActive) {
            g_localHeartAbnActive = false;
            g_alarm.clearLocal(AlarmManager::ALARM_HEART_ABNORMAL);
        }
    }

    // --- 呼吸异常 ---
    if (radar.vitalValid && radar.breathRate > 0) {
        bool breathAbn = (radar.breathRate < BREATH_RATE_MIN || radar.breathRate > BREATH_RATE_MAX);
        if (breathAbn && !g_localBreathAbnActive) {
            g_localBreathAbnActive = true;
            g_alarm.triggerLocal(AlarmManager::ALARM_BREATH_ABNORMAL);
        } else if (!breathAbn && g_localBreathAbnActive) {
            g_localBreathAbnActive = false;
            g_alarm.clearLocal(AlarmManager::ALARM_BREATH_ABNORMAL);
        }
    }
}

// ============================================================
// 调试输出
// ============================================================

void printDebug(const TelemetryData& t) {
    static uint32_t lastPrint = 0;
    if (millis() - lastPrint < 5000) return;
    lastPrint = millis();

    Serial.println(F("─── 遥测数据 ───"));
    Serial.printf("  环境: T=%.1f°C H=%.1f%% (温湿度:%d)\n",
                  t.temp, t.humi, t.dhtValid);
    Serial.printf("  存在:%d 运动:%d 跌倒:%d 驻留:%d (雷达:%d)\n",
                  t.presence, t.motion, t.fall, t.resident, t.radarValid);
    Serial.printf("  心率:%d 呼吸:%d (体征:%d)\n",
                  t.heartRate, t.breathRate, t.vitalValid);
    Serial.printf("  水浸:%d (水浸有效:%d) | WiFi:%d 蜂鸣器:%d\n",
                  t.rainDO, t.rainValid, t.wifiReady, t.buzzerState);
    Serial.printf("  运行:%ds 序号:%d\n", t.uptimeSec, g_sequence);
    Serial.println(F("─────────────────"));
}

// ============================================================
// 雷达任务 - 核心0 专用
// ============================================================

void radarTask(void* param) {
    Serial.println(F("[任务] 📡 雷达任务启动 (核心0)"));


I see the checkLocalAlarms is simpler - no fall debounce (1s), no vital alarm delay (30s), no presence-based gating for heart/breath alarms. I should add these back since they were in the working version.

Let me update checkLocalAlarms to the version with:
1. Fall debounce: 1s continuous fall before triggering
2. Vital alarm delay: 30s after first detecting presence
3. Clear heart/breath alarms when no presence


更新 `checkLocalAlarms` 加回跌倒防抖和心率呼吸 30 秒稳定期：