402 lines
13 KiB
C++
402 lines
13 KiB
C++
/**
|
|
* @file alarm_manager.cpp
|
|
* @brief 报警管理器 - 多类型分优先级实现
|
|
*
|
|
* 蜂鸣模式 (人身安全 > 环境 > 设备):
|
|
* 跌倒: 连速哔 (60ms/60ms) — 最急 ★★★★★
|
|
* 心率/呼吸: 5连急哔 (80ms/80ms) — 次急 ★★★★★
|
|
* 水浸: 3连哔 (120ms/100ms) — 中等 ★★★
|
|
* 温湿度: 2声长停 (200ms/400ms) — 温和 ★★
|
|
* 传感器: 单声长哔 (300ms/3s) — 提示 ★
|
|
*/
|
|
|
|
#include "alarm_manager.h"
|
|
|
|
// ============================================================
|
|
// 优先级定义 — 人身安全 > 环境安全 > 设备故障
|
|
// ============================================================
|
|
|
|
uint8_t AlarmManager::_priority(AlarmType t) {
|
|
switch (t) {
|
|
// ★★★★★ 人身安全 — 直接威胁生命
|
|
case ALARM_FALL: return 0;
|
|
case ALARM_HEART_ABNORMAL: return 1;
|
|
case ALARM_BREATH_ABNORMAL: return 1;
|
|
// ★★★ 环境安全 — 潜在危险
|
|
case ALARM_WATER_LEAK: 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() {
|
|
// 有源蜂鸣器: 用 digitalWrite, 不用 PWM
|
|
pinMode(BUZZER_PIN, OUTPUT);
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
|
|
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 = 200; offMs = 200; pauseMs = 0; // 跌倒: 急促哔哔
|
|
break;
|
|
case AlarmManager::ALARM_HEART_ABNORMAL:
|
|
case AlarmManager::ALARM_BREATH_ABNORMAL:
|
|
beeps = 5; onMs = 200; offMs = 150; pauseMs = 500; // 心率/呼吸: 5连哔
|
|
break;
|
|
// 环境安全
|
|
case AlarmManager::ALARM_WATER_LEAK:
|
|
beeps = 3; onMs = 250; offMs = 200; pauseMs = 600; // 水浸: 3连哔
|
|
break;
|
|
// 环境舒适
|
|
case AlarmManager::ALARM_TEMP_ABNORMAL:
|
|
case AlarmManager::ALARM_HUMI_ABNORMAL:
|
|
beeps = 2; onMs = 300; offMs = 500; pauseMs = 1500; // 温湿度: 2声长停
|
|
break;
|
|
// 设备状态
|
|
case AlarmManager::ALARM_SENSOR_FAULT:
|
|
beeps = 1; onMs = 500; offMs = 3000; 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;
|
|
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;
|
|
|
|
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 {
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
_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;
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
Serial.println(F("[告警] ⏰ 蜂鸣器定时关闭"));
|
|
return;
|
|
}
|
|
|
|
uint32_t now = millis();
|
|
|
|
// 云端定时: 哔哔声 (500ms响/500ms停), 不是长鸣
|
|
if (_buzzerOffTime > 0) {
|
|
if (now - _buzzerToggleTime >= 500) {
|
|
_buzzerToggleTime = now;
|
|
_buzzerBeepState = !_buzzerBeepState;
|
|
digitalWrite(BUZZER_PIN, _buzzerBeepState ? HIGH : LOW);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 本地报警: 按优先级模式 — 哔声/停顿交替
|
|
if (_beepsPerCycle == 0) {
|
|
// 连速哔 (跌倒): 简单翻转
|
|
if (now - _buzzerToggleTime >= (_buzzerBeepState ? _beepOnMs : _beepOffMs)) {
|
|
_buzzerToggleTime = now;
|
|
_buzzerBeepState = !_buzzerBeepState;
|
|
digitalWrite(BUZZER_PIN, _buzzerBeepState ? HIGH : LOW);
|
|
}
|
|
} else {
|
|
// 多声一组: N声哔 + 长停顿
|
|
if (!_buzzerBeepState) {
|
|
// 停顿中
|
|
uint32_t pause = (_beepCount >= _beepsPerCycle) ? _cyclePauseMs : _beepOffMs;
|
|
if (now - _buzzerToggleTime >= pause) {
|
|
_buzzerToggleTime = now;
|
|
if (_beepCount >= _beepsPerCycle) _beepCount = 0;
|
|
_buzzerBeepState = true;
|
|
digitalWrite(BUZZER_PIN, HIGH);
|
|
_beepCount++;
|
|
}
|
|
} else {
|
|
// 响声中
|
|
if (now - _buzzerToggleTime >= _beepOnMs) {
|
|
_buzzerToggleTime = now;
|
|
_buzzerBeepState = false;
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 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));
|
|
}
|
|
}
|