From b960682d731dfc356b27f706acd63d5a053ccd0c Mon Sep 17 00:00:00 2001 From: yangzhijia <547486824@qq.com> Date: Mon, 17 Aug 2026 13:48:37 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=20MQTT=20?= =?UTF-8?q?=E6=96=AD=E7=BA=BF=E9=87=8D=E8=BF=9E=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=8A=A5=E8=AD=A6=E9=98=88=E5=80=BC=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E8=AF=AF=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/config.h | 4 ++-- platformio.ini | 2 +- src/main.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- src/mqtt_manager.cpp | 10 ++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/include/config.h b/include/config.h index 7299bbd..8c3c340 100644 --- a/include/config.h +++ b/include/config.h @@ -131,8 +131,8 @@ #define HEART_RATE_MIN 50 #define HEART_RATE_MAX 120 -#define BREATH_RATE_MIN 8 -#define BREATH_RATE_MAX 24 +#define BREATH_RATE_MIN 10 +#define BREATH_RATE_MAX 28 #define TEMP_LOW 5.0f #define TEMP_HIGH 45.0f #define HUMI_LOW 15.0f diff --git a/platformio.ini b/platformio.ini index c90236f..a55295d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -42,4 +42,4 @@ lib_deps = ; 上传配置 upload_speed = 921600 -upload_port = COM19 +upload_port = COM20 diff --git a/src/main.cpp b/src/main.cpp index f7a647c..998c24a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,11 +63,15 @@ uint32_t g_breathNormalMs = 0; // 呼吸正常持续计时 (ms) uint32_t g_fallDetectMs = 0; // 跌倒检测计时 (ms) uint32_t g_fallClearMs = 0; // 跌倒清除计时 (ms) bool g_fallDismissed = false; // 已手动解除, 不重复报警 +uint32_t g_vitalLostMs = 0; // 生命体征丢失持续计时 (ms) +bool g_vitalLostActive = false; // 生命体征丢失报警激活 +bool g_vitalLostDismissed = false; // 生命体征丢失解除抑制 #define ALARM_HYSTERESIS 3 // 连续3次确认才触发 #define FALL_DETECT_MS 10000 // 跌倒持续10秒才触发 (房顶安装, 过滤坐下) #define FALL_CLEAR_MS 18000 // 跌倒消失18秒后才清除 (防误消) #define VITAL_DETECT_MS 20000 // 心率/呼吸持续异常20秒才触发 #define VITAL_CLEAR_MS 15000 // 心率/呼吸持续正常15秒才恢复 +#define VITAL_LOST_MS 60000 // 在床但心率呼吸持续为0达60秒 → 疑似骤停 // ============================================================ // 时间同步 @@ -126,6 +130,8 @@ void handleAlarmCommand(const AlarmCommand& cmd) { g_fallDetectMs = 0; g_fallClearMs = 0; g_fallDismissed = false; + g_vitalLostMs = 0; + g_vitalLostActive = false; // 解除抑制标志也复位 (远程恢复后重新武装) g_waterDismissed = false; @@ -133,6 +139,7 @@ void handleAlarmCommand(const AlarmCommand& cmd) { g_breathDismissed = false; g_tempDismissed = false; g_humiDismissed = false; + g_vitalLostDismissed = false; } else { // 告警触发 g_alarm.executeAlarm(cmd); @@ -263,7 +270,8 @@ TelemetryData buildTelemetry(const RadarRawData& radar) { t.breathRate = radar.breathRate; t.body = radar.body; t.sleepState = radar.sleepState; - t.sleepScore = radar.sleepScore; + // 清醒时不报睡眠评分 (雷达此时的值是残留/无意义), 避免误导 + t.sleepScore = (radar.resident && radar.sleepState != 0) ? radar.sleepScore : 0; t.radarValid = radar.radarValid; t.vitalValid = radar.vitalValid; @@ -425,6 +433,37 @@ void checkLocalAlarms(const RadarRawData& radar) { } #endif // RADAR_TYPE != AFD1 + // ─── ★★★★★ 人身安全: 生命体征丢失 (在床但心率呼吸持续为0 → 疑似骤停/死亡) ─── + // 用 resident(在床) 而非 presence(有人): 人离房时 presence 有保持时间会误报, + // 而 resident 离床即清, 精准区分"人离开"和"人死在床上"。复用 心率+呼吸异常 类型。 +#if RADAR_TYPE != RADAR_TYPE_AFD1 + if (enVital && millis() > 30000) { + bool vitalLost = radar.resident && radar.heartRate == 0 && radar.breathRate == 0; + if (vitalLost) { + if (!g_vitalLostActive && !g_vitalLostDismissed) { + if (g_vitalLostMs == 0) { + g_vitalLostMs = millis(); + } else if (millis() - g_vitalLostMs >= VITAL_LOST_MS) { + g_vitalLostActive = true; + g_vitalLostMs = 0; + g_alarm.triggerLocal(AlarmManager::ALARM_HEART_ABNORMAL, "生命体征丢失(疑似骤停)"); + g_alarm.triggerLocal(AlarmManager::ALARM_BREATH_ABNORMAL, "生命体征丢失(疑似骤停)"); + } + } + } else { + g_vitalLostMs = 0; + if (g_vitalLostActive) { + g_vitalLostActive = false; + g_alarm.clearLocal(AlarmManager::ALARM_HEART_ABNORMAL); + g_alarm.clearLocal(AlarmManager::ALARM_BREATH_ABNORMAL); + } + if (g_vitalLostDismissed) { + g_vitalLostDismissed = false; // 体征恢复或离床后重新武装 + } + } + } +#endif // RADAR_TYPE != AFD1 + // ─── ★★★ 环境安全: 水浸 (仅浴室) ─── if (enWater) { bool waterLeak = !g_sensor.getRainDO() && g_sensor.isRainValid(); @@ -594,12 +633,15 @@ void setup() { g_radarTimeoutCnt = 0; g_fallClearMs = 0; g_fallDismissed = true; // 抑制重复报警, 等跌倒信号消了再恢复 + g_vitalLostMs = 0; + g_vitalLostActive = false; // 其余类型同样抑制, 条件恢复正常后才重新武装 g_waterDismissed = true; g_heartDismissed = true; g_breathDismissed = true; g_tempDismissed = true; g_humiDismissed = true; + g_vitalLostDismissed = true; }); g_buttons.onProvisionLongPress([]() { g_wifi.startConfigPortal(); diff --git a/src/mqtt_manager.cpp b/src/mqtt_manager.cpp index e307c16..c6d18eb 100644 --- a/src/mqtt_manager.cpp +++ b/src/mqtt_manager.cpp @@ -145,6 +145,16 @@ bool MQTTManager::isConnected() { // ============================================================ void MQTTManager::loop() { + // 连接实际已断开 (网络抖动/broker重启/心跳超时), 复位标志触发重连 + // 否则 _connected 永远为 true, 下面 else 分支只调 _mqtt.loop() 却不重连, + // 设备会卡在"WiFi通/MQTT死"的僵尸状态 (小程序显示离线) + if (_connected && !_mqtt.connected()) { + Serial.println("[MQTT] ⚠️ 连接断开, 触发重连..."); + _connected = false; + _reconnectInterval = MQTT_RECONNECT_MIN; + _lastReconnectAttempt = 0; + } + if (!_connected) { uint32_t now = millis(); if (now - _lastReconnectAttempt >= _reconnectInterval) {