1	/**
2	 * @file radar_manager.h
3	 * @brief R60ABD1 毫米波雷达管理模块
4	 */
5
6	#ifndef RADAR_MANAGER_H
7	#define RADAR_MANAGER_H
8
9	#include <Arduino.h>
10	#include "config.h"
11	#include "radar_protocol.h"
12
13	// 雷达原始数据结构
14	struct RadarRawData {
15	    bool     presence;       // 有人/无人
16	    bool     motion;         // 运动 (false=静止)
17	    bool     fall;           // 跌倒
18	    bool     resident;       // 驻留
19
20	    uint8_t  heartRate;      // 心率
21	    uint8_t  breathRate;     // 呼吸
22	    uint8_t  body;           // 体动/能量值
23	    uint16_t distance;       // 距离 cm
24
25	    bool     radarValid;     // 雷达有效性
26	    bool     vitalValid;     // 生命体征有效性
27
28	    uint32_t lastUpdate;
29	};
30
31	typedef void (*RadarCallback)(const RadarRawData& data);
32
33	class RadarManager {
34	public:
35	    RadarManager();
36	    ~RadarManager();
37
38	    bool begin();
39	    void loop();
40
41	    /** 获取最新数据 */
42	    RadarRawData getData() const;
43
44	    /** 数据更新回调 */
45	    void onDataUpdate(RadarCallback cb);
46
47	    /** 发送命令 */
48	    void sendCommand(uint8_t control, uint8_t command,
49	                     const uint8_t* data = nullptr, uint16_t len = 0);
50	    void queryInitStatus();
51	    void queryDetectRange();
52
53	    bool isInitialized() const { return _initialized; }
54
55	    /** 检查雷达是否超时未收到数据 (>5秒无有效帧) */
56	    bool isTimeout() const {
57	        return (_frameCount > 0) && (millis() - _data.lastUpdate > 5000);
58	    }
59	    /** 雷达数据是否有效 */
60	    bool isDataValid() const { return _data.radarValid; }
61
62	private:
63	    void _parseByte(uint8_t byte);
64	    void _resetParser();
65	    bool _validateChecksum();
66	    void _processCompleteFrame();
67
68	    void _extractPresence(const RadarFrame& frame);
69	    void _extractBreathing(const RadarFrame& frame);
70	    void _extractHeartRate(const RadarFrame& frame);
71	    void _extractSleep(const RadarFrame& frame);
72	    void _extractWorkStatus(const RadarFrame& frame);
73	    void _extractDetectRange(const RadarFrame& frame);
74
75	    uint8_t _calcChecksum(const uint8_t* data, uint16_t len);
76	    void _notifyUpdate();
77
78	    HardwareSerial* _serial;
79	    FrameState      _state;
80	    RadarFrame      _currentFrame;
81	    uint16_t        _dataIndex;
82	    RadarRawData    _data;
83	    bool            _initialized;
84
85	    RadarCallback   _callback;
86	    uint32_t        _frameCount;
87	    uint32_t        _errorCount;
88	};
89
90	#endif // RADAR_MANAGER_H
91

/**
 * @file radar_manager.h
 * @brief R60ABD1 毫米波雷达管理模块
 */

#ifndef RADAR_MANAGER_H
#define RADAR_MANAGER_H

#include <Arduino.h>
#include "config.h"
#include "radar_protocol.h"

// 雷达原始数据结构
struct RadarRawData {
    bool     presence;       // 有人/无人
    bool     motion;         // 运动 (false=静止)
    bool     fall;           // 跌倒
    bool     resident;       // 驻留

    uint8_t  heartRate;      // 心率
    uint8_t  breathRate;     // 呼吸
    uint8_t  body;           // 体动/能量值
    uint16_t distance;       // 距离 cm

    bool     radarValid;     // 雷达有效性
    bool     vitalValid;     // 生命体征有效性

    uint32_t lastUpdate;
};

typedef void (*RadarCallback)(const RadarRawData& data);

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

    bool begin();
    void loop();

    /** 获取最新数据 */
    RadarRawData getData() const;

    /** 数据更新回调 */
    void onDataUpdate(RadarCallback cb);

    /** 发送命令 */
    void sendCommand(uint8_t control, uint8_t command,
                     const uint8_t* data = nullptr, uint16_t len = 0);
    void queryInitStatus();
    void queryDetectRange();

    bool isInitialized() const { return _initialized; }

    /** 检查雷达是否超时未收到数据 (>5秒无有效帧) */
    bool isTimeout() const {
        return (_frameCount > 0) && (millis() - _data.lastUpdate > 5000);
    }
    /** 雷达数据是否有效 */
    bool isDataValid() const { return _data.radarValid; }

private:
    void _parseByte(uint8_t byte);
    void _resetParser();
    bool _validateChecksum();
    void _processCompleteFrame();

    void _extractPresence(const RadarFrame& frame);
    void _extractBreathing(const RadarFrame& frame);
    void _extractHeartRate(const RadarFrame& frame);
    void _extractSleep(const RadarFrame& frame);
    void _extractWorkStatus(const RadarFrame& frame);
    void _extractDetectRange(const RadarFrame& frame);

    uint8_t _calcChecksum(const uint8_t* data, uint16_t len);
    void _notifyUpdate();

    HardwareSerial* _serial;
    FrameState      _state;
    RadarFrame      _currentFrame;
    uint16_t        _dataIndex;
    RadarRawData    _data;
    bool            _initialized;

    RadarCallback   _callback;
    uint32_t        _frameCount;
    uint32_t        _errorCount;
};

#endif // RADAR_MANAGER_H


Now I have a good understanding of the current system. Let me summarize what's there and plan the expansion.

Current alarm system:
1. `AlarmManager` - handles buzzer + LED for alarms
2. `checkLocalAlarms()` in main.cpp - checks for fall, water leak, heart abnormal, breath abnormal
3. Server-side alarm commands via MQTT

What the user wants: "拓展所有的报警逻辑" - expand all alarm logic.

Let me think about what this means practically. The current system has basic alarm triggers, but there are many areas that could be expanded:

1. **Buzzer patterns** - Currently only has "3 beeps pause" pattern for local alarms. Different alarm types should have different patterns.
2. **LED patterns** - Falls get 200ms flash, others get 500ms. Could be more nuanced.
3. **Alarm priority** - Some alarms (fall) are more critical than others (heart rate). Priority should affect behavior.
4. **Alarm persistence** - Alarms should persist until acknowledged, not auto-clear with data changes.
5. **Display integration** - OLED should show alarm status more prominently.
6. **MQTT reporting** - Local alarms should be reported to the server.
7. **Alarm escalation** - If an alarm persists for too long, escalate (e.g., faster buzzer).
8. **Sensor fault alarms** - When sensors go offline (DHT, radar timeout, water sensor offline).
9. **Temperature/humidity alarms** - DHT data can trigger alarms (currently only heart rate and breath have thresholds but temp/humidity thresholds are defined in config.h as `TEMP_LOW`, `TEMP_HIGH`, `HUMI_LOW`, `HUMI_HIGH` but not used in main.cpp!)
10. **Buzzer manual dismiss** - The button to dismiss alarms exists but the buzzer pattern should acknowledge it better.

Let me plan a comprehensive expansion. The user's request is broad ("拓展所有的报警逻辑"), so I need to make the system more complete and robust.

Here's my plan:

### 1. AlarmManager expansion
- **Priority system**: FALL > WATER_LEAK > HEART_ABNORMAL > BREATH_ABNORMAL > SENSOR_FAULT
- **Buzzer patterns per priority**:
  - Fall: continuous rapid beeping (50ms on, 50ms off) — most urgent
  - Water leak: 5 rapid beeps then pause
  - Heart/breath: current 3 beeps pause
  - Sensor fault: 1 long beep every 3 seconds (gentle reminder)
- **LED patterns**: faster for higher priority
- **Alarm logging**: keep a log of recent alarms with timestamps
- **Alarm confirmation**: acknowledge that an alarm was dismissed by user

### 2. checkLocalAlarms() expansion in main.cpp
- Add **DHT temp/humidity check** (config.h already has thresholds)
- Add **radar timeout alarm** (sensor fault)
- Add **water sensor offline alarm**
- Add **alarm auto-recovery with hysteresis** (prevent rapid on/off cycling)
- Report local alarms via MQTT when connected

### 3. Display integration
- Show alarm count on every page
- Flash alarm indicator on OLED when active

Actually, let me think about this differently. The user said "拓展所有的报警逻辑". Let me focus on practical, impactful expansions:

1. **Add sensor fault detection as alarms** (radar timeout, DHT fault, water sensor offline - these should show on OLED as alarms too)
2. **Add DHT temp/humidity threshold alarms** (thresholds are defined but not used)
3. **Different buzzer patterns for different alarm types**
4. **Alarm priority system**
5. **Report local alarms to MQTT** (currently only server-triggered alarms are reported)
6. **Better OLED alarm status display**

Let me also look at what `checkLocalAlarms` doesn't cover but should:
- Temperature too high/low
- Humidity too high/low
- Radar sensor timeout
- DHT sensor fault
- Water sensor offline

Let me now plan the implementation. I'll need to:

1. Expand `AlarmManager` with:
   - New alarm types: `ALARM_TEMP_HIGH`, `ALARM_TEMP_LOW`, `ALARM_HUMI_HIGH`, `ALARM_HUMI_LOW`, `ALARM_SENSOR_FAULT`
   - Priority-based buzzer patterns
   - Callback for alarm state changes (to report via MQTT)

2. Expand `checkLocalAlarms()` in main.cpp with:
   - Temperature threshold checks
   - Humidity threshold checks
   - Sensor fault detection
   - Hysteresis (to prevent rapid cycling)

3. Update OLED to show alarm indicators

Let me start implementing. This is a significant change, so let me use plan mode.

Actually, the user said "现在说拓展所有的报警逻辑" and "嗯" - they want me to just do it. Let me proceed.

Let me start by expanding the alarm_manager.h with new alarm types and features.

Actually, let me re-read the user's intent. They said "现在说拓展所有的报警逻辑" - "now let's talk about expanding all the alarm logic". This could mean they want to discuss the plan first, or they want me to just implement it.

Given the previous pattern (they said "嗯" to my previous implementation), I think they want me to just analyze and implement.

Let me plan carefully and then implement. The key areas to expand:

**AlarmManager changes:**
1. Add SENSOR_FAULT alarm type
2. Add TEMP/HUMI alarm types
3. Priority-based buzzer patterns
4. Alarm change callback (for MQTT reporting)

**main.cpp changes:**
1. Add temp/humidity threshold checking to checkLocalAlarms
2. Add radar timeout to checkLocalAlarms
3. Add sensor fault tracking
4. Report local alarms via MQTT

**display_manager.cpp changes:**
1. Show alarm active indicator on all pages
2. Show the active alarm type

Let me start coding. I'll keep the changes focused and practical.

For the alarm types, I think instead of adding separate types for temp high/low and humi high/low, I should keep it simpler with:
- `ALARM_TEMP_ABNORMAL`
- `ALARM_HUMI_ABNORMAL`
- `ALARM_SENSOR_FAULT`

And the specific nature (high/low) is just contextual info.

Let me implement this step by step.


看完了整个报警体系。当前的问题：

- 温湿度阈值在 `config.h` 定义好了但根本没用到
- 雷达超时、传感器离线没有报警
- 所有报警用同一种蜂鸣器模式，不区分紧急程度
- 本地报警不上报 MQTT
- OLED 上看不到活跃报警提示

我来全面拓展。


TodoWrite

扩展 AlarmManager：新报警类型 + 优先级蜂鸣 + 回调

扩展 main.cpp：温湿度/传感器故障检测 + MQTT上报 + 防抖

OLED 显示活跃报警提示


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

扩展 AlarmManager：新报警类型 + 优先级蜂鸣 + 回调

扩展 main.cpp：温湿度/传感器故障检测 + MQTT上报 + 防抖

