feat: 按钮去抖+配网界面美化+水浸离线+LED独立任务+报警全拓展(人身优先)+有人才检测
This commit is contained in:
+104
-27
@@ -1,6 +1,12 @@
|
||||
/**
|
||||
* @file button_manager.cpp
|
||||
* @brief 三按钮管理器实现 - 去抖 + 长按/短按
|
||||
* @brief 三按钮管理器实现 - 双向去抖 + 长按/短按 + 超时自愈
|
||||
*
|
||||
* 核心改进 (v2):
|
||||
* 1. 按下沿和释放沿都经过 BTN_DEBOUNCE_MS 稳定确认
|
||||
* 2. 长按期间持续检测 (不再等到松手才触发)
|
||||
* 3. 按下超过 BTN_STUCK_TIMEOUT_MS 自动复位, 防止状态机卡死
|
||||
* 4. 用 stableState 替代瞬时 raw 驱动状态, 消除噪声误触发
|
||||
*/
|
||||
|
||||
#include "button_manager.h"
|
||||
@@ -14,9 +20,19 @@ ButtonManager::ButtonManager()
|
||||
, _cbDismiss(nullptr)
|
||||
, _cbPage(nullptr)
|
||||
{
|
||||
_btnProvision = { BTN_PROVISION_PIN, false, 0, false };
|
||||
_btnDismiss = { BTN_ALARM_DISMISS_PIN, false, 0, false };
|
||||
_btnPage = { BTN_PAGE_SWITCH_PIN, false, 0, false };
|
||||
auto initBtn = [](ButtonState& s, uint8_t pin) {
|
||||
s.pin = pin;
|
||||
s.stableState = false;
|
||||
s.rawState = false;
|
||||
s.changeTime = 0;
|
||||
s.pressTime = 0;
|
||||
s.longTriggered = false;
|
||||
s.shortPending = false;
|
||||
};
|
||||
|
||||
initBtn(_btnProvision, BTN_PROVISION_PIN);
|
||||
initBtn(_btnDismiss, BTN_ALARM_DISMISS_PIN);
|
||||
initBtn(_btnPage, BTN_PAGE_SWITCH_PIN);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -28,9 +44,19 @@ void ButtonManager::begin() {
|
||||
pinMode(BTN_ALARM_DISMISS_PIN, INPUT_PULLUP);
|
||||
pinMode(BTN_PAGE_SWITCH_PIN, INPUT_PULLUP);
|
||||
|
||||
Serial.println(F("[按钮] ✅ 三按钮初始化完成"));
|
||||
// 读取初始状态, 避免启动时的虚假边沿
|
||||
_btnProvision.rawState = _readPin(BTN_PROVISION_PIN);
|
||||
_btnProvision.stableState = _btnProvision.rawState;
|
||||
_btnDismiss.rawState = _readPin(BTN_ALARM_DISMISS_PIN);
|
||||
_btnDismiss.stableState = _btnDismiss.rawState;
|
||||
_btnPage.rawState = _readPin(BTN_PAGE_SWITCH_PIN);
|
||||
_btnPage.stableState = _btnPage.rawState;
|
||||
|
||||
Serial.println(F("[按钮] ✅ 三按钮初始化完成 (双向去抖+超时自愈)"));
|
||||
Serial.printf(" 配网: GPIO%d | 解除报警: GPIO%d | 翻页: GPIO%d\n",
|
||||
BTN_PROVISION_PIN, BTN_ALARM_DISMISS_PIN, BTN_PAGE_SWITCH_PIN);
|
||||
Serial.printf(" 去抖:%dms | 长按:%dms | 卡死超时:%dms\n",
|
||||
BTN_DEBOUNCE_MS, BTN_LONG_PRESS_MS, BTN_STUCK_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -49,40 +75,91 @@ void ButtonManager::loop() {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 单按钮状态机
|
||||
// 单按钮状态机 (基于稳定状态, 非瞬时边沿)
|
||||
// ============================================================
|
||||
|
||||
void ButtonManager::_processButton(ButtonState& btn, bool hasShort, bool hasLong) {
|
||||
bool raw = _readPin(btn.pin); // LOW = 按下
|
||||
uint32_t now = millis();
|
||||
bool raw = _readPin(btn.pin); // true = 按下 (LOW)
|
||||
|
||||
if (raw && !btn.pressed) {
|
||||
// 按下沿
|
||||
btn.pressed = true;
|
||||
btn.pressTime = millis();
|
||||
btn.longTriggered = false;
|
||||
// ── 第1步: raw 状态变化时启动去抖计时 ──
|
||||
if (raw != btn.rawState) {
|
||||
btn.rawState = raw;
|
||||
btn.changeTime = now;
|
||||
// 不立即改变 stableState — 等稳定确认
|
||||
}
|
||||
else if (!raw && btn.pressed) {
|
||||
// 松开沿
|
||||
btn.pressed = false;
|
||||
uint32_t holdTime = millis() - btn.pressTime;
|
||||
|
||||
if (holdTime < BTN_DEBOUNCE_MS) {
|
||||
return; // 抖动脉冲,忽略
|
||||
// ── 第2步: 去抖确认 — raw 保持稳定超过 BTN_DEBOUNCE_MS 才生效 ──
|
||||
bool debounced = (now - btn.changeTime >= BTN_DEBOUNCE_MS);
|
||||
bool stateChanged = (btn.rawState != btn.stableState);
|
||||
|
||||
if (debounced && stateChanged) {
|
||||
// 状态正式切换
|
||||
btn.stableState = btn.rawState;
|
||||
|
||||
if (btn.stableState) {
|
||||
// ── 确认按下 ──
|
||||
btn.pressTime = now;
|
||||
btn.longTriggered = false;
|
||||
btn.shortPending = hasShort; // 只有支持短按的按钮才标记待判定
|
||||
} else {
|
||||
// ── 确认释放 ──
|
||||
uint32_t holdTime = now - btn.pressTime;
|
||||
|
||||
// 短按判定: 按下时长在去抖和长按阈值之间
|
||||
if (btn.shortPending && holdTime >= BTN_DEBOUNCE_MS
|
||||
&& holdTime < BTN_LONG_PRESS_MS && !btn.longTriggered) {
|
||||
// 根据引脚分发短按回调
|
||||
if (btn.pin == BTN_ALARM_DISMISS_PIN && _cbDismiss) {
|
||||
_cbDismiss();
|
||||
}
|
||||
if (btn.pin == BTN_PAGE_SWITCH_PIN && _cbPage) {
|
||||
_cbPage();
|
||||
}
|
||||
}
|
||||
|
||||
btn.shortPending = false;
|
||||
btn.longTriggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLong && holdTime >= BTN_LONG_PRESS_MS && !btn.longTriggered) {
|
||||
// 长按触发
|
||||
// ── 第3步: 长按检测 (在按下稳定期间持续检查) ──
|
||||
if (hasLong && btn.stableState && !btn.longTriggered) {
|
||||
uint32_t holdTime = now - btn.pressTime;
|
||||
if (holdTime >= BTN_LONG_PRESS_MS) {
|
||||
btn.longTriggered = true;
|
||||
if (_cbProvision) _cbProvision();
|
||||
}
|
||||
else if (hasShort && holdTime >= BTN_DEBOUNCE_MS && holdTime < BTN_LONG_PRESS_MS) {
|
||||
// 短按触发
|
||||
if (btn.pin == BTN_ALARM_DISMISS_PIN && _cbDismiss) _cbDismiss();
|
||||
if (btn.pin == BTN_PAGE_SWITCH_PIN && _cbPage) _cbPage();
|
||||
btn.shortPending = false; // 长按已触发, 不再判定短按
|
||||
if (_cbProvision) {
|
||||
_cbProvision();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 第4步: 卡死自愈 — 按下超时强制复位 ──
|
||||
if (btn.stableState && (now - btn.pressTime >= BTN_STUCK_TIMEOUT_MS)) {
|
||||
Serial.printf("[按钮] ⚠️ GPIO%d 按下超时 %lums, 强制复位\n",
|
||||
btn.pin, now - btn.pressTime);
|
||||
_resetButton(btn);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 强制复位
|
||||
// ============================================================
|
||||
|
||||
void ButtonManager::_resetButton(ButtonState& btn) {
|
||||
btn.stableState = false;
|
||||
btn.rawState = _readPin(btn.pin); // 重新同步真实硬件状态
|
||||
btn.changeTime = 0;
|
||||
btn.pressTime = 0;
|
||||
btn.longTriggered = false;
|
||||
btn.shortPending = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 读引脚 (INPUT_PULLUP → LOW = 按下)
|
||||
// ============================================================
|
||||
|
||||
bool ButtonManager::_readPin(uint8_t pin) {
|
||||
return digitalRead(pin) == LOW; // INPUT_PULLUP: LOW = 按下
|
||||
return digitalRead(pin) == LOW;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user