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 <m.tools@qq.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Jason P 2025-07-10 19:49:15 -05:00 committed by GitHub
parent 093868f3ed
commit be75f11156
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 261 additions and 72 deletions

View File

@ -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<const char *>(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<const char *>(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);
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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