Only init screen if one found

This commit is contained in:
Jonathan Bennett 2025-06-07 14:07:48 -05:00
parent 3955a5616e
commit 75e41c1604
2 changed files with 273 additions and 259 deletions

View File

@ -256,292 +256,294 @@ int32_t ButtonThread::runOnce()
#endif #endif
if (btnEvent != BUTTON_EVENT_NONE) { if (btnEvent != BUTTON_EVENT_NONE) {
if (screen) {
#if HAS_SCREEN #if HAS_SCREEN
switch (btnEvent) { switch (btnEvent) {
case BUTTON_EVENT_PRESSED: { case BUTTON_EVENT_PRESSED: {
LOG_WARN("press!"); LOG_WARN("press!");
// Play boop sound for every button press // Play boop sound for every button press
playBoop(); playBoop();
// Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event) // Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event)
if (inputBroker) { if (inputBroker) {
InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_PRESSED, 0, 0, 0}; InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt); inputBroker->injectInputEvent(&evt);
}
// Start tracking for potential combination
waitingForLongPress = true;
shortPressTime = millis();
break;
}
case BUTTON_EVENT_DOUBLE_PRESSED: {
LOG_WARN("press!");
// Play boop sound for every button press
playBoop();
// Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event)
if (inputBroker) {
InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_DOUBLE_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
waitingForLongPress = false;
break;
}
case BUTTON_EVENT_LONG_PRESSED: {
LOG_WARN("Long press!");
// 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", (char)INPUT_BROKER_MSG_BUTTON_LONG_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
waitingForLongPress = false;
break;
}
default:
// Ignore all other events on screen devices
break;
}
btnEvent = BUTTON_EVENT_NONE;
#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;
}
#ifdef ELECROW_ThinkNode_M1
sendAdHocPosition();
break;
#endif
// Start tracking for potential combination
waitingForLongPress = true;
shortPressTime = millis();
switchPage();
break;
}
case BUTTON_EVENT_PRESSED_SCREEN: {
LOG_BUTTON("AltPress!");
// Play boop sound for every button press
playBoop();
// 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;
}
switchPage();
break;
#endif
// turn screen on or off
screen_flag = !screen_flag;
if (screen)
screen->setOn(screen_flag);
break;
}
case BUTTON_EVENT_DOUBLE_PRESSED: {
LOG_BUTTON("Double press!");
// Play boop sound for every button press
playBoop();
// 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();
// Show temporary on-screen confirmation banner for 3 seconds
screen->showOverlayBanner("Ad-hoc Ping Sent", 3000);
break;
}
case BUTTON_EVENT_MULTI_PRESSED: {
LOG_BUTTON("Mulitipress! %hux", multipressClickCount);
// Play boop sound for every button press
playBoop();
// Reset combination tracking
waitingForLongPress = false;
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();
const char *statusMsg = (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED)
? "GPS Enabled"
: "GPS Disabled";
if (screen) {
screen->forceDisplay(true); // Force a new UI frame, then force an EInk update
screen->showOverlayBanner(statusMsg, 3000);
}
} }
// Start tracking for potential combination
waitingForLongPress = true;
shortPressTime = millis();
break; break;
}
case BUTTON_EVENT_DOUBLE_PRESSED: {
LOG_WARN("press!");
// Play boop sound for every button press
playBoop();
// Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event)
if (inputBroker) {
InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_DOUBLE_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
waitingForLongPress = false;
break;
}
case BUTTON_EVENT_LONG_PRESSED: {
LOG_WARN("Long press!");
// 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", (char)INPUT_BROKER_MSG_BUTTON_LONG_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
waitingForLongPress = false;
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;
}
#ifdef ELECROW_ThinkNode_M1
sendAdHocPosition();
break;
#endif
// Start tracking for potential combination
waitingForLongPress = true;
shortPressTime = millis();
switchPage();
break;
}
case BUTTON_EVENT_PRESSED_SCREEN: {
LOG_BUTTON("AltPress!");
// Play boop sound for every button press
playBoop();
// 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;
}
switchPage();
break;
#endif
// turn screen on or off
screen_flag = !screen_flag;
if (screen)
screen->setOn(screen_flag);
break;
}
case BUTTON_EVENT_DOUBLE_PRESSED: {
LOG_BUTTON("Double press!");
// Play boop sound for every button press
playBoop();
// 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();
// Show temporary on-screen confirmation banner for 3 seconds
screen->showOverlayBanner("Ad-hoc Ping Sent", 3000);
break;
}
case BUTTON_EVENT_MULTI_PRESSED: {
LOG_BUTTON("Mulitipress! %hux", multipressClickCount);
// Play boop sound for every button press
playBoop();
// Reset combination tracking
waitingForLongPress = false;
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();
const char *statusMsg = (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED)
? "GPS Enabled"
: "GPS Disabled";
if (screen) {
screen->forceDisplay(true); // Force a new UI frame, then force an EInk update
screen->showOverlayBanner(statusMsg, 3000);
}
}
break;
#elif defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2) #elif defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2)
case 3: case 3:
LOG_INFO("3 clicks: toggle buzzer"); LOG_INFO("3 clicks: toggle buzzer");
buzzer_flag = !buzzer_flag; buzzer_flag = !buzzer_flag;
if (!buzzer_flag) if (!buzzer_flag)
noTone(PIN_BUZZER); noTone(PIN_BUZZER);
break; break;
#endif #endif
#if defined(USE_EINK) && defined(PIN_EINK_EN) && !defined(ELECROW_ThinkNode_M1) // i.e. T-Echo #if defined(USE_EINK) && defined(PIN_EINK_EN) && !defined(ELECROW_ThinkNode_M1) // i.e. T-Echo
// 4 clicks: toggle backlight // 4 clicks: toggle backlight
case 4: case 4:
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
break; break;
#endif #endif
#if !MESHTASTIC_EXCLUDE_SCREEN && HAS_SCREEN #if !MESHTASTIC_EXCLUDE_SCREEN && HAS_SCREEN
// 5 clicks: start accelerometer/magenetometer calibration for 30 seconds // 5 clicks: start accelerometer/magenetometer calibration for 30 seconds
case 5: case 5:
if (accelerometerThread) { if (accelerometerThread) {
accelerometerThread->calibrate(30); accelerometerThread->calibrate(30);
} }
break; break;
// 6 clicks: start accelerometer/magenetometer calibration for 60 seconds // 6 clicks: start accelerometer/magenetometer calibration for 60 seconds
case 6: case 6:
if (accelerometerThread) { if (accelerometerThread) {
accelerometerThread->calibrate(60); accelerometerThread->calibrate(60);
} }
break; break;
#endif #endif
// No valid multipress action // No valid multipress action
default: default:
break;
} // end switch: click count
break; break;
} // end switch: click count } // end multipress event
break; case BUTTON_EVENT_LONG_PRESSED: {
} // end multipress event LOG_BUTTON("Long press!");
case BUTTON_EVENT_LONG_PRESSED: { // Check if this is part of a short-press + long-press combination
LOG_BUTTON("Long press!"); 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;
}
// Check if this is part of a short-press + long-press combination // Reset combination tracking
if (waitingForLongPress && (millis() - shortPressTime) <= BUTTON_COMBO_TIMEOUT_MS) {
LOG_BUTTON("Combo detected: short-press + long-press!");
btnEvent = BUTTON_EVENT_COMBO_SHORT_LONG;
waitingForLongPress = false; waitingForLongPress = false;
break;
}
// Reset combination tracking
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: {
LOG_INFO("Shutdown from long press");
// Reset combination tracking
waitingForLongPress = false;
playShutdownMelody();
delay(3000);
power->shutdown();
nodeDB->saveToDisk();
break;
}
#ifdef BUTTON_PIN_TOUCH
case BUTTON_EVENT_TOUCH_LONG_PRESSED: {
LOG_BUTTON("Touch press!");
// Play boop sound for every button press
playBoop();
// Reset combination tracking
waitingForLongPress = false;
// Ignore if: no screen
if (!screen)
break;
#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;
#endif
// Wake if asleep
if (powerFSM.getState() == &stateDARK)
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);
// Update display (legacy behaviour) if (screen) {
screen->forceDisplay(); // Show shutdown message as a temporary overlay banner
break; 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: {
LOG_INFO("Shutdown from long press");
// Reset combination tracking
waitingForLongPress = false;
playShutdownMelody();
delay(3000);
power->shutdown();
nodeDB->saveToDisk();
break;
}
#ifdef BUTTON_PIN_TOUCH
case BUTTON_EVENT_TOUCH_LONG_PRESSED: {
LOG_BUTTON("Touch press!");
// Play boop sound for every button press
playBoop();
// Reset combination tracking
waitingForLongPress = false;
// Ignore if: no screen
if (!screen)
break;
#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;
#endif
// Wake if asleep
if (powerFSM.getState() == &stateDARK)
powerFSM.trigger(EVENT_PRESS);
// Update display (legacy behaviour)
screen->forceDisplay();
break;
}
#endif // BUTTON_PIN_TOUCH #endif // BUTTON_PIN_TOUCH
case BUTTON_EVENT_COMBO_SHORT_LONG: { case BUTTON_EVENT_COMBO_SHORT_LONG: {
// Placeholder for short-press + long-press combination // Placeholder for short-press + long-press combination
LOG_BUTTON("Short-press + Long-press combination detected!"); LOG_BUTTON("Short-press + Long-press combination detected!");
// Play the combination tune // Play the combination tune
playComboTune(); playComboTune();
// Optionally show a message on screen // Optionally show a message on screen
if (screen) { if (screen) {
screen->showOverlayBanner("Combo Tune Played", 2000); screen->showOverlayBanner("Combo Tune Played", 2000);
}
break;
} }
break;
}
default: default:
break; break;
}
btnEvent = BUTTON_EVENT_NONE;
} }
btnEvent = BUTTON_EVENT_NONE;
#endif
} }
return 50; return 50;

View File

@ -850,9 +850,21 @@ void setup()
// Initialize the screen first so we can show the logo while we start up everything else. // Initialize the screen first so we can show the logo while we start up everything else.
#if HAS_SCREEN #if HAS_SCREEN
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry); screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
} #elif defined(ARCH_PORTDUINO)
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) &&
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
}
#else
if (screen_found.port != ScanI2C::I2CPort::NO_I2C)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
#endif #endif
}
#endif // HAS_SCREEN
// setup TZ prior to time actions. // setup TZ prior to time actions.
#if !MESHTASTIC_EXCLUDE_TZ #if !MESHTASTIC_EXCLUDE_TZ
LOG_DEBUG("Use compiled/slipstreamed %s", slipstreamTZString); // important, removing this clobbers our magic string LOG_DEBUG("Use compiled/slipstreamed %s", slipstreamTZString); // important, removing this clobbers our magic string