From f701b58b4bc5cac5cea5053093e8f757b5a9dfc7 Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 2 Jun 2026 00:53:00 +1000 Subject: [PATCH 1/4] NRF52Board: ungate isExternalPowered() --- src/helpers/NRF52Board.cpp | 28 ++++++++++++++-------------- src/helpers/NRF52Board.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 2c8753d464..17265f0455 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -66,20 +66,6 @@ void NRF52Board::initPowerMgr() { } } -bool NRF52Board::isExternalPowered() { - // Check if SoftDevice is enabled before using its API - uint8_t sd_enabled = 0; - sd_softdevice_is_enabled(&sd_enabled); - - if (sd_enabled) { - uint32_t usb_status; - sd_power_usbregstatus_get(&usb_status); - return (usb_status & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; - } else { - return (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; - } -} - const char* NRF52Board::getResetReasonString(uint32_t reason) { if (reason & POWER_RESETREAS_RESETPIN_Msk) return "Reset Pin"; if (reason & POWER_RESETREAS_DOG_Msk) return "Watchdog"; @@ -251,6 +237,20 @@ void NRF52BoardDCDC::begin() { } } +bool NRF52Board::isExternalPowered() { + // Check if SoftDevice is enabled before using its API + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + + if (sd_enabled) { + uint32_t usb_status; + sd_power_usbregstatus_get(&usb_status); + return (usb_status & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; + } else { + return (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; + } +} + void NRF52Board::sleep(uint32_t secs) { // Clear FPU interrupt flags to avoid insomnia // see errata 87 for details https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_87.html diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index c9f1e071b8..17065cf443 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -53,9 +53,9 @@ class NRF52Board : public mesh::MainBoard { virtual bool getBootloaderVersion(char* version, size_t max_len) override; virtual bool startOTAUpdate(const char *id, char reply[]) override; virtual void sleep(uint32_t secs) override; + bool isExternalPowered() override; #ifdef NRF52_POWER_MANAGEMENT - bool isExternalPowered() override; uint16_t getBootVoltage() override { return boot_voltage_mv; } virtual uint32_t getResetReason() const override { return reset_reason; } uint8_t getShutdownReason() const override { return shutdown_reason; } From 3ce1cf404ed66bbdfdd8687605b46cd429f7c0bd Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 2 Jun 2026 00:55:25 +1000 Subject: [PATCH 2/4] NRF52: disable autoshutdown if powered, add auto-shutdown warning for oled --- examples/companion_radio/ui-new/UITask.cpp | 28 ++++++++++----------- examples/companion_radio/ui-tiny/UITask.cpp | 17 ++++++++++--- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index ee12ca740d..0cebc10001 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -832,22 +832,20 @@ void UITask::loop() { if (millis() > next_batt_chck) { uint16_t milliVolts = getBattMilliVolts(); if (milliVolts > 0 && milliVolts < AUTO_SHUTDOWN_MILLIVOLTS) { - - // show low battery shutdown alert - // we should only do this for eink displays, which will persist after power loss - #if defined(THINKNODE_M1) || defined(LILYGO_TECHO) - if (_display != NULL) { - _display->startFrame(); - _display->setTextSize(2); - _display->setColor(DisplayDriver::RED); - _display->drawTextCentered(_display->width() / 2, 20, "Low Battery."); - _display->drawTextCentered(_display->width() / 2, 40, "Shutting Down!"); - _display->endFrame(); + if(!board.isExternalPowered()) { + if (_display != NULL) { + _display->startFrame(); + _display->setTextSize(2); + _display->setColor(DisplayDriver::RED); + _display->drawTextCentered(_display->width() / 2, 20, "Low Battery."); + _display->drawTextCentered(_display->width() / 2, 40, "Shutting Down!"); + _display->endFrame(); + #if !defined(THINKNODE_M1) && !defined(LILYGO_TECHO) // TODO: refactor eink variants to use EINK_DISPLAY macros to gate this properly + delay(3000); + #endif + } + shutdown(); } - #endif - - shutdown(); - } next_batt_chck = millis() + 8000; } diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 45a07a02ef..125c4f7bab 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -727,14 +727,23 @@ void UITask::loop() { if (millis() > next_batt_chck) { _cached_batt_mv = getBattMilliVolts(); if (_cached_batt_mv > 0 && _cached_batt_mv < AUTO_SHUTDOWN_MILLIVOLTS) { - - shutdown(); - + if(!board.isExternalPowered()) { + if (_display != NULL) { + _display->startFrame(); + _display->setTextSize(2); + _display->drawTextCentered(_display->width() / 2, 6, "Low battery!"); + _display->setTextSize(1); + _display->drawTextCentered(_display->width() / 2, 18, "Shutting down!"); + _display->endFrame(); + delay(3000); // TODO: refactor eink variants to use EINK_DISPLAY macros to gate this properly + } + shutdown(); + } } next_batt_chck = millis() + 8000; } #else - if (_display != NULL && _display->isOn() && millis >= next_batt_chck) { + if (_display != NULL && _display->isOn() && millis() >= next_batt_chck) { _cached_batt_mv = getBattMilliVolts(); next_batt_chck = millis() + 8000; } From 5e3edd0bbc0c8d1b49a544eb858f8d061d83fe12 Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 2 Jun 2026 18:36:07 +1000 Subject: [PATCH 3/4] add bool isEink() for eink display drivers --- src/helpers/ui/DisplayDriver.h | 1 + src/helpers/ui/E213Display.h | 1 + src/helpers/ui/E290Display.h | 1 + src/helpers/ui/GxEPDDisplay.h | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index ec63c19123..dcc5fe0318 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -14,6 +14,7 @@ class DisplayDriver { int height() const { return _h; } virtual bool isOn() = 0; + virtual bool isEink() { return false; } // default to non-eink, override in eink drivers virtual void turnOn() = 0; virtual void turnOff() = 0; virtual void clear() = 0; diff --git a/src/helpers/ui/E213Display.h b/src/helpers/ui/E213Display.h index 420792c8a3..add8f11b35 100644 --- a/src/helpers/ui/E213Display.h +++ b/src/helpers/ui/E213Display.h @@ -26,6 +26,7 @@ class E213Display : public DisplayDriver { } bool begin(); bool isOn() override { return _isOn; } + bool isEink() override { return true; } void turnOn() override; void turnOff() override; void clear() override; diff --git a/src/helpers/ui/E290Display.h b/src/helpers/ui/E290Display.h index 2ca50225d1..88bf34ff17 100644 --- a/src/helpers/ui/E290Display.h +++ b/src/helpers/ui/E290Display.h @@ -22,6 +22,7 @@ class E290Display : public DisplayDriver { bool begin(); bool isOn() override { return _isOn; } + bool isEink() override { return true; } void turnOn() override; void turnOff() override; void clear() override; diff --git a/src/helpers/ui/GxEPDDisplay.h b/src/helpers/ui/GxEPDDisplay.h index 1a04cc2464..219b607644 100644 --- a/src/helpers/ui/GxEPDDisplay.h +++ b/src/helpers/ui/GxEPDDisplay.h @@ -46,7 +46,8 @@ class GxEPDDisplay : public DisplayDriver { bool begin(); - bool isOn() override {return _isOn;}; + bool isOn() override { return _isOn; } + bool isEink() override { return true; } void turnOn() override; void turnOff() override; void clear() override; From e449af1723c2daf502223f4f13d4b4ba8fb23f6c Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 2 Jun 2026 18:37:18 +1000 Subject: [PATCH 4/4] bypass auto-shutdown delay for e-ink devices --- examples/companion_radio/ui-new/UITask.cpp | 4 +--- examples/companion_radio/ui-tiny/UITask.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 0cebc10001..7821af3e14 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -840,9 +840,7 @@ void UITask::loop() { _display->drawTextCentered(_display->width() / 2, 20, "Low Battery."); _display->drawTextCentered(_display->width() / 2, 40, "Shutting Down!"); _display->endFrame(); - #if !defined(THINKNODE_M1) && !defined(LILYGO_TECHO) // TODO: refactor eink variants to use EINK_DISPLAY macros to gate this properly - delay(3000); - #endif + if (_display->isEink() == false) { delay(3000); } } shutdown(); } diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 125c4f7bab..bfaddf1d12 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -735,7 +735,7 @@ void UITask::loop() { _display->setTextSize(1); _display->drawTextCentered(_display->width() / 2, 18, "Shutting down!"); _display->endFrame(); - delay(3000); // TODO: refactor eink variants to use EINK_DISPLAY macros to gate this properly + if (_display->isEink() == false) { delay(3000); } } shutdown(); }