OLED 显示活跃报警提示


现在说拓展所有的报警逻辑


Let me start with alarm_manager.h - expanding alarm types, adding priority, adding callback, and improving the structure.


先扩展报警管理器头文件：


Write

/**
 * @file alarm_manager.h
 * @brief 报警管理器 - 多类型分优先级报警 (本地判定 + 服务端指令)
 *
 * 优先级: 跌倒 > 水浸 > 心率异常 > 呼吸异常 > 温湿度异常 > 传感器故障
 *
 * 本地模式: 离线也能用, 全部类型本地判定触发
 * 服务端模式: 收到 cmd/{gateid}/alarm, alarm/restore 执行联动
 * 防抖: 传感器故障需连续 N 次确认才触发, 避免误报
 */

#ifndef ALARM_MANAGER_H
#define ALARM_MANAGER_H

#include <Arduino.h>
#include "config.h"

// 报警回调 — 通知外部 (如 MQTT 上报)
typedef void (*AlarmCallback)(uint8_t type, bool triggered, const char* desc);

class AlarmManager {
public:
    // 告警类型 (按优先级从高到低)
    enum AlarmType : uint8_t {
        ALARM_NONE = 0,
        ALARM_FALL,               // 跌倒 (本地 + 服务端) ★★★★★
        ALARM_WATER_LEAK,         // 水浸 (本地 + 服务端) ★★★★
        ALARM_HEART_ABNORMAL,     // 心率异常 (本地) ★★★
        ALARM_BREATH_ABNORMAL,    // 呼吸异常 (本地) ★★★
        ALARM_TEMP_ABNORMAL,      // 温度异常 (本地) ★★
        ALARM_HUMI_ABNORMAL,      // 湿度异常 (本地) ★★
        ALARM_SENSOR_FAULT,       // 传感器故障 (本地) ★
        ALARM_SERVER,             // 服务端下发 (通用)
        ALARM_COUNT
    };

    AlarmManager();
    ~AlarmManager();

    bool begin();
    void loop();

    // ============================================================
    // 本地报警接口 (离线可用)
    // ============================================================

    /** 触发本地报警 (防抖由调用方处理) */
    void triggerLocal(AlarmType type, const char* detail = nullptr);
    /** 清除本地报警 */
    void clearLocal(AlarmType type);
    /** 清除所有本地报警 (含防抖计数器) */
    void clearAllLocal();

    // ============================================================
    // 服务端指令接口 (协议规范 §5.1, §5.2)
    // ============================================================

    /** 执行服务端告警指令 */
    void executeAlarm(const AlarmCommand& cmd);
    /** 执行服务端恢复指令 */
    void executeRestore(const AlarmCommand& cmd);
    /** 蜂鸣器测试 */
    void buzzerTest(uint32_t durationSec);

    // ============================================================
    // 回调注册
    // ============================================================

    /** 报警状态变化回调 (触发/清除时调用, 用于MQTT上报) */
    void onAlarmChange(AlarmCallback cb) { _alarmCb = cb; }

    // ============================================================
    // 状态查询
    // ============================================================

    bool isBuzzerOn() const           { return _buzzerOn; }
    bool hasActiveAlarm() const       { return _activeCount > 0; }
    uint8_t getActiveCount() const    { return _activeCount; }
    AlarmType getHighestAlarm() const;
    const char* getAlarmName(AlarmType type) const;
    const char* getActiveAlarmStr() const;

private:
    // 报警优先级 (越小越紧急)
    static uint8_t _priority(AlarmType t);

    void _setBuzzer(bool on);
    void _setLED(bool on);
    void _updateBuzzer();
    void _updateLED();
    uint32_t _buzzerIntervalFor(AlarmType type);

    // 蜂鸣器/LED 状态
    bool     _buzzerOn;
    bool     _ledOn;
    uint32_t _buzzerOffTime;       // 定时关闭时刻 (0=手动/无限)
    uint32_t _buzzerToggleTime;    // 间歇切换时刻
    bool     _buzzerBeepState;     // 间歇状态 (响/停)
    uint8_t  _beepCount;           // 当前周期哔了几声
    uint8_t  _beepsPerCycle;       // 当前报警类型每周期几声
    uint32_t _beepOnMs;            // 每声持续 ms
    uint32_t _beepOffMs;           // 每声间隔 ms
    uint32_t _cyclePauseMs;        // 周期间停顿 ms

    // 活动报警列表 (最多 5 个同时)
    static const uint8_t MAX_ACTIVE = 5;
    AlarmType _activeAlarms[MAX_ACTIVE];
    uint8_t   _activeCount;

    // 当前详情 (用于上报)
    char      _activeDetail[64];

    // LED 闪烁
    uint32_t _lastLEDToggle;

    // 回调
    AlarmCallback _alarmCb;
};

#endif // ALARM_MANAGER_H


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

/**
 * @file alarm_manager.h
 * @brief 报警管理器 - 多类型分优先级报警 (本地判定 + 服务端指令)
 *
 * 优先级: 跌倒 > 水浸 > 心率异常 > 呼吸异常 > 温湿度异常 > 传感器故障
 *
 * 本地模式: 离线也能用, 全部类型本地判定触发
 * 服务端模式: 收到 cmd/{gateid}/alarm, alarm/restore 执行联动
 * 防抖: 传感器故障需连续 N 次确认才触发, 避免误报
 */

#ifndef ALARM_MANAGER_H
#define ALARM_MANAGER_H

#include <Arduino.h>
#include "config.h"

// 报警回调 — 通知外部 (如 MQTT 上报)
typedef void (*AlarmCallback)(uint8_t type, bool triggered, const char* desc);

class AlarmManager {
public:
    // 告警类型 (按优先级从高到低)
    enum AlarmType : uint8_t {
        ALARM_NONE = 0,
        ALARM_FALL,               // 跌倒 (本地 + 服务端) ★★★★★
        ALARM_WATER_LEAK,         // 水浸 (本地 + 服务端) ★★★★
        ALARM_HEART_ABNORMAL,     // 心率异常 (本地) ★★★
        ALARM_BREATH_ABNORMAL,    // 呼吸异常 (本地) ★★★
        ALARM_TEMP_ABNORMAL,      // 温度异常 (本地) ★★
        ALARM_HUMI_ABNORMAL,      // 湿度异常 (本地) ★★
        ALARM_SENSOR_FAULT,       // 传感器故障 (本地) ★
        ALARM_SERVER,             // 服务端下发 (通用)
        ALARM_COUNT
    };

    AlarmManager();
    ~AlarmManager();

    bool begin();
    void loop();

    // ============================================================
    // 本地报警接口 (离线可用)
    // ============================================================

    /** 触发本地报警 (防抖由调用方处理) */
    void triggerLocal(AlarmType type, const char* detail = nullptr);
    /** 清除本地报警 */
    void clearLocal(AlarmType type);
    /** 清除所有本地报警 (含防抖计数器) */
    void clearAllLocal();

    // ============================================================
    // 服务端指令接口 (协议规范 §5.1, §5.2)
    // ============================================================

    /** 执行服务端告警指令 */
    void executeAlarm(const AlarmCommand& cmd);
    /** 执行服务端恢复指令 */
    void executeRestore(const AlarmCommand& cmd);
    /** 蜂鸣器测试 */
    void buzzerTest(uint32_t durationSec);

    // ============================================================
    // 回调注册
    // ============================================================

    /** 报警状态变化回调 (触发/清除时调用, 用于MQTT上报) */
    void onAlarmChange(AlarmCallback cb) { _alarmCb = cb; }

    // ============================================================
    // 状态查询
    // ============================================================

    bool isBuzzerOn() const           { return _buzzerOn; }
    bool hasActiveAlarm() const       { return _activeCount > 0; }
    uint8_t getActiveCount() const    { return _activeCount; }
    AlarmType getHighestAlarm() const;
    const char* getAlarmName(AlarmType type) const;
    const char* getActiveAlarmStr() const;

private:
    // 报警优先级 (越小越紧急)
    static uint8_t _priority(AlarmType t);

    void _setBuzzer(bool on);
    void _setLED(bool on);
    void _updateBuzzer();
    void _updateLED();
    uint32_t _buzzerIntervalFor(AlarmType type);

    // 蜂鸣器/LED 状态
    bool     _buzzerOn;
    bool     _ledOn;
    uint32_t _buzzerOffTime;       // 定时关闭时刻 (0=手动/无限)
    uint32_t _buzzerToggleTime;    // 间歇切换时刻
    bool     _buzzerBeepState;     // 间歇状态 (响/停)
    uint8_t  _beepCount;           // 当前周期哔了几声
    uint8_t  _beepsPerCycle;       // 当前报警类型每周期几声
    uint32_t _beepOnMs;            // 每声持续 ms
    uint32_t _beepOffMs;           // 每声间隔 ms
    uint32_t _cyclePauseMs;        // 周期间停顿 ms

    // 活动报警列表 (最多 5 个同时)
    static const uint8_t MAX_ACTIVE = 5;
    AlarmType _activeAlarms[MAX_ACTIVE];
    uint8_t   _activeCount;

    // 当前详情 (用于上报)
    char      _activeDetail[64];

    // LED 闪烁
    uint32_t _lastLEDToggle;

    // 回调
    AlarmCallback _alarmCb;
};

#endif // ALARM_MANAGER_H

- * @brief 报警管理器 - 双模式 (本地判定 + 服务端指令)

+ * @brief 报警管理器 - 多类型分优先级报警 (本地判定 + 服务端指令)

- * 本地模式: 离线也能用, 跌倒/水浸/心率异常/呼吸异常 本地判定触发

+ * 优先级: 跌倒 > 水浸 > 心率异常 > 呼吸异常 > 温湿度异常 > 传感器故障

+ * 本地模式: 离线也能用, 全部类型本地判定触发

+ * 防抖: 传感器故障需连续 N 次确认才触发, 避免误报

+// 报警回调 — 通知外部 (如 MQTT 上报)

+typedef void (*AlarmCallback)(uint8_t type, bool triggered, const char* desc);

-    // 告警类型 (本地 + 服务端共用)

+    // 告警类型 (按优先级从高到低)

-        ALARM_FALL,             // 跌倒 (本地 + 服务端)

-        ALARM_WATER_LEAK,       // 水浸 (本地 + 服务端)

-        ALARM_HEART_ABNORMAL,   // 心率异常 (本地)

-        ALARM_BREATH_ABNORMAL,  // 呼吸异常 (本地)

-        ALARM_SERVER,           // 服务端下发 (通用)

+        ALARM_FALL,               // 跌倒 (本地 + 服务端) ★★★★★

+        ALARM_WATER_LEAK,         // 水浸 (本地 + 服务端) ★★★★

+        ALARM_HEART_ABNORMAL,     // 心率异常 (本地) ★★★

+        ALARM_BREATH_ABNORMAL,    // 呼吸异常 (本地) ★★★

+        ALARM_TEMP_ABNORMAL,      // 温度异常 (本地) ★★

+        ALARM_HUMI_ABNORMAL,      // 湿度异常 (本地) ★★

+        ALARM_SENSOR_FAULT,       // 传感器故障 (本地) ★

+        ALARM_SERVER,             // 服务端下发 (通用)

// 本地报警接口 (离线可用)

-    /** 触发本地报警 */

+    /** 触发本地报警 (防抖由调用方处理) */

+    void triggerLocal(AlarmType type, const char* detail = nullptr);

/** 清除本地报警 */

-    /** 清除所有本地报警 */

+    /** 清除所有本地报警 (含防抖计数器) */

-    // 状态查询 (上报用)

+    // 回调注册

-    bool isBuzzerOn() const      { return _buzzerOn; }

-    bool hasActiveAlarm() const  { return _activeCount > 0; }

-    const char* getActiveAlarmType() const;

+    /** 报警状态变化回调 (触发/清除时调用, 用于MQTT上报) */

+    void onAlarmChange(AlarmCallback cb) { _alarmCb = cb; }

