From a4ead96c39dd5aff5e995747f92ebc6c48ba03a8 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 8 Jun 2025 09:18:10 -0500 Subject: [PATCH] ExternalNotifications now observe inputBroker --- src/input/ButtonThread.cpp | 285 ++++++++------------- src/input/ButtonThread.h | 1 - src/modules/CannedMessageModule.cpp | 3 - src/modules/ExternalNotificationModule.cpp | 13 + src/modules/ExternalNotificationModule.h | 6 + 5 files changed, 128 insertions(+), 180 deletions(-) diff --git a/src/input/ButtonThread.cpp b/src/input/ButtonThread.cpp index 05e38b5ca..2fac03cff 100644 --- a/src/input/ButtonThread.cpp +++ b/src/input/ButtonThread.cpp @@ -5,7 +5,6 @@ #include "GPS.h" #endif #include "MeshService.h" -#include "PowerFSM.h" #include "RadioLibInterface.h" #include "buzz.h" #include "input/InputBroker.h" @@ -217,174 +216,118 @@ int32_t ButtonThread::runOnce() #endif if (btnEvent != BUTTON_EVENT_NONE) { - if (screen) { -#if HAS_SCREEN - switch (btnEvent) { - case BUTTON_EVENT_PRESSED: { - LOG_WARN("press!"); + switch (btnEvent) { + case BUTTON_EVENT_PRESSED: { + LOG_WARN("press!"); - // Play boop sound for every button press - playBoop(); + // Play boop sound for every button press + playBoop(); - // Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event) - InputEvent evt; - evt.source = _originName; - evt.kbchar = 0; - evt.touchX = 0; - evt.touchY = 0; - evt.inputEvent = INPUT_BROKER_USER_PRESS; - this->notifyObservers(&evt); - break; - } - case BUTTON_EVENT_LONG_PRESSED: { - LOG_WARN("Long press!"); + // Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event) + InputEvent evt; + evt.source = _originName; + evt.kbchar = 0; + evt.touchX = 0; + evt.touchY = 0; + evt.inputEvent = INPUT_BROKER_USER_PRESS; + this->notifyObservers(&evt); - // Play beep sound - playBeep(); - - // Forward long press to InputBroker (but NOT as DOWN/SELECT, just forward a "button long press" event) - if (inputBroker) { - InputEvent evt = {"button", INPUT_BROKER_SELECT, 0, 0, 0}; - this->notifyObservers(&evt); - } - break; - } - default: - // Ignore all other events on screen devices - break; - } - btnEvent = BUTTON_EVENT_NONE; -#endif - } else { - // On devices without screen: full legacy logic - switch (btnEvent) { - case BUTTON_EVENT_PRESSED: { - LOG_BUTTON("press!"); - - // Play boop sound for every button press - playBoop(); - - // If a nag notification is running, stop it and prevent other actions - if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { - externalNotificationModule->stopNow(); - break; - } - - // Start tracking for potential combination + // Start tracking for potential combination + if (!screen) { waitingForLongPress = true; shortPressTime = millis(); - - powerFSM.trigger(EVENT_PRESS); - break; } + break; + } - case BUTTON_EVENT_PRESSED_SCREEN: { - LOG_BUTTON("AltPress!"); + case BUTTON_EVENT_PRESSED_SCREEN: { + LOG_BUTTON("AltPress!"); - // Play boop sound for every button press - playBoop(); + // Play boop sound for every button press + playBoop(); - // Reset combination tracking - waitingForLongPress = false; + // Reset combination tracking + waitingForLongPress = false; -#ifdef ELECROW_ThinkNode_M1 - // If a nag notification is running, stop it and prevent other actions - if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { - externalNotificationModule->stopNow(); - break; - } - powerFSM.trigger(EVENT_PRESS); - break; -#endif - break; - } + break; + } - case BUTTON_EVENT_DOUBLE_PRESSED: { - LOG_BUTTON("Double press!"); + case BUTTON_EVENT_DOUBLE_PRESSED: { // not wired in if screen detected + LOG_BUTTON("Double press!"); - // Play boop sound for every button press - playBoop(); + // Play boop sound for every button press + playBoop(); - // Reset combination tracking - waitingForLongPress = false; + // Reset combination tracking + waitingForLongPress = false; -#ifdef ELECROW_ThinkNode_M1 - digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); - break; -#endif + // Send GPS position immediately + sendAdHocPosition(); - // Send GPS position immediately - sendAdHocPosition(); + break; + } - // Show temporary on-screen confirmation banner for 3 seconds - if (screen) - screen->showOverlayBanner("Ad-hoc Ping Sent", 3000); - break; - } + case BUTTON_EVENT_MULTI_PRESSED: { // not wired in when screen is present + LOG_BUTTON("Mulitipress! %hux", multipressClickCount); - case BUTTON_EVENT_MULTI_PRESSED: { - LOG_BUTTON("Mulitipress! %hux", multipressClickCount); + // Play boop sound for every button press + playBoop(); - // Play boop sound for every button press - playBoop(); + // Reset combination tracking + waitingForLongPress = false; - // Reset combination tracking - waitingForLongPress = false; - - switch (multipressClickCount) { + switch (multipressClickCount) { #if HAS_GPS && !defined(ELECROW_ThinkNode_M1) - // 3 clicks: toggle GPS - case 3: - if (!config.device.disable_triple_click && (gps != nullptr)) { - gps->toggleGpsMode(); - } - break; + // 3 clicks: toggle GPS + case 3: + if (!config.device.disable_triple_click && (gps != nullptr)) { + gps->toggleGpsMode(); + } + break; #endif #if defined(USE_EINK) && defined(PIN_EINK_EN) && !defined(ELECROW_ThinkNode_M1) // i.e. T-Echo - // 4 clicks: toggle backlight - case 4: - digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); - break; -#endif - // No valid multipress action - default: - break; - } // end switch: click count - + // 4 clicks: toggle backlight + case 4: + digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); break; - } // end multipress event +#endif + // No valid multipress action + default: + break; + } // end switch: click count - case BUTTON_EVENT_LONG_PRESSED: { - LOG_BUTTON("Long press!"); + break; + } // end multipress event - // Check if this is part of a short-press + long-press combination - if (waitingForLongPress && (millis() - shortPressTime) <= BUTTON_COMBO_TIMEOUT_MS) { - LOG_BUTTON("Combo detected: short-press + long-press!"); - btnEvent = BUTTON_EVENT_COMBO_SHORT_LONG; - waitingForLongPress = false; - break; - } + case BUTTON_EVENT_LONG_PRESSED: { + LOG_BUTTON("Long press!"); - // Reset combination tracking + // Check if this is part of a short-press + long-press combination + if (waitingForLongPress && (millis() - shortPressTime) <= BUTTON_COMBO_TIMEOUT_MS) { + LOG_BUTTON("Combo detected: short-press + long-press!"); + btnEvent = BUTTON_EVENT_COMBO_SHORT_LONG; waitingForLongPress = false; - - powerFSM.trigger(EVENT_PRESS); - - if (screen) { - // Show shutdown message as a temporary overlay banner - screen->showOverlayBanner("Shutting Down..."); // Display for 3 seconds - } - - // Lead-up sound already played during button hold - // Just a simple beep to confirm long press threshold reached - playBeep(); break; } - // Do actual shutdown when button released, otherwise the button release - // may wake the board immediatedly. - case BUTTON_EVENT_LONG_RELEASED: { + // Forward long press to InputBroker (but NOT as DOWN/SELECT, just forward a "button long press" event) + InputEvent evt = {"button", INPUT_BROKER_SELECT, 0, 0, 0}; + this->notifyObservers(&evt); + + // Reset combination tracking + waitingForLongPress = false; + + // Lead-up sound already played during button hold + // Just a simple beep to confirm long press threshold reached + playBeep(); + break; + } + + // Do actual shutdown when button released, otherwise the button release + // may wake the board immediatedly. + case BUTTON_EVENT_LONG_RELEASED: { + if (!screen) { LOG_INFO("Shutdown from long press"); // Reset combination tracking @@ -394,59 +337,49 @@ int32_t ButtonThread::runOnce() delay(3000); power->shutdown(); nodeDB->saveToDisk(); - break; } + break; + } #ifdef BUTTON_PIN_TOUCH - case BUTTON_EVENT_TOUCH_LONG_PRESSED: { - LOG_BUTTON("Touch press!"); + case BUTTON_EVENT_TOUCH_LONG_PRESSED: { + LOG_BUTTON("Touch press!"); - // Play boop sound for every button press - playBoop(); + // Play boop sound for every button press + playBoop(); - // Reset combination tracking - waitingForLongPress = false; - - // Ignore if: no screen - if (!screen) - break; + // Reset combination tracking + waitingForLongPress = false; #ifdef TTGO_T_ECHO - // Ignore if: TX in progress - // Uncommon T-Echo hardware bug, LoRa TX triggers touch button - if (!RadioLibInterface::instance || RadioLibInterface::instance->isSending()) - break; + // Ignore if: TX in progress + // Uncommon T-Echo hardware bug, LoRa TX triggers touch button + if (!RadioLibInterface::instance || RadioLibInterface::instance->isSending()) + break; #endif - // Wake if asleep - if (powerFSM.getState() == &stateDARK) - powerFSM.trigger(EVENT_PRESS); - - // Update display (legacy behaviour) - screen->forceDisplay(); - break; - } + break; + } #endif // BUTTON_PIN_TOUCH - case BUTTON_EVENT_COMBO_SHORT_LONG: { - // Placeholder for short-press + long-press combination - LOG_BUTTON("Short-press + Long-press combination detected!"); + case BUTTON_EVENT_COMBO_SHORT_LONG: { + // Placeholder for short-press + long-press combination + LOG_BUTTON("Short-press + Long-press combination detected!"); - // Play the combination tune - playComboTune(); + // Play the combination tune + playComboTune(); - // Optionally show a message on screen - if (screen) { - screen->showOverlayBanner("Combo Tune Played", 2000); - } - break; + // Optionally show a message on screen + if (screen) { + screen->showOverlayBanner("Combo Tune Played", 2000); } + break; + } - default: - break; - } - btnEvent = BUTTON_EVENT_NONE; - } // (!screen) + default: + break; + } + btnEvent = BUTTON_EVENT_NONE; } return 50; diff --git a/src/input/ButtonThread.h b/src/input/ButtonThread.h index 937bb93e4..c3f2ec2b6 100644 --- a/src/input/ButtonThread.h +++ b/src/input/ButtonThread.h @@ -106,7 +106,6 @@ class ButtonThread : public Observable, public concurrency:: static void sendAdHocPosition(); // IRQ callbacks - static void userButtonPressed() { btnEvent = BUTTON_EVENT_PRESSED; } static void userButtonPressedScreen() { btnEvent = BUTTON_EVENT_PRESSED_SCREEN; } static void userButtonDoublePressed() { btnEvent = BUTTON_EVENT_DOUBLE_PRESSED; } static void userButtonMultiPressed(void *callerThread); // Retrieve click count from non-static Onebutton while still valid diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 2cddb42cd..dbbfcaadf 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -8,7 +8,6 @@ #include "FSCommon.h" #include "MeshService.h" #include "NodeDB.h" -#include "PowerFSM.h" // needed for button bypass #include "SPILock.h" #include "buzz.h" #include "detect/ScanI2C.h" @@ -587,7 +586,6 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo // Normal canned message selection if (runState == CANNED_MESSAGE_RUN_STATE_INACTIVE || runState == CANNED_MESSAGE_RUN_STATE_DISABLED) { - powerFSM.trigger(EVENT_PRESS); } else { payload = runState; runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT; @@ -1002,7 +1000,6 @@ int32_t CannedMessageModule::runOnce() } if ((this->messagesCount > this->currentMessageIndex) && (strlen(this->messages[this->currentMessageIndex]) > 0)) { if (strcmp(this->messages[this->currentMessageIndex], "~") == 0) { - powerFSM.trigger(EVENT_PRESS); return INT32_MAX; } else { sendText(this->dest, this->channel, this->messages[this->currentMessageIndex], true); diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index fe80404f3..5da21d664 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -350,6 +350,9 @@ ExternalNotificationModule::ExternalNotificationModule() // moduleConfig.external_notification.alert_message_buzzer = true; if (moduleConfig.external_notification.enabled) { + if (inputBroker) // put our callback in the inputObserver list + inputObserver.observe(inputBroker); + if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) { memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); @@ -595,4 +598,14 @@ void ExternalNotificationModule::handleSetRingtone(const char *from_msg) if (changed) { nodeDB->saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig); } +} + +int ExternalNotificationModule::handleInputEvent(const InputEvent *event) +{ + LOG_WARN("ExternalNotification Handle Input"); + if (nagCycleCutoff != UINT32_MAX) { + stopNow(); + return 1; + } + return 0; } \ No newline at end of file diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h index 089e0b8a3..6de428d13 100644 --- a/src/modules/ExternalNotificationModule.h +++ b/src/modules/ExternalNotificationModule.h @@ -3,6 +3,8 @@ #include "SinglePortModule.h" #include "concurrency/OSThread.h" #include "configuration.h" +#include "input/InputBroker.h" + #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6) #include #else @@ -27,11 +29,15 @@ class rtttl */ class ExternalNotificationModule : public SinglePortModule, private concurrency::OSThread { + CallbackObserver inputObserver = + CallbackObserver(this, &ExternalNotificationModule::handleInputEvent); uint32_t output = 0; public: ExternalNotificationModule(); + int handleInputEvent(const InputEvent *arg); + uint32_t nagCycleCutoff = 1; void setExternalState(uint8_t index = 0, bool on = false);