From 006d3bd00c178f589b6fd90f2852ea43b24d4766 Mon Sep 17 00:00:00 2001 From: meck Date: Fri, 10 Jul 2026 10:11:08 +0000 Subject: [PATCH] T-Watch S3: alarm rings for the full 5 minutes, PWR key dismisses The alarm was self-dismissing after ~10-15 seconds. Cause: AUTO_OFF_MILLIS is 15000, so the display turned off shortly after the alarm fired; UITask.cpp:2769 raise-to-wake then saw !_display->isOn() && board.tiltFired() -- the buzzing motor on the wrist is exactly what trips the BMA423 tilt detector -- and called setCurrScreen(lock_screen). The 'navigating away counts as a dismiss' rule added with the alarm screen then killed it. Any tap did the same. While ringing, UITask::loop now holds the alarm screen current and pushes _auto_off forward each iteration. The display therefore stays on, raise-to-wake is never reached, and checkDisplayOn() passes KEY_ENTER through to handleInput() rather than swallowing the first press as a wake. Dismissal is now the PWR key only (KEY_ENTER). Taps are swallowed. The WATCH_ALARM_RINGING_MS timeout (5 minutes) is unchanged and remains the backstop. Ringing footer updated from 'Tap to dismiss' to 'Press PWR to dismiss'. The ordering inside loop() makes this safe: the button dispatch and curr->handleInput() both run before the alarm hold, so a dismiss takes effect on the same iteration and the hold is not re-applied. The 90s per-slot fire cooldown prevents an immediate re-fire within the alarm's own minute. --- examples/companion_radio/ui-new/UITask.cpp | 16 +++++++++------- variants/lilygo_twatch_s3/WatchAlarmScreen.h | 15 ++++++++++++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index cb694f3c..c755b4bc 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -2476,15 +2476,17 @@ void UITask::loop() { // with the display off or another screen showing. if (watch_alarm_screen) { WatchAlarmScreen* wa = (WatchAlarmScreen*)watch_alarm_screen; - if (wa->tick()) { - setCurrScreen(watch_alarm_screen); + bool fired = wa->tick(); + if (fired || wa->isRinging()) { + // Hold the screen and the display for the whole ring. Without this the + // display auto-offs after AUTO_OFF_MILLIS, the buzzing trips the BMA423 + // tilt detector, and raise-to-wake below swaps in the clock screen. It + // also guarantees the PWR key reaches handleInput() as KEY_ENTER rather + // than being consumed by checkDisplayOn() as a wake. + if (curr != watch_alarm_screen) setCurrScreen(watch_alarm_screen); if (_display != NULL && !_display->isOn()) _display->turnOn(); _auto_off = millis() + AUTO_OFF_MILLIS; - _next_refresh = 0; - } else if (wa->isRinging() && curr != watch_alarm_screen) { - // Navigating away (e.g. the status-bar tap that goes home) counts as a - // dismiss, otherwise the motor would keep buzzing off-screen. - wa->dismiss(); + if (fired) _next_refresh = 0; } } #endif diff --git a/variants/lilygo_twatch_s3/WatchAlarmScreen.h b/variants/lilygo_twatch_s3/WatchAlarmScreen.h index fb9e8a4a..a7f19602 100644 --- a/variants/lilygo_twatch_s3/WatchAlarmScreen.h +++ b/variants/lilygo_twatch_s3/WatchAlarmScreen.h @@ -12,6 +12,11 @@ // Alarm is the DRV2605L on I2C 0x5A driving the 0827 coin motor. Its BLDO2 rail // is already up from power_init(), so no motorEnable() call is needed. // +// A ringing alarm is dismissed ONLY by the PWR key (which arrives as KEY_ENTER), +// or by the WATCH_ALARM_RINGING_MS timeout. Taps are swallowed, and UITask::loop +// holds this screen current and the display awake for the duration -- otherwise +// the buzzing trips the BMA423 tilt detector and raise-to-wake steals the screen. +// // Config lives on the LittleFS partition mounted at /maps in main.cpp -- the // same one the Plus uses for map tiles. The watch has no SD card. // @@ -334,7 +339,7 @@ private: display.setColor(DisplayDriver::LIGHT); display.setTextSize(1); display.drawTextCentered(display.width() / 2, display.height() - 24, "ALARM"); - ((LGFXDisplay*)&display)->printSmallFont(24, display.height() - 10, "Tap to dismiss"); + ((LGFXDisplay*)&display)->printSmallFont(14, display.height() - 10, "Press PWR to dismiss"); } public: @@ -350,7 +355,8 @@ public: // Physical tap in logical (display.width()) coordinates. Returns true if the // tap was consumed. The status-bar strip is handled by main.cpp before this. bool handleTap(int lx, int ly) { - if (_mode == RINGING) { dismiss(); return true; } + // Ringing: swallow taps. Only the PWR key dismisses (see handleInput). + if (_mode == RINGING) return true; if (_mode == LIST) { for (int i = 0; i < WATCH_ALARM_SLOT_COUNT; i++) { @@ -395,7 +401,10 @@ public: bool handleInput(char c) override { // The PMU short press arrives as KEY_ENTER. - if (_mode == RINGING) { dismiss(); return true; } + if (_mode == RINGING) { + if (c == KEY_ENTER) dismiss(); // PWR key only + return true; // swallow everything else + } if (_mode == EDIT && c == KEY_ENTER) { commitEdit(); return true; } if (_mode == LIST && c == KEY_ENTER) { _edit = _cfg.slots[_sel];