+    // 状态查询

+    bool isBuzzerOn() const           { return _buzzerOn; }

+    bool hasActiveAlarm() const       { return _activeCount > 0; }

+    uint8_t getActiveCount() const    { return _activeCount; }

+    const char* getAlarmName(AlarmType type) const;

+    const char* getActiveAlarmStr() const;

+    // 报警优先级 (越小越紧急)

+    static uint8_t _priority(AlarmType t);

+    uint32_t _buzzerIntervalFor(AlarmType type);

-    uint32_t _buzzerOffTime;       // 定时关闭时刻 (0=手动关)

+    uint32_t _buzzerOffTime;       // 定时关闭时刻 (0=手动/无限)

uint32_t _buzzerToggleTime;    // 间歇切换时刻

bool     _buzzerBeepState;     // 间歇状态 (响/停)

uint8_t  _beepCount;           // 当前周期哔了几声

+    uint8_t  _beepsPerCycle;       // 当前报警类型每周期几声

+    uint32_t _beepOnMs;            // 每声持续 ms

+    uint32_t _beepOffMs;           // 每声间隔 ms

+    uint32_t _cyclePauseMs;        // 周期间停顿 ms

-    // 活动报警列表 (最多 4 个同时)

+    // 活动报警列表 (最多 5 个同时)

+    static const uint8_t MAX_ACTIVE = 5;

+    AlarmType _activeAlarms[MAX_ACTIVE];

+    // 当前详情 (用于上报)

/**
 * @file alarm_manager.h
 * @brief 报警管理器 - 双模式 (本地判定 + 服务端指令)
 *
 * 本地模式: 离线也能用, 跌倒/水浸/心率异常/呼吸异常 本地判定触发
 * 服务端模式: 收到 cmd/{gateid}/alarm, alarm/restore 执行联动
 */

#ifndef ALARM_MANAGER_H
#define ALARM_MANAGER_H

#include <Arduino.h>
#include "config.h"

class AlarmManager {
public:
    // 告警类型 (本地 + 服务端共用)
    enum AlarmType {
        ALARM_NONE = 0,
        ALARM_FALL,             // 跌倒 (本地 + 服务端)
        ALARM_WATER_LEAK,       // 水浸 (本地 + 服务端)
        ALARM_HEART_ABNORMAL,   // 心率异常 (本地)
        ALARM_BREATH_ABNORMAL,  // 呼吸异常 (本地)
        ALARM_SERVER,           // 服务端下发 (通用)
    };

    AlarmManager();
    ~AlarmManager();

    bool begin();
    void loop();

    // ============================================================
    // 本地报警接口 (离线可用)
    // ============================================================

    /** 触发本地报警 */
    void triggerLocal(AlarmType type);
    /** 清除本地报警 */
    void clearLocal(AlarmType type);
    /** 清除所有本地报警 */
    void clearAllLocal();

    // ============================================================
    // 服务端指令接口 (协议规范 §5.1, §5.2)
    // ============================================================

    /** 执行服务端告警指令 */
    void executeAlarm(const AlarmCommand& cmd);
    /** 执行服务端恢复指令 */
    void executeRestore(const AlarmCommand& cmd);
    /** 蜂鸣器测试 */
    void buzzerTest(uint32_t durationSec);

    // ============================================================
    // 状态查询 (上报用)
    // ============================================================

    bool isBuzzerOn() const      { return _buzzerOn; }
    bool hasActiveAlarm() const  { return _activeCount > 0; }
    const char* getActiveAlarmType() const;

private:
    void _setBuzzer(bool on);
    void _setLED(bool on);
    void _updateBuzzer();
    void _updateLED();

    // 蜂鸣器/LED 状态
    bool     _buzzerOn;
    bool     _ledOn;
    uint32_t _buzzerOffTime;       // 定时关闭时刻 (0=手动关)
    uint32_t _buzzerToggleTime;    // 间歇切换时刻
    bool     _buzzerBeepState;     // 间歇状态 (响/停)
    uint8_t  _beepCount;           // 当前周期哔了几声

    // 活动报警列表 (最多 4 个同时)
    AlarmType _activeAlarms[4];
    uint8_t   _activeCount;

    // LED 闪烁
    uint32_t _lastLEDToggle;
};

#endif // ALARM_MANAGER_H


Now rewrite alarm_manager.cpp with the expanded logic.


现在重写报警管理器实现——优先级蜂鸣 + 新类型：


/**
 * @file alarm_manager.cpp
 * @brief 报警管理器 - 多类型分优先级实现
 *
 * 蜂鸣模式 (按最高优先级报警):
 *   跌倒:     连速哔 (80ms响/80ms停) — 最急
 *   水浸:     5连哔停顿 (100ms/100ms) — 次急
 *   心率/呼吸: 3连哔停顿 (150ms/100ms) — 中等
 *   温湿度:    2连哔长停 (200ms/400ms) — 温和
 *   传感器故障: 单声长哔 (500ms/2s) — 仅提示
 */

#include "alarm_manager.h"

// ============================================================
// 优先级定义 (数值越小越紧急)
// ============================================================

uint8_t AlarmManager::_priority(AlarmType t) {
    switch (t) {
    case ALARM_FALL:             return 0;
    case ALARM_WATER_LEAK:       return 1;
    case ALARM_HEART_ABNORMAL:   return 2;
    case ALARM_BREATH_ABNORMAL:  return 2;
    case ALARM_TEMP_ABNORMAL:    return 3;
    case ALARM_HUMI_ABNORMAL:    return 3;
    case ALARM_SENSOR_FAULT:     return 4;
    case ALARM_SERVER:           return 5;
    default:                     return 9;
    }
}

// ============================================================
// 构造 & 析构
// ============================================================

AlarmManager::AlarmManager()
    : _buzzerOn(false)
    , _ledOn(false)
    , _buzzerOffTime(0)
    , _buzzerToggleTime(0)
    , _buzzerBeepState(false)
    , _beepCount(0)
    , _beepsPerCycle(3)
    , _beepOnMs(150)
    , _beepOffMs(100)
    , _cyclePauseMs(600)
    , _activeCount(0)
    , _lastLEDToggle(0)
    , _alarmCb(nullptr)
{
    for (uint8_t i = 0; i < MAX_ACTIVE; i++) _activeAlarms[i] = ALARM_NONE;
    memset(_activeDetail, 0, sizeof(_activeDetail));
}

AlarmManager::~AlarmManager() {}

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

bool AlarmManager::begin() {
    ledcSetup(BUZZER_PWM_CHANNEL, BUZZER_PWM_FREQ, BUZZER_PWM_RESOLUTION);
    ledcAttachPin(BUZZER_PIN, BUZZER_PWM_CHANNEL);
    ledcWrite(BUZZER_PWM_CHANNEL, 0);

    pinMode(LED_ALARM_PIN, OUTPUT);
    digitalWrite(LED_ALARM_PIN, LOW);

    Serial.println(F("[告警] ✅ 多类型分优先级报警就绪 (7种类型)"));
    return true;
}

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

void AlarmManager::loop() {
    _updateBuzzer();
    _updateLED();
}

// ============================================================
// 报警名称映射
// ============================================================

const char* AlarmManager::getAlarmName(AlarmType type) const {
    switch (type) {
    case ALARM_FALL:             return "跌倒";
    case ALARM_WATER_LEAK:       return "水浸";
    case ALARM_HEART_ABNORMAL:   return "心率异常";
    case ALARM_BREATH_ABNORMAL:  return "呼吸异常";
    case ALARM_TEMP_ABNORMAL:    return "温度异常";
    case ALARM_HUMI_ABNORMAL:    return "湿度异常";
    case ALARM_SENSOR_FAULT:     return "传感器故障";
    case ALARM_SERVER:           return "云端指令";
    default:                     return "未知";
    }
}

// ============================================================
// 最高优先级报警
// ============================================================

AlarmManager::AlarmType AlarmManager::getHighestAlarm() const {
    if (_activeCount == 0) return ALARM_NONE;
    AlarmType best = ALARM_NONE;
    uint8_t   bestP = 99;
    for (uint8_t i = 0; i < _activeCount; i++) {
        uint8_t p = _priority(_activeAlarms[i]);
        if (p < bestP) { bestP = p; best = _activeAlarms[i]; }
    }
    return best;
}

const char* AlarmManager::getActiveAlarmStr() const {
    AlarmType t = getHighestAlarm();
    if (t == ALARM_NONE) return "";
    return getAlarmName(t);
}

// ============================================================
// 蜂鸣参数表 (按最高优先级报警驱动)
// ============================================================

uint32_t AlarmManager::_buzzerIntervalFor(AlarmType type) {
    switch (type) {
    case ALARM_FALL:             return 200;   // LED 快闪
    case ALARM_WATER_LEAK:       return 300;
    case ALARM_HEART_ABNORMAL:   return 500;
    case ALARM_BREATH_ABNORMAL:  return 500;
    case ALARM_TEMP_ABNORMAL:    return 800;
    case ALARM_HUMI_ABNORMAL:    return 800;
    case ALARM_SENSOR_FAULT:     return 1000;
    default:                     return 500;
    }
}

// ============================================================
// 更新蜂鸣参数 (根据当前最高优先级报警)
// ============================================================

static void _calcBuzzerParams(AlarmManager::AlarmType t,
                              uint8_t& beeps, uint32_t& onMs,
                              uint32_t& offMs, uint32_t& pauseMs) {
    switch (t) {
    case AlarmManager::ALARM_FALL:
        beeps = 0; onMs = 80; offMs = 80; pauseMs = 0;   // 连续急哔
        break;
    case AlarmManager::ALARM_WATER_LEAK:
        beeps = 5; onMs = 100; offMs = 100; pauseMs = 500; // 5连哔
        break;
    case AlarmManager::ALARM_HEART_ABNORMAL:
    case AlarmManager::ALARM_BREATH_ABNORMAL:
        beeps = 3; onMs = 150; offMs = 100; pauseMs = 600; // 3连哔
        break;
    case AlarmManager::ALARM_TEMP_ABNORMAL:
    case AlarmManager::ALARM_HUMI_ABNORMAL:
        beeps = 2; onMs = 200; offMs = 400; pauseMs = 1200; // 2声间隔
        break;
    case AlarmManager::ALARM_SENSOR_FAULT:
        beeps = 1; onMs = 500; offMs = 2000; pauseMs = 0;  // 单声长哔
        break;
    default:
        beeps = 0; onMs = 500; offMs = 500; pauseMs = 0;   // 持续
        break;
    }
}

// ============================================================
// 本地报警触发
// ============================================================

void AlarmManager::triggerLocal(AlarmType type, const char* detail) {
    // 去重
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == type) return;
    }

    if (_activeCount < MAX_ACTIVE) {
        _activeAlarms[_activeCount++] = type;
    }

    const char* name = getAlarmName(type);
    Serial.printf("[告警] 🚨 触发: %s (活动:%d)", name, _activeCount);
    if (detail && detail[0]) Serial.printf(" | %s", detail);
    Serial.println();

    // 更新蜂鸣参数 (按最高优先级)
    AlarmType highest = getHighestAlarm();
    _calcBuzzerParams(highest, _beepsPerCycle, _beepOnMs, _beepOffMs, _cyclePauseMs);

    // 保存详情
    if (detail) {
        strncpy(_activeDetail, detail, sizeof(_activeDetail) - 1);
    }

    _setBuzzer(true);
    _setLED(true);

    // 通知外部
    if (_alarmCb) _alarmCb((uint8_t)type, true, detail ? detail : name);
}

