diff --git a/include/config.h b/include/config.h index f959037..7299bbd 100644 --- a/include/config.h +++ b/include/config.h @@ -19,7 +19,7 @@ // --- 雷达型号选择 --- #define RADAR_TYPE_ABD1 0 // R60ABD1: 心率/呼吸精准, 跌倒一般 #define RADAR_TYPE_AFD1 1 // R60AFD1: 跌倒/姿态精准, 无心率呼吸 -#define RADAR_TYPE RADAR_TYPE_ABD1 // AFD1 (跌倒/姿态精准, 卫生间用) +#define RADAR_TYPE RADAR_TYPE_AFD1 // AFD1 (跌倒/姿态精准, 卫生间用) // --- 毫米波雷达 --- #define RADAR_UART_PORT Serial2 diff --git a/platformio.ini b/platformio.ini index a55295d..c90236f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -42,4 +42,4 @@ lib_deps = ; 上传配置 upload_speed = 921600 -upload_port = COM20 +upload_port = COM19 diff --git a/src/alarm_manager.cpp b/src/alarm_manager.cpp index fbeb265..3d29944 100644 --- a/src/alarm_manager.cpp +++ b/src/alarm_manager.cpp @@ -256,8 +256,13 @@ void AlarmManager::executeAlarm(const AlarmCommand& cmd) { 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 (strcmp(cmd.alarmType, "fall") == 0) localType = ALARM_FALL; + else if (strcmp(cmd.alarmType, "water_leak") == 0) localType = ALARM_WATER_LEAK; + else if (strcmp(cmd.alarmType, "heart_abnormal") == 0) localType = ALARM_HEART_ABNORMAL; + else if (strcmp(cmd.alarmType, "breath_abnormal") == 0) localType = ALARM_BREATH_ABNORMAL; + else if (strcmp(cmd.alarmType, "temp_abnormal") == 0) localType = ALARM_TEMP_ABNORMAL; + else if (strcmp(cmd.alarmType, "humi_abnormal") == 0) localType = ALARM_HUMI_ABNORMAL; + else if (strcmp(cmd.alarmType, "sensor_fault") == 0) localType = ALARM_SENSOR_FAULT; if (cmd.buzzerOn) { triggerLocal(localType, cmd.alarmDesc); diff --git a/src/main.cpp b/src/main.cpp index 948002a..29da683 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,13 @@ bool g_localHumiAbnActive = false; bool g_localRadarFaultActive = false; bool g_localDhtFaultActive = false; +// 手动解除抑制 — dismiss 后不重复报警, 条件恢复正常才重新武装 +bool g_waterDismissed = false; +bool g_heartDismissed = false; +bool g_breathDismissed = false; +bool g_tempDismissed = false; +bool g_humiDismissed = false; + // 报警防抖计数器 (连续N次确认才触发) uint8_t g_tempAbnCnt = 0; uint8_t g_humiAbnCnt = 0; @@ -113,6 +120,13 @@ void handleAlarmCommand(const AlarmCommand& cmd) { g_fallDetectMs = 0; g_fallClearMs = 0; g_fallDismissed = false; + + // 解除抑制标志也复位 (远程恢复后重新武装) + g_waterDismissed = false; + g_heartDismissed = false; + g_breathDismissed = false; + g_tempDismissed = false; + g_humiDismissed = false; } else { // 告警触发 g_alarm.executeAlarm(cmd); @@ -286,40 +300,39 @@ void checkLocalAlarms(const RadarRawData& radar) { bool humanPresent = radar.presence && radar.radarValid; // ─── ★★★★★ 人身安全: 跌倒 (仅浴室/走廊/客厅) ─── - if (enFall && radar.fall && humanPresent && !g_fallDismissed && millis() > 30000) { + bool fallNow = enFall && radar.fall && humanPresent; // 当前是否处于跌倒状态 + + if (fallNow && !g_fallDismissed && millis() > 30000) { + // 跌倒中且未解除 → 累积时长, 满 FALL_DETECT_MS 触发 if (!g_localFallActive) { - // 持续检测中: 必须满 FALL_DETECT_MS 才报警 if (g_fallDetectMs == 0) { g_fallDetectMs = millis(); } else if (millis() - g_fallDetectMs >= FALL_DETECT_MS) { g_localFallActive = true; g_fallDetectMs = 0; - g_fallClearMs = 0; g_alarm.triggerLocal(AlarmManager::ALARM_FALL, "雷达检测到跌倒"); } - } else { - g_fallClearMs = 0; // 还在跌倒, 重置清除计时 } } else { g_fallDetectMs = 0; - // 解除抑制需要 fall 持续消失 FALL_CLEAR_MS (防 AFD1 频繁跳变) - if (g_fallDismissed && g_fallClearMs == 0) { + } + + // 跌倒信号真正消失后: 延迟清除报警 + 重新武装解除抑制 (防 AFD1 频繁跳变) + if (!fallNow) { + if (g_fallClearMs == 0) { g_fallClearMs = millis(); } - if (!g_fallDismissed || millis() - g_fallClearMs >= FALL_CLEAR_MS) { - g_fallDismissed = false; - g_fallClearMs = 0; - } - if (g_localFallActive) { - // 跌倒已消失 → 延迟清除 - if (g_fallClearMs == 0) { - g_fallClearMs = millis(); - } else if (millis() - g_fallClearMs >= FALL_CLEAR_MS) { + if (millis() - g_fallClearMs >= FALL_CLEAR_MS) { + if (g_localFallActive) { g_localFallActive = false; - g_fallClearMs = 0; g_alarm.clearLocal(AlarmManager::ALARM_FALL); } + if (g_fallDismissed) { + g_fallDismissed = false; // 跌倒真正消失才重新武装 + } } + } else { + g_fallClearMs = 0; // 仍在跌倒, 重置消失计时 } // ─── ★★★★★ 人身安全: 心率异常 (仅卧室+ABD1, 防抖3次, 30s) ─── @@ -327,7 +340,7 @@ void checkLocalAlarms(const RadarRawData& radar) { if (enVital && millis() > 30000 && humanPresent && radar.vitalValid && radar.heartRate > 0) { bool heartAbn = (radar.heartRate < HEART_RATE_MIN || radar.heartRate > HEART_RATE_MAX); if (heartAbn) { - if (++g_heartAbnCnt >= ALARM_HYSTERESIS && !g_localHeartAbnActive) { + if (!g_heartDismissed && ++g_heartAbnCnt >= ALARM_HYSTERESIS && !g_localHeartAbnActive) { g_localHeartAbnActive = true; char buf[32]; snprintf(buf, sizeof(buf), "心率异常: %d bpm", radar.heartRate); @@ -335,6 +348,7 @@ void checkLocalAlarms(const RadarRawData& radar) { } } else { g_heartAbnCnt = 0; + g_heartDismissed = false; if (g_localHeartAbnActive) { g_localHeartAbnActive = false; g_alarm.clearLocal(AlarmManager::ALARM_HEART_ABNORMAL); @@ -343,6 +357,7 @@ void checkLocalAlarms(const RadarRawData& radar) { } else if (!humanPresent && g_localHeartAbnActive) { g_localHeartAbnActive = false; g_heartAbnCnt = 0; + g_heartDismissed = false; g_alarm.clearLocal(AlarmManager::ALARM_HEART_ABNORMAL); } #endif // RADAR_TYPE != AFD1 @@ -352,7 +367,7 @@ void checkLocalAlarms(const RadarRawData& radar) { if (enVital && millis() > 30000 && humanPresent && radar.vitalValid && radar.breathRate > 0) { bool breathAbn = (radar.breathRate < BREATH_RATE_MIN || radar.breathRate > BREATH_RATE_MAX); if (breathAbn) { - if (++g_breathAbnCnt >= ALARM_HYSTERESIS && !g_localBreathAbnActive) { + if (!g_breathDismissed && ++g_breathAbnCnt >= ALARM_HYSTERESIS && !g_localBreathAbnActive) { g_localBreathAbnActive = true; char buf[32]; snprintf(buf, sizeof(buf), "呼吸异常: %d 次/分", radar.breathRate); @@ -360,6 +375,7 @@ void checkLocalAlarms(const RadarRawData& radar) { } } else { g_breathAbnCnt = 0; + g_breathDismissed = false; if (g_localBreathAbnActive) { g_localBreathAbnActive = false; g_alarm.clearLocal(AlarmManager::ALARM_BREATH_ABNORMAL); @@ -368,6 +384,7 @@ void checkLocalAlarms(const RadarRawData& radar) { } else if (!humanPresent && g_localBreathAbnActive) { g_localBreathAbnActive = false; g_breathAbnCnt = 0; + g_breathDismissed = false; g_alarm.clearLocal(AlarmManager::ALARM_BREATH_ABNORMAL); } #endif // RADAR_TYPE != AFD1 @@ -375,12 +392,17 @@ void checkLocalAlarms(const RadarRawData& radar) { // ─── ★★★ 环境安全: 水浸 (仅浴室) ─── if (enWater) { 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 (waterLeak) { + if (!g_waterDismissed && !g_localWaterActive) { + g_localWaterActive = true; + g_alarm.triggerLocal(AlarmManager::ALARM_WATER_LEAK, "水浸传感器触发"); + } + } else { + g_waterDismissed = false; + if (g_localWaterActive) { + g_localWaterActive = false; + g_alarm.clearLocal(AlarmManager::ALARM_WATER_LEAK); + } } } // enWater @@ -389,7 +411,7 @@ void checkLocalAlarms(const RadarRawData& radar) { float temp = g_sensor.getTemp(); bool tempAbn = (temp < TEMP_LOW || temp > TEMP_HIGH); if (tempAbn) { - if (++g_tempAbnCnt >= ALARM_HYSTERESIS && !g_localTempAbnActive) { + if (!g_tempDismissed && ++g_tempAbnCnt >= ALARM_HYSTERESIS && !g_localTempAbnActive) { g_localTempAbnActive = true; char buf[32]; snprintf(buf, sizeof(buf), "温度%.1f°C 超出范围", temp); @@ -397,6 +419,7 @@ void checkLocalAlarms(const RadarRawData& radar) { } } else { g_tempAbnCnt = 0; + g_tempDismissed = false; if (g_localTempAbnActive) { g_localTempAbnActive = false; g_alarm.clearLocal(AlarmManager::ALARM_TEMP_ABNORMAL); @@ -407,7 +430,7 @@ void checkLocalAlarms(const RadarRawData& radar) { float humi = g_sensor.getHumi(); bool humiAbn = (humi < HUMI_LOW || humi > HUMI_HIGH); if (humiAbn) { - if (++g_humiAbnCnt >= ALARM_HYSTERESIS && !g_localHumiAbnActive) { + if (!g_humiDismissed && ++g_humiAbnCnt >= ALARM_HYSTERESIS && !g_localHumiAbnActive) { g_localHumiAbnActive = true; char buf[32]; snprintf(buf, sizeof(buf), "湿度%.1f%% 超出范围", humi); @@ -415,6 +438,7 @@ void checkLocalAlarms(const RadarRawData& radar) { } } else { g_humiAbnCnt = 0; + g_humiDismissed = false; if (g_localHumiAbnActive) { g_localHumiAbnActive = false; g_alarm.clearLocal(AlarmManager::ALARM_HUMI_ABNORMAL); @@ -527,9 +551,17 @@ void setup() { g_localDhtFaultActive = false; g_tempAbnCnt = 0; g_humiAbnCnt = 0; + g_heartAbnCnt = 0; + g_breathAbnCnt = 0; g_radarTimeoutCnt = 0; g_fallClearMs = 0; g_fallDismissed = true; // 抑制重复报警, 等跌倒信号消了再恢复 + // 其余类型同样抑制, 条件恢复正常后才重新武装 + g_waterDismissed = true; + g_heartDismissed = true; + g_breathDismissed = true; + g_tempDismissed = true; + g_humiDismissed = true; }); g_buttons.onProvisionLongPress([]() { g_wifi.startConfigPortal();