From be75f111560086e1bef91e49743fb919e287e084 Mon Sep 17 00:00:00 2001 From: Jason P Date: Thu, 10 Jul 2025 19:49:15 -0500 Subject: [PATCH] Update Screen Wake Default Behavior (#7282) * feat(display): enable screen wake on received messages * feat(menu): add Screen Wakeup option in system menu * feat(ui): update wake on message configuration and refactor save logic * feat(TextMessageModule): conditionally trigger screen wake on received message * Refactoring system menu options for notification and screen. * Fix MUI options in the system menu. * Build out Reboot/Shutdown Menu and consolidate options within it * Trunk fixes * Protobuf ref * Revert generated files * Update plumbing for screen_wakeup_menu * Begin work on crafting a method to stop screen wake for received messages * SharedUIDisplay.cpp doesn't need ExternalNotificationModule.h * Stop screen wake if External Notification is enabled * Removing extra log lines * Add role and battery state checks for not waking screen. Menu updates to resolve some Back options not being linked * Resolve some additional merge conflict related issues * Shouldn't throttle the power menu * Finalize renames of some menus * Flip Flop MUI Menu to avoid accidental clicks * NULL check for powerStatus * Remove "Wakeup" eNum * Update src/graphics/Screen.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * CoPilot was close this should fix the builds --------- Co-authored-by: whywilson Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/graphics/Screen.cpp | 79 +++++++---- src/graphics/Screen.h | 4 + src/graphics/draw/MenuHandler.cpp | 229 ++++++++++++++++++++++++------ src/graphics/draw/MenuHandler.h | 14 +- src/modules/TextMessageModule.cpp | 7 +- 5 files changed, 261 insertions(+), 72 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 2bade47b2..f670225c3 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -83,6 +83,29 @@ extern uint16_t TFT_MESH; #include "platform/portduino/PortduinoGlue.h" #endif +bool shouldWakeOnReceivedMessage() +{ + /* + The goal here is to determine when we do NOT wake up the screen on message received: + - Any ext. notifications are turned on + - If role is not client / client_mute + - If the battery level is very low + */ + if (moduleConfig.external_notification.enabled) { + return false; + } + if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT && + config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) { + return false; + } + if (powerStatus && powerStatus->getBatteryChargePercent() < 10) { + return false; + } + return true; +} + +bool wake_on_received_message = shouldWakeOnReceivedMessage(); // Master Switch to enable here + using namespace meshtastic; /** @todo remove */ namespace graphics @@ -1257,40 +1280,46 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) devicestate.has_rx_text_message = true; // Needed to include the message frame hasUnreadMessage = true; // Enables mail icon in the header setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view - forceDisplay(); // Forces screen redraw - // === Prepare banner content === - const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from); - const char *longName = (node && node->has_user) ? node->user.long_name : nullptr; + // Only wake/force display if the configuration allows it + wake_on_received_message = shouldWakeOnReceivedMessage(); + if (wake_on_received_message) { + setOn(true); // Wake up the screen first + forceDisplay(); // Forces screen redraw - const char *msgRaw = reinterpret_cast(packet->decoded.payload.bytes); + // === Prepare banner content === + const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from); + const char *longName = (node && node->has_user) ? node->user.long_name : nullptr; - char banner[256]; + const char *msgRaw = reinterpret_cast(packet->decoded.payload.bytes); - // Check for bell character in message to determine alert type - bool isAlert = false; - for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) { - if (msgRaw[i] == '\x07') { - isAlert = true; - break; + char banner[256]; + + // Check for bell character in message to determine alert type + bool isAlert = false; + for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) { + if (msgRaw[i] == '\x07') { + isAlert = true; + break; + } } - } - if (isAlert) { - if (longName && longName[0]) { - snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName); + if (isAlert) { + if (longName && longName[0]) { + snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName); + } else { + strcpy(banner, "Alert Received"); + } } else { - strcpy(banner, "Alert Received"); + if (longName && longName[0]) { + snprintf(banner, sizeof(banner), "New Message from\n%s", longName); + } else { + strcpy(banner, "New Message"); + } } - } else { - if (longName && longName[0]) { - snprintf(banner, sizeof(banner), "New Message from\n%s", longName); - } else { - strcpy(banner, "New Message"); - } - } - screen->showSimpleBanner(banner, 3000); + screen->showSimpleBanner(banner, 3000); + } } } diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 4deeb7395..19d14ecca 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -26,6 +26,8 @@ struct BannerOverlayOptions { }; } // namespace graphics +bool shouldWakeOnReceivedMessage(); + #if !HAS_SCREEN #include "power.h" namespace graphics @@ -123,6 +125,8 @@ class Screen #define SEGMENT_WIDTH 16 #define SEGMENT_HEIGHT 4 +extern bool wake_on_received_message; + /// Convert an integer GPS coords to a floating point #define DegD(i) (i * 1e-7) extern bool hasUnreadMessage; diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index c3a035c4f..f6b250ebc 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -129,11 +129,11 @@ void menuHandler::ClockFacePicker() screen->runNow(); } else if (selected == Digital) { uiconfig.is_clockface_analog = false; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); screen->setFrames(Screen::FOCUS_CLOCK); } else { uiconfig.is_clockface_analog = true; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); screen->setFrames(Screen::FOCUS_CLOCK); } }; @@ -346,37 +346,28 @@ void menuHandler::homeBaseMenu() void menuHandler::systemBaseMenu() { - // Check if brightness is supported bool hasSupportBrightness = false; #if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT hasSupportBrightness = true; #endif - enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test, enumEnd }; + enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd }; static const char *optionsArray[enumEnd] = {"Back"}; static int optionsEnumArray[enumEnd] = {Back}; int options = 1; - optionsArray[options] = "Reboot"; - optionsEnumArray[options++] = Reboot; - - optionsArray[options] = "Beeps Action"; - optionsEnumArray[options++] = Beeps; - - if (hasSupportBrightness) { - optionsArray[options] = "Brightness"; - optionsEnumArray[options++] = Brightness; - } - -#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT - optionsArray[options] = "Screen Color"; - optionsEnumArray[options++] = Color; -#endif -#if HAS_TFT - optionsArray[options] = "Switch to MUI"; - optionsEnumArray[options++] = MUI; + optionsArray[options] = "Notifications"; + optionsEnumArray[options++] = Notifications; +#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \ + defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT + optionsArray[options] = "Screen Options"; + optionsEnumArray[options++] = ScreenOptions; #endif + + optionsArray[options] = "Reboot/Shutdown"; + optionsEnumArray[options++] = PowerMenu; + if (test_enabled) { optionsArray[options] = "Test Menu"; optionsEnumArray[options++] = Test; @@ -388,20 +379,14 @@ void menuHandler::systemBaseMenu() bannerOptions.optionsCount = options; bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == Beeps) { - menuHandler::menuQueue = menuHandler::buzzermodemenupicker; + if (selected == Notifications) { + menuHandler::menuQueue = menuHandler::notifications_menu; screen->runNow(); - } else if (selected == Brightness) { - menuHandler::menuQueue = menuHandler::brightness_picker; + } else if (selected == ScreenOptions) { + menuHandler::menuQueue = menuHandler::screen_options_menu; screen->runNow(); - } else if (selected == Reboot) { - menuHandler::menuQueue = menuHandler::reboot_menu; - screen->runNow(); - } else if (selected == MUI) { - menuHandler::menuQueue = menuHandler::mui_picker; - screen->runNow(); - } else if (selected == Color) { - menuHandler::menuQueue = menuHandler::tftcolormenupicker; + } else if (selected == PowerMenu) { + menuHandler::menuQueue = menuHandler::power_menu; screen->runNow(); } else if (selected == Test) { menuHandler::menuQueue = menuHandler::test_menu; @@ -533,22 +518,19 @@ void menuHandler::compassNorthMenu() if (selected == Dynamic) { if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) { uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, - &uiconfig); + saveUIConfig(); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } } else if (selected == Fixed) { if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) { uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, - &uiconfig); + saveUIConfig(); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } } else if (selected == Freeze) { if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) { uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, - &uiconfig); + saveUIConfig(); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } } else if (selected == Back) { @@ -610,7 +592,7 @@ void menuHandler::BuzzerModeMenu() { static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"}; BannerOverlayOptions bannerOptions; - bannerOptions.message = "Beep Action"; + bannerOptions.message = "Buzzer Mode"; bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 4; bannerOptions.bannerCallback = [](int selected) -> void { @@ -660,7 +642,7 @@ void menuHandler::BrightnessPickerMenu() #endif // Save to device - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); LOG_INFO("Screen brightness set to %d", uiconfig.screen_brightness); } @@ -671,13 +653,13 @@ void menuHandler::BrightnessPickerMenu() void menuHandler::switchToMUIMenu() { - static const char *optionsArray[] = {"Yes", "No"}; + static const char *optionsArray[] = {"No", "Yes"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Switch to MUI?"; bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 2; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 0) { + if (selected == 1) { config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; config.bluetooth.enabled = false; service->reloadConfig(SEGMENT_CONFIG); @@ -742,6 +724,9 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) TFT_MESH_r = 255; TFT_MESH_g = 255; TFT_MESH_b = 255; + } else { + menuQueue = system_base_menu; + screen->runNow(); } #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT @@ -771,7 +756,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b; } LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color); - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); } #endif }; @@ -790,6 +775,29 @@ void menuHandler::rebootMenu() IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0)); nodeDB->saveToDisk(); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; + } else { + menuQueue = power_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::shutdownMenu() +{ + static const char *optionsArray[] = {"Back", "Confirm"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Shutdown Device?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == 1) { + IF_SCREEN(screen->showSimpleBanner("Shutting Down...", 0)); + nodeDB->saveToDisk(); + power->shutdown(); + } else { + menuQueue = power_menu; + screen->runNow(); } }; screen->showOverlayBanner(bannerOptions); @@ -888,6 +896,117 @@ void menuHandler::wifiToggleMenu() screen->showOverlayBanner(bannerOptions); } +void menuHandler::notificationsMenu() +{ + enum optionsNumbers { Back, BuzzerActions }; + static const char *optionsArray[] = {"Back", "Buzzer Actions"}; + static int optionsEnumArray[] = {Back, BuzzerActions}; + int options = 2; + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Notifications"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == BuzzerActions) { + menuHandler::menuQueue = menuHandler::buzzermodemenupicker; + screen->runNow(); + } else { + menuQueue = system_base_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::screenOptionsMenu() +{ + // Check if brightness is supported + bool hasSupportBrightness = false; +#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT + hasSupportBrightness = true; +#endif + + enum optionsNumbers { Back, Brightness, ScreenColor }; + static const char *optionsArray[4] = {"Back"}; + static int optionsEnumArray[4] = {Back}; + int options = 1; + + // Only show brightness for B&W displays + if (hasSupportBrightness && !HAS_TFT) { + optionsArray[options] = "Brightness"; + optionsEnumArray[options++] = Brightness; + } + + // Only show screen color for TFT displays +#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT + optionsArray[options] = "Screen Color"; + optionsEnumArray[options++] = ScreenColor; +#endif + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Screen Options"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Brightness) { + menuHandler::menuQueue = menuHandler::brightness_picker; + screen->runNow(); + } else if (selected == ScreenColor) { + menuHandler::menuQueue = menuHandler::tftcolormenupicker; + screen->runNow(); + } else { + menuQueue = system_base_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::powerMenu() +{ + + enum optionsNumbers { Back, Reboot, Shutdown, MUI }; + static const char *optionsArray[4] = {"Back"}; + static int optionsEnumArray[4] = {Back}; + int options = 1; + + optionsArray[options] = "Reboot"; + optionsEnumArray[options++] = Reboot; + + optionsArray[options] = "Shutdown"; + optionsEnumArray[options++] = Shutdown; + +#if HAS_TFT + optionsArray[options] = "Switch to MUI"; + optionsEnumArray[options++] = MUI; +#endif + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Reboot / Shutdown"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Reboot) { + menuHandler::menuQueue = menuHandler::reboot_menu; + screen->runNow(); + } else if (selected == Shutdown) { + menuHandler::menuQueue = menuHandler::shutdown_menu; + screen->runNow(); + } else if (selected == MUI) { + menuHandler::menuQueue = menuHandler::mui_picker; + screen->runNow(); + } else { + menuQueue = system_base_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::keyVerificationInitMenu() { screen->showNodePicker("Node to Verify", 30000, @@ -941,6 +1060,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case clock_menu: clockMenu(); break; + case system_base_menu: + systemBaseMenu(); + break; case position_base_menu: positionBaseMenu(); break; @@ -970,6 +1092,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case reboot_menu: rebootMenu(); break; + case shutdown_menu: + shutdownMenu(); + break; case add_favorite: addFavoriteMenu(); break; @@ -994,6 +1119,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case bluetooth_toggle_menu: BluetoothToggleMenu(); break; + case notifications_menu: + notificationsMenu(); + break; + case screen_options_menu: + screenOptionsMenu(); + break; + case power_menu: + powerMenu(); + break; case throttle_message: screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000); break; @@ -1001,6 +1135,11 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) menuQueue = menu_none; } +void menuHandler::saveUIConfig() +{ + nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); +} + } // namespace graphics #endif \ No newline at end of file diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 5846a3c91..2273dbbed 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -23,14 +23,19 @@ class menuHandler tftcolormenupicker, brightness_picker, reboot_menu, + shutdown_menu, add_favorite, remove_favorite, test_menu, number_test, wifi_toggle_menu, + bluetooth_toggle_menu, + notifications_menu, + screen_options_menu, + power_menu, + system_base_menu, key_verification_init, key_verification_final_prompt, - bluetooth_toggle_menu, throttle_message }; static screenMenus menuQueue; @@ -55,12 +60,19 @@ class menuHandler static void resetNodeDBMenu(); static void BrightnessPickerMenu(); static void rebootMenu(); + static void shutdownMenu(); static void addFavoriteMenu(); static void removeFavoriteMenu(); static void testMenu(); static void numberTest(); static void wifiBaseMenu(); static void wifiToggleMenu(); + static void notificationsMenu(); + static void screenOptionsMenu(); + static void powerMenu(); + + private: + static void saveUIConfig(); static void keyVerificationInitMenu(); static void keyVerificationFinalPrompt(); static void BluetoothToggleMenu(); diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp index f1d01ad16..f0835073b 100644 --- a/src/modules/TextMessageModule.cpp +++ b/src/modules/TextMessageModule.cpp @@ -4,6 +4,7 @@ #include "PowerFSM.h" #include "buzz.h" #include "configuration.h" +#include "graphics/Screen.h" TextMessageModule *textMessageModule; ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp) @@ -17,7 +18,11 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp devicestate.rx_text_message = mp; devicestate.has_rx_text_message = true; - powerFSM.trigger(EVENT_RECEIVED_MSG); + wake_on_received_message = shouldWakeOnReceivedMessage(); + // Only trigger screen wake if configuration allows it + if (wake_on_received_message) { + powerFSM.trigger(EVENT_RECEIVED_MSG); + } notifyObservers(&mp); return ProcessMessage::CONTINUE; // Let others look at this message also if they want