void AlarmManager::clearLocal(AlarmType type) {
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == type) {
            for (uint8_t j = i; j < _activeCount - 1; j++) {
                _activeAlarms[j] = _activeAlarms[j + 1];
            }
            _activeAlarms[_activeCount - 1] = ALARM_NONE;
            _activeCount--;
            break;
        }
    }

    const char* name = getAlarmName(type);
    Serial.printf("[告警] ✅ 清除: %s (剩余:%d)\n", name, _activeCount);

    if (_activeCount == 0) {
        _setBuzzer(false);
        _setLED(false);
        _activeDetail[0] = '\0';
    } else {
        // 降级到次高优先级报警的蜂鸣参数
        AlarmType highest = getHighestAlarm();
        _calcBuzzerParams(highest, _beepsPerCycle, _beepOnMs, _beepOffMs, _cyclePauseMs);
    }

    if (_alarmCb) _alarmCb((uint8_t)type, false, name);
}

void AlarmManager::clearAllLocal() {
    _activeCount = 0;
    for (uint8_t i = 0; i < MAX_ACTIVE; i++) _activeAlarms[i] = ALARM_NONE;
    _activeDetail[0] = '\0';
    _setBuzzer(false);
    _setLED(false);
    Serial.println(F("[告警] 清除所有报警"));
}

// ============================================================
// 服务端告警指令 (§5.1)
// ============================================================

void AlarmManager::executeAlarm(const AlarmCommand& cmd) {
    Serial.printf("[告警] ☁ 服务端: %s (等级=%s 蜂鸣=%d)\n",
                  cmd.alarmType, cmd.alarmLevel, cmd.buzzerOn);

    AlarmType localType = ALARM_SERVER;
    if (strcmp(cmd.alarmType, "fall") == 0)           localType = ALARM_FALL;
    else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK;

    if (cmd.buzzerOn) {
        triggerLocal(localType, cmd.alarmDesc);
        if (cmd.buzzerDuration > 0) {
            _buzzerOffTime = millis() + (uint32_t)cmd.buzzerDuration * 1000;
        } else {
            _buzzerOffTime = 0;
        }
    }

    if (cmd.nightLight || cmd.buzzerOn) {
        _setLED(true);
    }
}

void AlarmManager::executeRestore(const AlarmCommand& cmd) {
    Serial.printf("[告警] ☁ 恢复: %s\n", cmd.alarmType);

    AlarmType localType = ALARM_SERVER;
    if (strcmp(cmd.alarmType, "fall") == 0)           localType = ALARM_FALL;
    else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK;

    clearLocal(localType);

    if (!cmd.buzzerOn) _setBuzzer(false);
    if (!cmd.nightLight) _setLED(false);
}

// ============================================================
// 蜂鸣器测试
// ============================================================

void AlarmManager::buzzerTest(uint32_t durationSec) {
    Serial.printf("[告警] 🔔 蜂鸣器测试 %ds\n", durationSec);
    _setBuzzer(true);
    _buzzerOffTime = millis() + durationSec * 1000;
}

// ============================================================
// 内部控制
// ============================================================

void AlarmManager::_setBuzzer(bool on) {
    _buzzerOn = on;
    _buzzerBeepState = false;
    _buzzerToggleTime = 0;
    _beepCount = 0;
    if (on) {
        _buzzerOffTime = 0;
    } else {
        ledcWrite(BUZZER_PWM_CHANNEL, 0);
        _buzzerOffTime = 0;
    }
}

void AlarmManager::_setLED(bool on) {
    _ledOn = on;
    if (!on) digitalWrite(LED_ALARM_PIN, LOW);
}

// ============================================================
// 蜂鸣器更新 (优先级驱动模式)
// ============================================================

void AlarmManager::_updateBuzzer() {
    if (!_buzzerOn) return;

    // 定时关闭
    if (_buzzerOffTime > 0 && millis() >= _buzzerOffTime) {
        _buzzerOn = false;
        ledcWrite(BUZZER_PWM_CHANNEL, 0);
        Serial.println(F("[告警] ⏰ 蜂鸣器定时关闭"));
        return;
    }

    uint32_t now = millis();

    // 云端定时: 持续响
    if (_buzzerOffTime > 0) {
        ledcWrite(BUZZER_PWM_CHANNEL, 128);
        return;
    }

    // 本地报警: 按优先级模式 — 哔声/停顿交替
    if (_beepsPerCycle == 0) {
        // 连速哔 (跌倒): 简单翻转
        if (now - _buzzerToggleTime >= (_buzzerBeepState ? _beepOnMs : _beepOffMs)) {
            _buzzerToggleTime = now;
            _buzzerBeepState = !_buzzerBeepState;
            ledcWrite(BUZZER_PWM_CHANNEL, _buzzerBeepState ? 128 : 0);
        }
    } else {
        // 多声一组: N声哔 + 长停顿
        if (!_buzzerBeepState) {
            // 停顿中
            uint32_t pause = (_beepCount >= _beepsPerCycle) ? _cyclePauseMs : _beepOffMs;
            if (now - _buzzerToggleTime >= pause) {
                _buzzerToggleTime = now;
                if (_beepCount >= _beepsPerCycle) _beepCount = 0;
                _buzzerBeepState = true;
                ledcWrite(BUZZER_PWM_CHANNEL, 128);
                _beepCount++;
            }
        } else {
            // 响声中
            if (now - _buzzerToggleTime >= _beepOnMs) {
                _buzzerToggleTime = now;
                _buzzerBeepState = false;
                ledcWrite(BUZZER_PWM_CHANNEL, 0);
            }
        }
    }
}

// ============================================================
// LED 更新 (按最高优先级决定闪烁频率)
// ============================================================

void AlarmManager::_updateLED() {
    if (!_ledOn) {
        digitalWrite(LED_ALARM_PIN, LOW);
        return;
    }

    AlarmType highest = getHighestAlarm();
    uint32_t interval = _buzzerIntervalFor(highest);

    if (millis() - _lastLEDToggle >= interval) {
        _lastLEDToggle = millis();
        digitalWrite(LED_ALARM_PIN, !digitalRead(LED_ALARM_PIN));
    }
}


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

/**
 * @file alarm_manager.cpp
 * @brief 报警管理器 - 多类型分优先级实现
 *
 * 蜂鸣模式 (按最高优先级报警):
 *   跌倒:     连速哔 (80ms响/80ms停) — 最急
 *   水浸:     5连哔停顿 (100ms/100ms) — 次急
 *   心率/呼吸: 3连哔停顿 (150ms/100ms) — 中等
 *   温湿度:    2连哔长停 (200ms/400ms) — 温和
 *   传感器故障: 单声长哔 (500ms/2s) — 仅提示
 */

#include "alarm_manager.h"

// ============================================================
// 优先级定义 (数值越小越紧急)
// ============================================================

uint8_t AlarmManager::_priority(AlarmType t) {
    switch (t) {
    case ALARM_FALL:             return 0;
    case ALARM_WATER_LEAK:       return 1;
    case ALARM_HEART_ABNORMAL:   return 2;
    case ALARM_BREATH_ABNORMAL:  return 2;
    case ALARM_TEMP_ABNORMAL:    return 3;
    case ALARM_HUMI_ABNORMAL:    return 3;
    case ALARM_SENSOR_FAULT:     return 4;
    case ALARM_SERVER:           return 5;
    default:                     return 9;
    }
}

// ============================================================
// 构造 & 析构
// ============================================================

AlarmManager::AlarmManager()
    : _buzzerOn(false)
    , _ledOn(false)
    , _buzzerOffTime(0)
    , _buzzerToggleTime(0)
    , _buzzerBeepState(false)
    , _beepCount(0)
    , _beepsPerCycle(3)
    , _beepOnMs(150)
    , _beepOffMs(100)
    , _cyclePauseMs(600)
    , _activeCount(0)
    , _lastLEDToggle(0)
    , _alarmCb(nullptr)
{
    for (uint8_t i = 0; i < MAX_ACTIVE; i++) _activeAlarms[i] = ALARM_NONE;
    memset(_activeDetail, 0, sizeof(_activeDetail));
}

AlarmManager::~AlarmManager() {}

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

bool AlarmManager::begin() {
    ledcSetup(BUZZER_PWM_CHANNEL, BUZZER_PWM_FREQ, BUZZER_PWM_RESOLUTION);
    ledcAttachPin(BUZZER_PIN, BUZZER_PWM_CHANNEL);
    ledcWrite(BUZZER_PWM_CHANNEL, 0);

    pinMode(LED_ALARM_PIN, OUTPUT);
    digitalWrite(LED_ALARM_PIN, LOW);

    Serial.println(F("[告警] ✅ 多类型分优先级报警就绪 (7种类型)"));
    return true;
}

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

void AlarmManager::loop() {
    _updateBuzzer();
    _updateLED();
}

// ============================================================
// 报警名称映射
// ============================================================

const char* AlarmManager::getAlarmName(AlarmType type) const {
    switch (type) {
    case ALARM_FALL:             return "跌倒";
    case ALARM_WATER_LEAK:       return "水浸";
    case ALARM_HEART_ABNORMAL:   return "心率异常";
    case ALARM_BREATH_ABNORMAL:  return "呼吸异常";
    case ALARM_TEMP_ABNORMAL:    return "温度异常";
    case ALARM_HUMI_ABNORMAL:    return "湿度异常";
    case ALARM_SENSOR_FAULT:     return "传感器故障";
    case ALARM_SERVER:           return "云端指令";
    default:                     return "未知";
    }
}

// ============================================================
// 最高优先级报警
// ============================================================

AlarmManager::AlarmType AlarmManager::getHighestAlarm() const {
    if (_activeCount == 0) return ALARM_NONE;
    AlarmType best = ALARM_NONE;
    uint8_t   bestP = 99;
    for (uint8_t i = 0; i < _activeCount; i++) {
        uint8_t p = _priority(_activeAlarms[i]);
        if (p < bestP) { bestP = p; best = _activeAlarms[i]; }
    }
    return best;
}

const char* AlarmManager::getActiveAlarmStr() const {
    AlarmType t = getHighestAlarm();
    if (t == ALARM_NONE) return "";
    return getAlarmName(t);
}

// ============================================================
// 蜂鸣参数表 (按最高优先级报警驱动)
// ============================================================

uint32_t AlarmManager::_buzzerIntervalFor(AlarmType type) {
    switch (type) {
    case ALARM_FALL:             return 200;   // LED 快闪
    case ALARM_WATER_LEAK:       return 300;
    case ALARM_HEART_ABNORMAL:   return 500;
    case ALARM_BREATH_ABNORMAL:  return 500;
    case ALARM_TEMP_ABNORMAL:    return 800;
    case ALARM_HUMI_ABNORMAL:    return 800;
    case ALARM_SENSOR_FAULT:     return 1000;
    default:                     return 500;
    }
}

// ============================================================
// 更新蜂鸣参数 (根据当前最高优先级报警)
// ============================================================

static void _calcBuzzerParams(AlarmManager::AlarmType t,
                              uint8_t& beeps, uint32_t& onMs,
                              uint32_t& offMs, uint32_t& pauseMs) {
    switch (t) {
    case AlarmManager::ALARM_FALL:
        beeps = 0; onMs = 80; offMs = 80; pauseMs = 0;   // 连续急哔
        break;
    case AlarmManager::ALARM_WATER_LEAK:
        beeps = 5; onMs = 100; offMs = 100; pauseMs = 500; // 5连哔
        break;
    case AlarmManager::ALARM_HEART_ABNORMAL:
    case AlarmManager::ALARM_BREATH_ABNORMAL:
        beeps = 3; onMs = 150; offMs = 100; pauseMs = 600; // 3连哔
        break;
    case AlarmManager::ALARM_TEMP_ABNORMAL:
    case AlarmManager::ALARM_HUMI_ABNORMAL:
        beeps = 2; onMs = 200; offMs = 400; pauseMs = 1200; // 2声间隔
        break;
    case AlarmManager::ALARM_SENSOR_FAULT:
        beeps = 1; onMs = 500; offMs = 2000; pauseMs = 0;  // 单声长哔
        break;
    default:
        beeps = 0; onMs = 500; offMs = 500; pauseMs = 0;   // 持续
        break;
    }
}

// ============================================================
// 本地报警触发
// ============================================================

