feat: 开机确认窗口 + 快捷回复/九宫格导航 + 充电检测加速
This commit is contained in:
@@ -116,13 +116,11 @@ bool TCA9535ButtonThread::init()
|
||||
tca9535GpsEn(true);
|
||||
|
||||
// ===================================================================
|
||||
// 第二步:POWER_EN 已由 main.cpp 在 Wire.begin() 后立即拉高(早期锁定)
|
||||
// 此处只需确认状态机进入 RUNNING,不再需要等待 P1.3 按住 2 秒。
|
||||
// 原因:系统从 Wire.begin() 到 tca9535ButtonThread::init() 之间需要
|
||||
// 数秒的初始化时间(LoRa/WiFi/BLE/GPS 等),用户早已松开按键,
|
||||
// 无法在此处等待。开机供电维持已在 main.cpp 最早期完成。
|
||||
// 第三步:POWER_EN 已由 main.cpp 在 Wire.begin() 后立即拉高,
|
||||
// 并等待 P1.3 持续按住 2 秒确认开机(超时 3 秒则断电关机)。
|
||||
// 此处只需确认状态机进入 RUNNING。
|
||||
// ===================================================================
|
||||
LOG_INFO("TCA9535: POWER_EN already latched in early boot, skipping boot-hold wait");
|
||||
LOG_INFO("TCA9535: Boot already confirmed in early boot, state=RUNNING");
|
||||
_powerState = TCA9535PowerState::RUNNING;
|
||||
|
||||
// ===================================================================
|
||||
@@ -211,7 +209,7 @@ int32_t TCA9535ButtonThread::runOnce()
|
||||
// 充电检测:轮询 P1.1 (CHARGE_DET),高电平=正在充电
|
||||
// ===================================================================
|
||||
#ifdef TCA9535_CHARGE_DET_PIN
|
||||
if (millis() - _chargeDetLastMs >= 2000) {
|
||||
if (millis() - _chargeDetLastMs >= 500) {
|
||||
_chargeDetLastMs = millis();
|
||||
bool charging = tca9535ReadChargeDet();
|
||||
if (charging != tca9535IsCharging) {
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
*
|
||||
* 电源管理逻辑:
|
||||
* 开机:物理按键按下 → MOS 导通 → ESP32/TCA9535 得电
|
||||
* init() 读 P1.3,持续按住 2 秒 → tca9535PowerEn(true) 维持供电
|
||||
* 未按满 2 秒松开 → 不拉高 POWER_EN → MOS 断开 → 断电
|
||||
* main.cpp Wire.begin() 后立即拉高 POWER_EN 锁住供电,
|
||||
* 然后等待 P1.3 持续按住 2 秒确认开机(超时 3 秒则断电关机)
|
||||
* 关机:运行中 P1.3 持续低电平 2 秒 → tca9535PowerEn(false) → 断电
|
||||
*
|
||||
* 寄存器布局:
|
||||
|
||||
@@ -586,10 +586,43 @@ void setup()
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
#ifdef HAS_TCA9535_BUTTON
|
||||
// TCA9535 POWER_EN 必须在 I²C 初始化完成后立即拉高,否则用户松开按键后
|
||||
// MOS 断电,系统在 setup() 中途就会掉电。此处无条件锁住供电,
|
||||
// 后续 tca9535ButtonThread::init() 只负责键盘和状态机初始化。
|
||||
// MOS 断电,系统在 setup() 中途就会掉电。
|
||||
tca9535PowerEn(true);
|
||||
LOG_INFO("TCA9535: POWER_EN latched HIGH (early boot)");
|
||||
|
||||
// 开机确认窗口:检测 P1.3 是否持续按住 2 秒,防止意外通电卡死
|
||||
// 最多等待 3 秒,3 秒内未连续按满 2 秒则断电关机
|
||||
{
|
||||
bool bootConfirmed = false;
|
||||
uint32_t pressStart = 0;
|
||||
uint32_t deadline = millis() + 3000;
|
||||
LOG_INFO("TCA9535: Waiting for 2s button hold to confirm boot (timeout 3s)...");
|
||||
|
||||
while (millis() < deadline) {
|
||||
bool pressed = tca9535ReadPowerBoot();
|
||||
if (pressed && pressStart == 0) {
|
||||
pressStart = millis();
|
||||
} else if (pressed && pressStart != 0) {
|
||||
if (millis() - pressStart >= 2000) {
|
||||
bootConfirmed = true;
|
||||
LOG_INFO("TCA9535: Boot confirmed (button held 2s)");
|
||||
break;
|
||||
}
|
||||
} else if (!pressed && pressStart != 0) {
|
||||
// 松手重置计时
|
||||
pressStart = 0;
|
||||
}
|
||||
delay(50); // 50ms 轮询
|
||||
}
|
||||
|
||||
if (!bootConfirmed) {
|
||||
LOG_WARN("TCA9535: Boot not confirmed, shutting down");
|
||||
tca9535PowerEn(false);
|
||||
// 等待 MOS 断开
|
||||
delay(500);
|
||||
doDeepSleep(0, false, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
if (portduino_config.i2cdev != "") {
|
||||
|
||||
@@ -368,6 +368,18 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
case CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE:
|
||||
return 1;
|
||||
|
||||
// Canned message list: LEFT/RIGHT enters free text input
|
||||
case CANNED_MESSAGE_RUN_STATE_ACTIVE:
|
||||
if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_RIGHT) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
||||
requestFocus();
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
notifyObservers(&e);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
// If sending, block all input except global/system (handled above)
|
||||
case CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER:
|
||||
return handleEmotePickerInput(event);
|
||||
@@ -380,7 +392,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_RIGHT) {
|
||||
break;
|
||||
}
|
||||
// Handle UP/DOWN: activate canned message list!
|
||||
// Handle UP/DOWN: activate canned message list
|
||||
if (event->inputEvent == INPUT_BROKER_UP || event->inputEvent == INPUT_BROKER_DOWN ||
|
||||
event->inputEvent == INPUT_BROKER_ALT_LONG) {
|
||||
LaunchWithDestination(NODENUM_BROADCAST);
|
||||
@@ -415,14 +427,14 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
bool CannedMessageModule::isUpEvent(const InputEvent *event)
|
||||
{
|
||||
return event->inputEvent == INPUT_BROKER_UP ||
|
||||
((runState == CANNED_MESSAGE_RUN_STATE_ACTIVE || runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER ||
|
||||
((runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER ||
|
||||
runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) &&
|
||||
(event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_ALT_PRESS));
|
||||
}
|
||||
bool CannedMessageModule::isDownEvent(const InputEvent *event)
|
||||
{
|
||||
return event->inputEvent == INPUT_BROKER_DOWN ||
|
||||
((runState == CANNED_MESSAGE_RUN_STATE_ACTIVE || runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER ||
|
||||
((runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER ||
|
||||
runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) &&
|
||||
(event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS));
|
||||
}
|
||||
@@ -853,18 +865,13 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move cursor left
|
||||
if (event->inputEvent == INPUT_BROKER_LEFT) {
|
||||
payload = INPUT_BROKER_LEFT;
|
||||
lastTouchMillis = millis();
|
||||
runOnce();
|
||||
return true;
|
||||
}
|
||||
// Move cursor right
|
||||
if (event->inputEvent == INPUT_BROKER_RIGHT) {
|
||||
payload = INPUT_BROKER_RIGHT;
|
||||
lastTouchMillis = millis();
|
||||
runOnce();
|
||||
// LEFT/RIGHT in FREETEXT: go back to canned message list (ACTIVE), preserving input
|
||||
if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_RIGHT) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
notifyObservers(&e);
|
||||
screen->forceDisplay();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -890,6 +897,14 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
||||
return handleTabSwitch(event); // Reuse tab logic
|
||||
}
|
||||
|
||||
// '*' key from TCA9535 numpad acts as backspace
|
||||
if (event->kbchar == '*') {
|
||||
payload = 0x08;
|
||||
lastTouchMillis = millis();
|
||||
runOnce();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Printable ASCII (add char to draft)
|
||||
if (event->kbchar >= 32 && event->kbchar <= 126) {
|
||||
payload = event->kbchar;
|
||||
|
||||
Reference in New Issue
Block a user