void AlarmManager::triggerLocal(AlarmType type, const char* detail) {
    // 去重
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == type) return;
    }

    if (_activeCount < MAX_ACTIVE) {
        _activeAlarms[_activeCount++] = type;
    }

    const char* name = getAlarmName(type);
    Serial.printf("[告警] 🚨 触发: %s (活动:%d)", name, _activeCount);
    if (detail && detail[0]) Serial.printf(" | %s", detail);
    Serial.println();

    // 更新蜂鸣参数 (按最高优先级)
    AlarmType highest = getHighestAlarm();
    _calcBuzzerParams(highest, _beepsPerCycle, _beepOnMs, _beepOffMs, _cyclePauseMs);

    // 保存详情
    if (detail) {
        strncpy(_activeDetail, detail, sizeof(_activeDetail) - 1);
    }

    _setBuzzer(true);
    _setLED(true);

    // 通知外部
    if (_alarmCb) _alarmCb((uint8_t)type, true, detail ? detail : name);
}

void AlarmManager::clearLocal(AlarmType type) {
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == type) {
            for (uint8_t j = i; j < _activeCount - 1; j++) {
                _activeAlarms[j] = _activeAlarms[j + 1];
            }
            _activeAlarms[_activeCount - 1] = ALARM_NONE;
            _activeCount--;
            break;
        }
    }

    const char* name = getAlarmName(type);
    Serial.printf("[告警] ✅ 清除: %s (剩余:%d)\n", name, _activeCount);

    if (_activeCount == 0) {
        _setBuzzer(false);
        _setLED(false);
        _activeDetail[0] = '\0';
    } else {
        // 降级到次高优先级报警的蜂鸣参数
        AlarmType highest = getHighestAlarm();
        _calcBuzzerParams(highest, _beepsPerCycle, _beepOnMs, _beepOffMs, _cyclePauseMs);
    }

    if (_alarmCb) _alarmCb((uint8_t)type, false, name);
}

void AlarmManager::clearAllLocal() {
    _activeCount = 0;
    for (uint8_t i = 0; i < MAX_ACTIVE; i++) _activeAlarms[i] = ALARM_NONE;
    _activeDetail[0] = '\0';
    _setBuzzer(false);
    _setLED(false);
    Serial.println(F("[告警] 清除所有报警"));
}

// ============================================================
// 服务端告警指令 (§5.1)
// ============================================================

void AlarmManager::executeAlarm(const AlarmCommand& cmd) {
    Serial.printf("[告警] ☁ 服务端: %s (等级=%s 蜂鸣=%d)\n",
                  cmd.alarmType, cmd.alarmLevel, cmd.buzzerOn);

    AlarmType localType = ALARM_SERVER;
    if (strcmp(cmd.alarmType, "fall") == 0)           localType = ALARM_FALL;
    else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK;

    if (cmd.buzzerOn) {
        triggerLocal(localType, cmd.alarmDesc);
        if (cmd.buzzerDuration > 0) {
            _buzzerOffTime = millis() + (uint32_t)cmd.buzzerDuration * 1000;
        } else {
            _buzzerOffTime = 0;
        }
    }

    if (cmd.nightLight || cmd.buzzerOn) {
        _setLED(true);
    }
}

void AlarmManager::executeRestore(const AlarmCommand& cmd) {
    Serial.printf("[告警] ☁ 恢复: %s\n", cmd.alarmType);

    AlarmType localType = ALARM_SERVER;
    if (strcmp(cmd.alarmType, "fall") == 0)           localType = ALARM_FALL;
    else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK;

    clearLocal(localType);

    if (!cmd.buzzerOn) _setBuzzer(false);
    if (!cmd.nightLight) _setLED(false);
}

// ============================================================
// 蜂鸣器测试
// ============================================================

void AlarmManager::buzzerTest(uint32_t durationSec) {
    Serial.printf("[告警] 🔔 蜂鸣器测试 %ds\n", durationSec);
    _setBuzzer(true);
    _buzzerOffTime = millis() + durationSec * 1000;
}

// ============================================================
// 内部控制
// ============================================================

void AlarmManager::_setBuzzer(bool on) {
    _buzzerOn = on;
    _buzzerBeepState = false;
    _buzzerToggleTime = 0;
    _beepCount = 0;
    if (on) {
        _buzzerOffTime = 0;
    } else {
        ledcWrite(BUZZER_PWM_CHANNEL, 0);
        _buzzerOffTime = 0;
    }
}

void AlarmManager::_setLED(bool on) {
    _ledOn = on;
    if (!on) digitalWrite(LED_ALARM_PIN, LOW);
}

// ============================================================
// 蜂鸣器更新 (优先级驱动模式)
// ============================================================

void AlarmManager::_updateBuzzer() {
    if (!_buzzerOn) return;

    // 定时关闭
    if (_buzzerOffTime > 0 && millis() >= _buzzerOffTime) {
        _buzzerOn = false;
        ledcWrite(BUZZER_PWM_CHANNEL, 0);
        Serial.println(F("[告警] ⏰ 蜂鸣器定时关闭"));
        return;
    }

    uint32_t now = millis();

    // 云端定时: 持续响
    if (_buzzerOffTime > 0) {
        ledcWrite(BUZZER_PWM_CHANNEL, 128);
        return;
    }

    // 本地报警: 按优先级模式 — 哔声/停顿交替
    if (_beepsPerCycle == 0) {
        // 连速哔 (跌倒): 简单翻转
        if (now - _buzzerToggleTime >= (_buzzerBeepState ? _beepOnMs : _beepOffMs)) {
            _buzzerToggleTime = now;
            _buzzerBeepState = !_buzzerBeepState;
            ledcWrite(BUZZER_PWM_CHANNEL, _buzzerBeepState ? 128 : 0);
        }
    } else {
        // 多声一组: N声哔 + 长停顿
        if (!_buzzerBeepState) {
            // 停顿中
            uint32_t pause = (_beepCount >= _beepsPerCycle) ? _cyclePauseMs : _beepOffMs;
            if (now - _buzzerToggleTime >= pause) {
                _buzzerToggleTime = now;
                if (_beepCount >= _beepsPerCycle) _beepCount = 0;
                _buzzerBeepState = true;
                ledcWrite(BUZZER_PWM_CHANNEL, 128);
                _beepCount++;
            }
        } else {
            // 响声中
            if (now - _buzzerToggleTime >= _beepOnMs) {
                _buzzerToggleTime = now;
                _buzzerBeepState = false;
                ledcWrite(BUZZER_PWM_CHANNEL, 0);
            }
        }
    }
}

// ============================================================
// LED 更新 (按最高优先级决定闪烁频率)
// ============================================================

void AlarmManager::_updateLED() {
    if (!_ledOn) {
        digitalWrite(LED_ALARM_PIN, LOW);
        return;
    }

    AlarmType highest = getHighestAlarm();
    uint32_t interval = _buzzerIntervalFor(highest);

    if (millis() - _lastLEDToggle >= interval) {
        _lastLEDToggle = millis();
        digitalWrite(LED_ALARM_PIN, !digitalRead(LED_ALARM_PIN));
    }
}

- * @brief 报警管理器 - 双模式实现

+ * @brief 报警管理器 - 多类型分优先级实现

- * 本地: 跌倒/水浸/心率异常/呼吸异常 → 立即蜂鸣器+LED

- * 服务端: cmd/alarm, cmd/alarm/restore → 执行联动 + 回复响应

+ * 蜂鸣模式 (按最高优先级报警):

+ *   传感器故障: 单声长哔 (500ms/2s) — 仅提示

+// 优先级定义 (数值越小越紧急)

+uint8_t AlarmManager::_priority(AlarmType t) {

+    case ALARM_FALL:             return 0;

+    case ALARM_WATER_LEAK:       return 1;

+    case ALARM_HEART_ABNORMAL:   return 2;

+    case ALARM_BREATH_ABNORMAL:  return 2;

+    case ALARM_TEMP_ABNORMAL:    return 3;

+    case ALARM_HUMI_ABNORMAL:    return 3;

+    case ALARM_SENSOR_FAULT:     return 4;

+    case ALARM_SERVER:           return 5;

+    default:                     return 9;

-    for (int i = 0; i < 4; i++) _activeAlarms[i] = ALARM_NONE;

+    for (uint8_t i = 0; i < MAX_ACTIVE; i++) _activeAlarms[i] = ALARM_NONE;

+    memset(_activeDetail, 0, sizeof(_activeDetail));

-    ledcWrite(BUZZER_PWM_CHANNEL, 0);  // 关

-    Serial.println(F("[告警] ✅ 双模式报警 (本地+服务端)"));

+    Serial.println(F("[告警] ✅ 多类型分优先级报警就绪 (7种类型)"));

-// 本地报警触发 (离线可用)

-void AlarmManager::triggerLocal(AlarmType type) {

+const char* AlarmManager::getAlarmName(AlarmType type) const {

+    case ALARM_FALL:             return "跌倒";

+    case ALARM_WATER_LEAK:       return "水浸";

+    case ALARM_HEART_ABNORMAL:   return "心率异常";

+    case ALARM_BREATH_ABNORMAL:  return "呼吸异常";

+    case ALARM_TEMP_ABNORMAL:    return "温度异常";

+    case ALARM_HUMI_ABNORMAL:    return "湿度异常";

+    case ALARM_SENSOR_FAULT:     return "传感器故障";

+    case ALARM_SERVER:           return "云端指令";

+    default:                     return "未知";

+AlarmManager::AlarmType AlarmManager::getHighestAlarm() const {

+    if (_activeCount == 0) return ALARM_NONE;

+    for (uint8_t i = 0; i < _activeCount; i++) {

+        uint8_t p = _priority(_activeAlarms[i]);

+        if (p < bestP) { bestP = p; best = _activeAlarms[i]; }

+const char* AlarmManager::getActiveAlarmStr() const {

+// 蜂鸣参数表 (按最高优先级报警驱动)

+uint32_t AlarmManager::_buzzerIntervalFor(AlarmType type) {

+    case ALARM_FALL:             return 200;   // LED 快闪

+    case ALARM_WATER_LEAK:       return 300;

+    case ALARM_HEART_ABNORMAL:   return 500;

+    case ALARM_BREATH_ABNORMAL:  return 500;

+    case ALARM_TEMP_ABNORMAL:    return 800;

+    case ALARM_HUMI_ABNORMAL:    return 800;

+    case ALARM_SENSOR_FAULT:     return 1000;

+    default:                     return 500;

+// 更新蜂鸣参数 (根据当前最高优先级报警)

+static void _calcBuzzerParams(AlarmManager::AlarmType t,

+                              uint8_t& beeps, uint32_t& onMs,

+                              uint32_t& offMs, uint32_t& pauseMs) {

+        beeps = 0; onMs = 80; offMs = 80; pauseMs = 0;   // 连续急哔

+    case AlarmManager::ALARM_WATER_LEAK:

+        beeps = 5; onMs = 100; offMs = 100; pauseMs = 500; // 5连哔

+    case AlarmManager::ALARM_HEART_ABNORMAL:

+    case AlarmManager::ALARM_BREATH_ABNORMAL:

+        beeps = 3; onMs = 150; offMs = 100; pauseMs = 600; // 3连哔

+    case AlarmManager::ALARM_TEMP_ABNORMAL:

+    case AlarmManager::ALARM_HUMI_ABNORMAL:

+        beeps = 2; onMs = 200; offMs = 400; pauseMs = 1200; // 2声间隔

+    case AlarmManager::ALARM_SENSOR_FAULT:

+        beeps = 1; onMs = 500; offMs = 2000; pauseMs = 0;  // 单声长哔

+        beeps = 0; onMs = 500; offMs = 500; pauseMs = 0;   // 持续

+void AlarmManager::triggerLocal(AlarmType type, const char* detail) {

for (uint8_t i = 0; i < _activeCount; i++) {

-    case ALARM_FALL:            name = "跌倒";        break;

-    case ALARM_WATER_LEAK:      name = "水浸";        break;

-    case ALARM_HEART_ABNORMAL:  name = "心率异常";    break;

-    case ALARM_BREATH_ABNORMAL: name = "呼吸异常";    break;

+    const char* name = getAlarmName(type);

+    Serial.printf("[告警] 🚨 触发: %s (活动:%d)", name, _activeCount);

+    if (detail && detail[0]) Serial.printf(" | %s", detail);

-    Serial.printf("[告警] 🚨 本地报警触发: %s (活动报警:%d)\n", name, _activeCount);

+    // 更新蜂鸣参数 (按最高优先级)

+    AlarmType highest = getHighestAlarm();

+    _calcBuzzerParams(highest, _beepsPerCycle, _beepOnMs, _beepOffMs, _cyclePauseMs);

-    // 立即开启蜂鸣器和 LED

+    // 保存详情

+        strncpy(_activeDetail, detail, sizeof(_activeDetail) - 1);

+    // 通知外部

+    if (_alarmCb) _alarmCb((uint8_t)type, true, detail ? detail : name);

-    Serial.printf("[告警] 本地报警清除, 剩余活动:%d\n", _activeCount);

+    Serial.printf("[告警] ✅ 清除: %s (剩余:%d)\n", name, _activeCount);

-    // 没有活动报警时关闭

+        // 降级到次高优先级报警的蜂鸣参数

+        AlarmType highest = getHighestAlarm();

+    if (_alarmCb) _alarmCb((uint8_t)type, false, name);

-    Serial.println(F("[告警] 清除所有本地报警"));

-    Serial.printf("[告警] ☁ 服务端告警: %s (等级=%s, 蜂鸣器=%d)\n",

+    Serial.printf("[告警] ☁ 服务端: %s (等级=%s 蜂鸣=%d)\n",

+        triggerLocal(localType, cmd.alarmDesc);

-// 服务端恢复指令 (§5.2)

-    Serial.printf("[告警] ☁ 服务端恢复: %s\n", cmd.alarmType);

+    Serial.printf("[告警] ☁ 恢复: %s\n", cmd.alarmType);

+    if (!cmd.buzzerOn) _setBuzzer(false);

+    if (!cmd.nightLight) _setLED(false);

-    Serial.printf("[告警] 🔔 蜂鸣器测试 %d秒\n", durationSec);

+    Serial.printf("[告警] 🔔 蜂鸣器测试 %ds\n", durationSec);

-const char* AlarmManager::getActiveAlarmType() const {

-    case ALARM_FALL:            return "fall";

-    case ALARM_WATER_LEAK:      return "water_leak";

-    case ALARM_HEART_ABNORMAL:  return "heart_abnormal";

-    case ALARM_BREATH_ABNORMAL: return "breath_abnormal";

-    case ALARM_SERVER:          return "server";

-        _buzzerOffTime = 0;  // 本地报警=无限哔哔

if (!on) digitalWrite(LED_ALARM_PIN, LOW);

+// 蜂鸣器更新 (优先级驱动模式)

-    // 定时关闭检查

-    if (_buzzerOn && _buzzerOffTime > 0 && millis() >= _buzzerOffTime) {

+    // 定时关闭

+    if (_buzzerOffTime > 0 && millis() >= _buzzerOffTime) {

-    // 本地报警: 三连哔 停 — 循环 (哔哔哔...哔哔哔...)

-    if (_buzzerOn && _buzzerOffTime == 0) {

+    // 云端定时: 持续响

+        ledcWrite(BUZZER_PWM_CHANNEL, 128);

+    // 本地报警: 按优先级模式 — 哔声/停顿交替

+        // 连速哔 (跌倒): 简单翻转

+        if (now - _buzzerToggleTime >= (_buzzerBeepState ? _beepOnMs : _beepOffMs)) {

+            _buzzerBeepState = !_buzzerBeepState;

+            ledcWrite(BUZZER_PWM_CHANNEL, _buzzerBeepState ? 128 : 0);

+        // 多声一组: N声哔 + 长停顿

-            // 在停顿中，检查是否该下一声

+            // 停顿中

+            uint32_t pause = (_beepCount >= _beepsPerCycle) ? _cyclePauseMs : _beepOffMs;

-                if (_beepCount >= 3) _beepCount = 0;  // 重置周期

+                if (_beepCount >= _beepsPerCycle) _beepCount = 0;

-            if (now - _buzzerToggleTime >= 150) {

+            // 响声中

+            if (now - _buzzerToggleTime >= _beepOnMs) {

-    // 云端定时: 持续响

-    else if (_buzzerOn && _buzzerOffTime > 0) {

-        ledcWrite(BUZZER_PWM_CHANNEL, 128);

+// LED 更新 (按最高优先级决定闪烁频率)

-    // 根据告警类型调整闪烁频率

-    for (uint8_t i = 0; i < _activeCount; i++) {

-        if (_activeAlarms[i] == ALARM_FALL) {

+    AlarmType highest = getHighestAlarm();

+    uint32_t interval = _buzzerIntervalFor(highest);

if (millis() - _lastLEDToggle >= interval) {

/**
 * @file alarm_manager.cpp
 * @brief 报警管理器 - 双模式实现
 *
 * 本地: 跌倒/水浸/心率异常/呼吸异常 → 立即蜂鸣器+LED
 * 服务端: cmd/alarm, cmd/alarm/restore → 执行联动 + 回复响应
 */

#include "alarm_manager.h"

AlarmManager::AlarmManager()
    : _buzzerOn(false)
    , _ledOn(false)
    , _buzzerOffTime(0)
    , _buzzerToggleTime(0)
    , _buzzerBeepState(false)
    , _beepCount(0)
    , _activeCount(0)
    , _lastLEDToggle(0)
{
    for (int i = 0; i < 4; i++) _activeAlarms[i] = ALARM_NONE;
}

AlarmManager::~AlarmManager() {}

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

bool AlarmManager::begin() {
    ledcSetup(BUZZER_PWM_CHANNEL, BUZZER_PWM_FREQ, BUZZER_PWM_RESOLUTION);
    ledcAttachPin(BUZZER_PIN, BUZZER_PWM_CHANNEL);
    ledcWrite(BUZZER_PWM_CHANNEL, 0);  // 关

    pinMode(LED_ALARM_PIN, OUTPUT);
    digitalWrite(LED_ALARM_PIN, LOW);

    _buzzerToggleTime = 0;
    _buzzerBeepState = false;
    _beepCount = 0;

    Serial.println(F("[告警] ✅ 双模式报警 (本地+服务端)"));
    return true;
}

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

void AlarmManager::loop() {
    _updateBuzzer();
    _updateLED();
}

// ============================================================
// 本地报警触发 (离线可用)
// ============================================================

void AlarmManager::triggerLocal(AlarmType type) {
    // 去重
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == type) return;
    }

    if (_activeCount < 4) {
        _activeAlarms[_activeCount++] = type;
    }

    const char* name = "";
    switch (type) {
    case ALARM_FALL:            name = "跌倒";        break;
    case ALARM_WATER_LEAK:      name = "水浸";        break;
    case ALARM_HEART_ABNORMAL:  name = "心率异常";    break;
    case ALARM_BREATH_ABNORMAL: name = "呼吸异常";    break;
    default: name = "未知"; break;
    }

    Serial.printf("[告警] 🚨 本地报警触发: %s (活动报警:%d)\n", name, _activeCount);

    // 立即开启蜂鸣器和 LED
    _setBuzzer(true);
    _setLED(true);
}

void AlarmManager::clearLocal(AlarmType type) {
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == type) {
            for (uint8_t j = i; j < _activeCount - 1; j++) {
                _activeAlarms[j] = _activeAlarms[j + 1];
            }
            _activeAlarms[_activeCount - 1] = ALARM_NONE;
            _activeCount--;
            break;
        }
    }

    Serial.printf("[告警] 本地报警清除, 剩余活动:%d\n", _activeCount);

    // 没有活动报警时关闭
    if (_activeCount == 0) {
        _setBuzzer(false);
        _setLED(false);
    }
}

void AlarmManager::clearAllLocal() {
    _activeCount = 0;
    for (int i = 0; i < 4; i++) _activeAlarms[i] = ALARM_NONE;
    _setBuzzer(false);
    _setLED(false);
    Serial.println(F("[告警] 清除所有本地报警"));
}

// ============================================================
// 服务端告警指令 (§5.1)
// ============================================================

void AlarmManager::executeAlarm(const AlarmCommand& cmd) {
    Serial.printf("[告警] ☁ 服务端告警: %s (等级=%s, 蜂鸣器=%d)\n",
                  cmd.alarmType, cmd.alarmLevel, cmd.buzzerOn);

    // 映射 alarmType 到本地类型
    AlarmType localType = ALARM_SERVER;
    if (strcmp(cmd.alarmType, "fall") == 0)           localType = ALARM_FALL;
    else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK;

    if (cmd.buzzerOn) {
        triggerLocal(localType);
        if (cmd.buzzerDuration > 0) {
            _buzzerOffTime = millis() + (uint32_t)cmd.buzzerDuration * 1000;
        } else {
            _buzzerOffTime = 0;
        }
    }

    if (cmd.nightLight || cmd.buzzerOn) {
        _setLED(true);
    }
}

// ============================================================
// 服务端恢复指令 (§5.2)
// ============================================================

void AlarmManager::executeRestore(const AlarmCommand& cmd) {
    Serial.printf("[告警] ☁ 服务端恢复: %s\n", cmd.alarmType);

    AlarmType localType = ALARM_SERVER;
    if (strcmp(cmd.alarmType, "fall") == 0)           localType = ALARM_FALL;
    else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK;

    clearLocal(localType);

    if (!cmd.buzzerOn) {
        _setBuzzer(false);
    }
    if (!cmd.nightLight) {
        _setLED(false);
    }
}

// ============================================================
// 蜂鸣器测试
// ============================================================

void AlarmManager::buzzerTest(uint32_t durationSec) {
    Serial.printf("[告警] 🔔 蜂鸣器测试 %d秒\n", durationSec);
    _setBuzzer(true);
    _buzzerOffTime = millis() + durationSec * 1000;
}

// ============================================================
// 状态查询
// ============================================================

const char* AlarmManager::getActiveAlarmType() const {
    if (_activeCount == 0) return "";
    switch (_activeAlarms[0]) {
    case ALARM_FALL:            return "fall";
    case ALARM_WATER_LEAK:      return "water_leak";
    case ALARM_HEART_ABNORMAL:  return "heart_abnormal";
    case ALARM_BREATH_ABNORMAL: return "breath_abnormal";
    case ALARM_SERVER:          return "server";
    default: return "";
    }
}

// ============================================================
// 内部控制
// ============================================================

void AlarmManager::_setBuzzer(bool on) {
    _buzzerOn = on;
    _buzzerBeepState = false;
    _buzzerToggleTime = 0;
    _beepCount = 0;
    if (on) {
        _buzzerOffTime = 0;  // 本地报警=无限哔哔
    } else {
        ledcWrite(BUZZER_PWM_CHANNEL, 0);
        _buzzerOffTime = 0;
    }
}

void AlarmManager::_setLED(bool on) {
    _ledOn = on;
    if (!on) digitalWrite(LED_ALARM_PIN, LOW);
}

void AlarmManager::_updateBuzzer() {
    // 定时关闭检查
    if (_buzzerOn && _buzzerOffTime > 0 && millis() >= _buzzerOffTime) {
        _buzzerOn = false;
        ledcWrite(BUZZER_PWM_CHANNEL, 0);
        Serial.println(F("[告警] ⏰ 蜂鸣器定时关闭"));
        return;
    }
    // 本地报警: 三连哔 停 — 循环 (哔哔哔...哔哔哔...)
    if (_buzzerOn && _buzzerOffTime == 0) {
        uint32_t now = millis();
        // 周期: 3声哔 (每声150ms响+100ms停) + 600ms大停顿
        if (!_buzzerBeepState) {
            // 在停顿中，检查是否该下一声
            uint32_t pause = (_beepCount >= 3) ? 600 : 100;
            if (now - _buzzerToggleTime >= pause) {
                _buzzerToggleTime = now;
                if (_beepCount >= 3) _beepCount = 0;  // 重置周期
                _buzzerBeepState = true;
                ledcWrite(BUZZER_PWM_CHANNEL, 128);
                _beepCount++;
            }
        } else {
            // 在响中，150ms后停下
            if (now - _buzzerToggleTime >= 150) {
                _buzzerToggleTime = now;
                _buzzerBeepState = false;
                ledcWrite(BUZZER_PWM_CHANNEL, 0);
            }
        }
    }
    // 云端定时: 持续响
    else if (_buzzerOn && _buzzerOffTime > 0) {
        ledcWrite(BUZZER_PWM_CHANNEL, 128);
    }
}

void AlarmManager::_updateLED() {
    if (!_ledOn) {
        digitalWrite(LED_ALARM_PIN, LOW);
        return;
    }

    // 根据告警类型调整闪烁频率
    uint32_t interval = 500;
    for (uint8_t i = 0; i < _activeCount; i++) {
        if (_activeAlarms[i] == ALARM_FALL) {
            interval = 200;  // 跌倒快闪
            break;
        }
    }

    if (millis() - _lastLEDToggle >= interval) {
        _lastLEDToggle = millis();
        digitalWrite(LED_ALARM_PIN, !digitalRead(LED_ALARM_PIN));
    }
}


Now I need to expand the alarm checking logic in main.cpp. Let me also add the OLED alarm indicator. Wait, I should first update main.cpp with the expanded checkLocalAlarms function.

The key changes to main.cpp:
1. Add temp/humidity anomaly checking
2. Add radar timeout (sensor fault) checking
3. Add water sensor offline checking
4. Add DHT fault checking
5. Add hysteresis (require consecutive readings before triggering to prevent false alarms)
6. Register MQTT alarm callback
7. Add new flag variables for temp/humi/sensor fault tracking

Let me also add the new tracking variables to main.cpp.


TodoWrite

扩展 AlarmManager：新报警类型 + 优先级蜂鸣 + 回调

扩展 main.cpp：温湿度/传感器故障检测 + MQTT上报 + 防抖

OLED 显示活跃报警提示


扩展 AlarmManager：新报警类型 + 优先级蜂鸣 + 回调

扩展 main.cpp：温湿度/传感器故障检测 + MQTT上报 + 防抖

OLED 显示活跃报警提示

扩展 AlarmManager：新报警类型 + 优先级蜂鸣 + 回调

扩展 main.cpp：温湿度/传感器故障检测 + MQTT上报 + 防抖

OLED 显示活跃报警提示


Now update main.cpp. I need to:

1. Add new tracking variables for temp/humi/sensor fault
2. Expand checkLocalAlarms() with:
   - Temperature threshold checking (hysteresis: need 3 consecutive readings)
   - Humidity threshold checking (hysteresis: need 3 consecutive readings)
   - Radar timeout checking (need 5 seconds of no data)
   - Water sensor validity checking
   - DHT fault after 10 consecutive failures
3. Add alarm callback for MQTT reporting
4. Register the callback in setup()

Let me first look at the full main.cpp again to understand the structure.


现在拓展 `main.cpp` 里的报警检测和 MQTT 上报：


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;
238	        g_alarm.triggerLocal(AlarmManager::ALARM_FALL);
239	    } else if (!radar.fall && g_localFallActive) {
240	        g_localFallActive = false;
241	        g_alarm.clearLocal(AlarmManager::ALARM_FALL);
242	    }
243
244	    // --- 水浸检测 ---
245	    bool waterLeak = !g_sensor.getRainDO() && g_sensor.isRainValid();
246	    if (waterLeak && !g_localWaterActive) {
247	        g_localWaterActive = true;
248	        g_alarm.triggerLocal(AlarmManager::ALARM_WATER_LEAK);
249	    } else if (!waterLeak && g_localWaterActive) {
250	        g_localWaterActive = false;
251	        g_alarm.clearLocal(AlarmManager::ALARM_WATER_LEAK);
252	    }
253
254	    // --- 心率异常 (离线本地判定, 阈值预留后续调整) ---
255	    if (radar.vitalValid && radar.heartRate > 0) {
256	        bool heartAbn = (radar.heartRate < HEART_RATE_MIN || radar.heartRate > HEART_RATE_MAX);
257	        if (heartAbn && !g_localHeartAbnActive) {
258	            g_localHeartAbnActive = true;
259	            g_alarm.triggerLocal(AlarmManager::ALARM_HEART_ABNORMAL);
260	        } else if (!heartAbn && g_localHeartAbnActive) {
261	            g_localHeartAbnActive = false;
262	            g_alarm.clearLocal(AlarmManager::ALARM_HEART_ABNORMAL);
263	        }
264	    }
265
266	    // --- 呼吸异常 ---
267	    if (radar.vitalValid && radar.breathRate > 0) {
268	        bool breathAbn = (radar.breathRate < BREATH_RATE_MIN || radar.breathRate > BREATH_RATE_MAX);
269	        if (breathAbn && !g_localBreathAbnActive) {
270	            g_localBreathAbnActive = true;
271	            g_alarm.triggerLocal(AlarmManager::ALARM_BREATH_ABNORMAL);
272	        } else if (!breathAbn && g_localBreathAbnActive) {
273	            g_localBreathAbnActive = false;
274	            g_alarm.clearLocal(AlarmManager::ALARM_BREATH_ABNORMAL);
275	        }
276	    }
277	}
278
279	// ============================================================
280	// 调试输出
281	// ============================================================
282
283	void printDebug(const TelemetryData& t) {
284	    static uint32_t lastPrint = 0;
285	    if (millis() - lastPrint < 5000) return;
286	    lastPrint = millis();
287
288	    Serial.println(F("─── 遥测数据 ───"));
289	    Serial.printf("  环境: T=%.1f°C H=%.1f%% (温湿度:%d)\n",
290	                  t.temp, t.humi, t.dhtValid);
291	    Serial.printf("  存在:%d 运动:%d 跌倒:%d 驻留:%d (雷达:%d)\n",
292	                  t.presence, t.motion, t.fall, t.resident, t.radarValid);
293	    Serial.printf("  心率:%d 呼吸:%d (体征:%d)\n",
294	                  t.heartRate, t.breathRate, t.vitalValid);
295	    Serial.printf("  水浸:%d (水浸有效:%d) | WiFi:%d 蜂鸣器:%d\n",
296	                  t.rainDO, t.rainValid, t.wifiReady, t.buzzerState);
297	    Serial.printf("  运行:%ds 序号:%d\n", t.uptimeSec, g_sequence);
298	    Serial.println(F("─────────────────"));
299	}
300
301	// ============================================================
302	// 雷达任务 - 核心0 专用
303	// ============================================================
304
305	void radarTask(void* param) {
306	    Serial.println(F("[任务] 📡 雷达任务启动 (核心0)"));
307	    TickType_t lastWake = xTaskGetTickCount();
308	    const TickType_t freq = pdMS_TO_TICKS(10);
309
310	    while (true) {
311	        g_radar.loop();
312	        vTaskDelayUntil(&lastWake, freq);
313	    }
314	}
315
316	// ============================================================
317	// Setup
318	// ============================================================
319
320	void setup() {
321	    Serial.begin(115200);
322	    delay(500);
323
324	    Serial.println(F("\n"));
325	    Serial.println(F("╔══════════════════════════════════════════╗"));
326	    Serial.println(F("║  🏥 五劳无感康养监测系统 v1.0.0       ║"));
327	    Serial.println(F("║  ESP32-S3 + R60ABD1 + DHT11 + 水浸     ║"));
328	    Serial.println(F("║  MQTT 协议 V0.08                        ║"));
329	    Serial.println(F("╚══════════════════════════════════════════╝"));
330
331	    g_bootTime = millis();
332
333	    // 1. 报警模块 (最先, 引脚安全)
334	    g_alarm.begin();
335
336	    // 2. 雷达
337	    g_radar.begin();
338
339	    // 3. OLED 显示
340	    g_display.begin();
341
342	    // 4. 按钮
343	    g_buttons.begin();
344	    g_buttons.onPageSwitch([]() {
345	        g_display.nextPage();
346	    });
347	    g_buttons.onAlarmDismiss([]() {
348	        g_alarm.clearAllLocal();
349	    });
350	    g_buttons.onProvisionLongPress([]() {
351	        g_wifi.startConfigPortal();
352	    });
353
354	    // 5. WiFi (WiFiManager 配网)
355	    g_wifi.begin();
356
357	    // 6. 时间同步
358	    if (g_wifi.isConnected()) {
359	        syncTime();
360	    }
361
362	    // 7. MQTT
363	    DeviceConfig cfg = g_wifi.getConfig();
364	    g_mqtt.setServer(cfg.mqtt_server, atoi(cfg.mqtt_port),
365	                     cfg.mqtt_user, cfg.mqtt_pass);
366	    g_mqtt.begin(cfg.device_id);
367
368	    // 注册 MQTT 回调
369	    g_mqtt.onAlarmCommand(handleAlarmCommand);
370	    g_mqtt.onControlCommand(handleControlCommand);
371	    g_mqtt.onProvisionCommand(handleProvisionCommand);
372
373	    // 连接 MQTT
374	    if (g_wifi.isConnected()) {
375	        g_mqtt.connect();
376	    }
377
378	    // 8. 传感器
379	    g_sensor.begin();
380
381	    // 9. 雷达任务 (核心0)
382	    xTaskCreatePinnedToCore(
383	        radarTask, "RadarTask",
384	        RADAR_TASK_STACK, nullptr,
385	        RADAR_TASK_PRIORITY, nullptr,
386	        RADAR_TASK_CORE
387	    );
388
389	    Serial.println(F("[系统] ✅ 系统就绪"));
390	    Serial.println(F("══════════════════════════════════════════\n"));
391	}
392
393	// ============================================================
394	// 主循环 (核心1)
395	// ============================================================
396
397	void loop() {
398	    uint32_t now = millis();
399
400	    // --- 0. 按钮轮询 ---
401	    g_buttons.loop();
402
403	    // --- 1. WiFi ---
404	    bool wifiOk = g_wifi.loop();
405
406	    // --- 2. MQTT (含自动重连) ---
407	    if (wifiOk) {
408	        g_mqtt.loop();
409
410	        // WiFi 刚连上但 MQTT 还没连
411	        static bool wasDisconnected = true;
412	        if (g_mqtt.isConnected()) {
413	            if (wasDisconnected) {
414	                // 刚恢复连接, 同步时间
415	                syncTime();
416	                wasDisconnected = false;
417	            }
418	        } else {
419	            wasDisconnected = true;
420	        }
421	    }
422
423	    // --- 3. 传感器 (独立，任何传感器故障不影响其他) ---
424	    g_sensor.read();
425
426	    // --- 4. 本地报警检查 (离线也工作) ---
427	    RadarRawData radar = g_radar.getData();
428	    checkLocalAlarms(radar);
429
430	    // --- 传感器故障检测 (每30秒检查一次，避免刷屏) ---
431	    static uint32_t lastFaultCheck = 0;
432	    if (now - lastFaultCheck > 30000) {
433	        lastFaultCheck = now;
434	        if (!g_sensor.isDhtValid() && millis() > 60000) {
435	            Serial.println(F("[系统] ⚠ DHT11 未检测到 (不影响运行)"));
436	        }
437	        if (g_radar.isTimeout()) {
438	            Serial.println(F("[系统] ⚠ 雷达超时无数据 (不影响运行)"));
439	        }
440	    }
441
442	    // --- 5. 报警模块更新 (定时器/闪烁) ---
443	    g_alarm.loop();
444
445	    // --- 6. 遥测上报 (1秒周期) ---
446	    if (now - g_lastReportTime >= REPORT_INTERVAL_MS) {
447	        g_lastReportTime = now;
448
449	        TelemetryData tele = buildTelemetry(radar);
450	        g_mqtt.publishAllPoints(tele, g_sequence);
451	        g_sequence++;
452
453	        // 调试输出 + OLED 显示
454	        printDebug(tele);
455	        g_display.update(tele, wifiOk, g_alarm.isBuzzerOn());
456	    }
457
458	    // --- 7. MQTT 配网状态 (WiFiManager 触发时) ---
459	    // WiFiManager 的 captive portal 启动/关闭时更新状态
460	    // 此逻辑已集成在 wifi_manager 中
461
462	    delay(10);
463	}
464

/**
 * @file main.cpp
 * @brief 五劳无感康养监测系统 - 主程序 (协议 V0.08)
 *
 * ESP32-S3 + R60ABD1 60G 毫米波雷达 + DHT11 + 水浸
 * 本地紧急报警 (离线可用) + 服务端告警联动
 */

#include <Arduino.h>
#include <WiFi.h>
#include "config.h"
#include "radar_protocol.h"
#include "radar_manager.h"
#include "wifi_manager.h"
#include "mqtt_manager.h"
#include "sensor_manager.h"
#include "alarm_manager.h"
#include "display_manager.h"
#include "button_manager.h"

// ============================================================
// 全局对象
// ============================================================

RadarManager   g_radar;
WiFiMgmt       g_wifi;
MQTTManager    g_mqtt;
SensorManager  g_sensor;
AlarmManager   g_alarm;
DisplayManager g_display;
ButtonManager  g_buttons;

// 上报控制
uint32_t g_lastReportTime  = 0;
uint32_t g_sequence        = 1;       // 上报序号
uint32_t g_bootTime        = 0;       // 启动时刻

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

// ============================================================
// 时间同步
// ============================================================

void syncTime() {
    Serial.println(F("[时间] 正在同步时间..."));
    configTime(NTP_GMT_OFFSET_SEC, NTP_DAYLIGHT_OFFSET, NTP_SERVER, "pool.ntp.org");

    struct tm timeinfo;
    int retry = 0;
    while (!getLocalTime(&timeinfo) && retry < 20) {
        delay(500);
        Serial.print(".");
        retry++;
    }

    if (retry < 20) {
        Serial.println();
        Serial.printf("[时间] ✅ 时间同步成功: %04d-%02d-%02d %02d:%02d:%02d\n",
                      timeinfo.tm_year + 1900, timeinfo.tm_mon + 1,
                      timeinfo.tm_mday, timeinfo.tm_hour,
                      timeinfo.tm_min, timeinfo.tm_sec);
    } else {
        Serial.println(F("\n[时间] ⚠ 时间同步失败, 使用默认时间"));
    }
}

// ============================================================
// MQTT 告警指令回调 (协议规范 §5.1, §5.2)
// ============================================================

void handleAlarmCommand(const AlarmCommand& cmd) {
    if (strcmp(cmd.type, "alarm_restore") == 0) {
        // 告警恢复
        g_alarm.executeRestore(cmd);

        // 清除对应本地告警状态
        if (strcmp(cmd.alarmType, "fall") == 0)           g_localFallActive = false;
        else if (strcmp(cmd.alarmType, "water_leak") == 0) g_localWaterActive = false;
        else if (strcmp(cmd.alarmType, "heart_abnormal") == 0)  g_localHeartAbnActive = false;
        else if (strcmp(cmd.alarmType, "breath_abnormal") == 0) g_localBreathAbnActive = false;
    } else {
        // 告警触发
        g_alarm.executeAlarm(cmd);
    }

    // 回复响应 (§4.5)
    g_mqtt.publishCmdResponse(cmd.cmdId, cmd.type, cmd.alarmType,
                              true, "0", "");
}

// ============================================================
// MQTT 控制指令回调 (§5.3)
// ============================================================

void handleControlCommand(const ControlCommand& cmd) {
    Serial.printf("[控制] 收到控制指令: %s\n", cmd.controlType);

    bool success = true;
    const char* errCode = "0";
    const char* errMsg  = "";

    if (strcmp(cmd.controlType, "buzzer_test") == 0) {
        uint32_t dur = cmd.params_duration > 0 ? cmd.params_duration : 3;
        g_alarm.buzzerTest(dur);
    }
    else if (strcmp(cmd.controlType, "device_restart") == 0) {
        Serial.println(F("[控制] 🔄 远程重启..."));
        g_mqtt.publishCmdResponse(cmd.cmdId, "control", "", true, "0", "");
        g_mqtt.publishOffline();
        delay(500);
        ESP.restart();
    }
    else if (strcmp(cmd.controlType, "factory_reset") == 0) {
        Serial.println(F("[控制] 🔄 恢复出厂设置..."));
        g_wifi.resetSettings();
    }
    else if (strcmp(cmd.controlType, "enter_provision") == 0) {
        Serial.println(F("[控制] 📶 进入配网模式"));
        g_wifi.startConfigPortal();
    }
    else {
        success = false;
        errCode = "2001";
        errMsg  = "不支持的指令类型";
    }

    g_mqtt.publishCmdResponse(cmd.cmdId, "control", "",
                              success, errCode, errMsg);
}

// ============================================================
// MQTT 配网指令回调 (§5.4)
// ============================================================

void handleProvisionCommand(const ProvisionCommand& cmd) {
    Serial.printf("[配网] 📶 收到配网指令: SSID=%s\n", cmd.wifi_ssid);

    if (strlen(cmd.wifi_ssid) == 0) {
        g_mqtt.publishProvisionResponse(cmd.cmdId, false,
                                        "1002", "未提供WiFi SSID");
        return;
    }

    // 连接新 WiFi
    g_mqtt.publishProvisionStatus("wifi_connecting", "");

    WiFi.begin(cmd.wifi_ssid, cmd.wifi_password);
    int retry = 0;
    while (WiFi.status() != WL_CONNECTED && retry < 30) {
        delay(500);
        Serial.print(".");
        retry++;
    }

    if (WiFi.status() == WL_CONNECTED) {
        Serial.printf("\n[配网] ✅ WiFi 已连接, IP=%s\n",
                      WiFi.localIP().toString().c_str());

        // 更新 MQTT 服务器配置
        if (strlen(cmd.mqtt_broker) > 0) {
            g_mqtt.setServer(cmd.mqtt_broker, cmd.mqtt_port,
                           GATEID_DEFAULT, GATEID_DEFAULT); // 正式账号 = gateid
        }

        char signal[8];
        snprintf(signal, sizeof(signal), "%d", WiFi.RSSI());
        g_mqtt.publishProvisionResponse(cmd.cmdId, true, "0", "", signal);

        // 重连 MQTT
        g_mqtt.disconnect();
        g_mqtt.connect();
        g_mqtt.publishOnline();
    } else {
        Serial.println(F("\n[配网] ❌ WiFi 连接失败"));
        g_mqtt.publishProvisionStatus("wifi_failed", "");
        g_mqtt.publishProvisionResponse(cmd.cmdId, false,
                                        "1003", "WiFi连接超时");
    }
}

// ============================================================
// 构建遥测数据
// ============================================================

TelemetryData buildTelemetry(const RadarRawData& radar) {
    TelemetryData t;
    memset(&t, 0, sizeof(t));

    // 设备标识
    strncpy(t.saleid,    SALEID_DEFAULT,   sizeof(t.saleid) - 1);
    strncpy(t.gateid,    g_mqtt.getGateId(), sizeof(t.gateid) - 1);
    strncpy(t.meterId,   METER_ID_DEFAULT, sizeof(t.meterId) - 1);
    strncpy(t.meterName, METER_NAME_DEFAULT, sizeof(t.meterName) - 1);

    // DHT11
    t.temp     = g_sensor.getTemp();
    t.humi     = g_sensor.getHumi();
    t.dhtValid = g_sensor.isDhtValid();

    // 雷达
    t.presence   = radar.presence;
    t.motion     = radar.motion;
    t.fall       = radar.fall;
    t.resident   = radar.resident;
    t.heartRate  = radar.heartRate;
    t.breathRate = radar.breathRate;
    t.body       = radar.body;
    t.radarValid = radar.radarValid;
    t.vitalValid = radar.vitalValid;

    // 水浸
    t.rainDO    = g_sensor.getRainDO();
    t.rainValid = g_sensor.isRainValid();

    // 设备状态
    t.wifiReady  = g_wifi.isConnected() && g_mqtt.isConnected();
    t.uptimeSec  = (millis() - g_bootTime) / 1000;
    t.buzzerState = g_alarm.isBuzzerOn();
    t.lastUpdate = millis();

    return t;
}

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

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)"));
    TickType_t lastWake = xTaskGetTickCount();
    const TickType_t freq = pdMS_TO_TICKS(10);

    while (true) {
        g_radar.loop();
        vTaskDelayUntil(&lastWake, freq);
    }
}

// ============================================================
// Setup
// ============================================================

void setup() {
    Serial.begin(115200);
    delay(500);

    Serial.println(F("\n"));
    Serial.println(F("╔══════════════════════════════════════════╗"));
    Serial.println(F("║  🏥 五劳无感康养监测系统 v1.0.0       ║"));
    Serial.println(F("║  ESP32-S3 + R60ABD1 + DHT11 + 水浸     ║"));
    Serial.println(F("║  MQTT 协议 V0.08                        ║"));
    Serial.println(F("╚══════════════════════════════════════════╝"));

    g_bootTime = millis();

    // 1. 报警模块 (最先, 引脚安全)
    g_alarm.begin();

    // 2. 雷达
    g_radar.begin();

    // 3. OLED 显示
    g_display.begin();

    // 4. 按钮
    g_buttons.begin();
    g_buttons.onPageSwitch([]() {
        g_display.nextPage();
    });
    g_buttons.onAlarmDismiss([]() {
        g_alarm.clearAllLocal();
    });
    g_buttons.onProvisionLongPress([]() {
        g_wifi.startConfigPortal();
    });

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

        // 调试输出 + OLED 显示
        printDebug(tele);
        g_display.update(tele, wifiOk, g_alarm.isBuzzerOn());
    }

    // --- 7. MQTT 配网状态 (WiFiManager 触发时) ---
    // WiFiManager 的 captive portal 启动/关闭时更新状态
    // 此逻辑已集成在 wifi_manager 中

    delay(10);
}


Now I need to update main.cpp with:

1. New tracking variables for temp, humi, sensor faults
2. Expand `checkLocalAlarms()` with:
   - Temp/humi threshold checking (with hysteresis counters)
   - Radar timeout → sensor fault alarm
   - DHT continuous fault → sensor fault alarm
   - Water sensor offline → sensor fault alarm
3. Register MQTT alarm callback
4. Update the setup to register the callback

Let me write the changes.

New tracking variables needed:
```cpp
bool g_localFallActive       = false;
bool g_localWaterActive      = false;
bool g_localHeartAbnActive   = false;
bool g_localBreathAbnActive  = false;
bool g_localTempAbnActive    = false;  // NEW
bool g_localHumiAbnActive    = false;  // NEW
bool g_localRadarFaultActive = false;  // NEW
bool g_localDhtFaultActive   = false;  // NEW
bool g_localWaterOfflineActive = false; // NEW

// Hysteresis counters
uint8_t g_tempAbnCnt   = 0;  // NEW
uint8_t g_humiAbnCnt   = 0;  // NEW
uint8_t g_radarFaultCnt = 0; // NEW
```

For the alarm callback:
```cpp
void onLocalAlarm(uint8_t type, bool triggered, const char* desc) {
    // Report local alarm to MQTT when connected
    if (g_mqtt.isConnected()) {
        // Build alarm event and publish
        g_mqtt.publishAlarmEvent(type, triggered, desc);
    }
}
```

Actually, let me check if `publishAlarmEvent` exists in mqtt_manager... I need to look at mqtt_manager.h to see what publish methods are available.

Let me check MQTTManager for available methods.