From ea32ba0f8dd7d321f154eba30e4585e28778c167 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Thu, 12 Feb 2026 13:12:13 +0100 Subject: [PATCH] add HalPowerManager --- lib/hal/HalGPIO.cpp | 19 --------------- lib/hal/HalGPIO.h | 6 ----- lib/hal/HalPowerManager.cpp | 47 ++++++++++++++++++++++++++++++++++++ lib/hal/HalPowerManager.h | 26 ++++++++++++++++++++ src/main.cpp | 48 +++++++------------------------------ 5 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 lib/hal/HalPowerManager.cpp create mode 100644 lib/hal/HalPowerManager.h diff --git a/lib/hal/HalGPIO.cpp b/lib/hal/HalGPIO.cpp index 89ce13ba..64a251de 100644 --- a/lib/hal/HalGPIO.cpp +++ b/lib/hal/HalGPIO.cpp @@ -1,11 +1,9 @@ #include #include -#include void HalGPIO::begin() { inputMgr.begin(); SPI.begin(EPD_SCLK, SPI_MISO, EPD_MOSI, EPD_CS); - pinMode(BAT_GPIO0, INPUT); pinMode(UART0_RXD, INPUT); } @@ -23,23 +21,6 @@ bool HalGPIO::wasAnyReleased() const { return inputMgr.wasAnyReleased(); } unsigned long HalGPIO::getHeldTime() const { return inputMgr.getHeldTime(); } -void HalGPIO::startDeepSleep() { - // Ensure that the power button has been released to avoid immediately turning back on if you're holding it - while (inputMgr.isPressed(BTN_POWER)) { - delay(50); - inputMgr.update(); - } - // Arm the wakeup trigger *after* the button is released - esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); - // Enter Deep Sleep - esp_deep_sleep_start(); -} - -int HalGPIO::getBatteryPercentage() const { - static const BatteryMonitor battery = BatteryMonitor(BAT_GPIO0); - return battery.readPercentage(); -} - bool HalGPIO::isUsbConnected() const { // U0RXD/GPIO20 reads HIGH when USB is connected return digitalRead(UART0_RXD) == HIGH; diff --git a/lib/hal/HalGPIO.h b/lib/hal/HalGPIO.h index 615a8d63..45ca50a5 100644 --- a/lib/hal/HalGPIO.h +++ b/lib/hal/HalGPIO.h @@ -38,12 +38,6 @@ class HalGPIO { bool wasAnyReleased() const; unsigned long getHeldTime() const; - // Setup wake up GPIO and enter deep sleep - void startDeepSleep(); - - // Get battery percentage (range 0-100) - int getBatteryPercentage() const; - // Check if USB is connected bool isUsbConnected() const; diff --git a/lib/hal/HalPowerManager.cpp b/lib/hal/HalPowerManager.cpp new file mode 100644 index 00000000..5c945f59 --- /dev/null +++ b/lib/hal/HalPowerManager.cpp @@ -0,0 +1,47 @@ +#include + +#include "HalPowerManager.h" +#include "HalGPIO.h" + +void HalPowerManager::begin() { + pinMode(BAT_GPIO0, INPUT); + normalFreq = getCpuFrequencyMhz(); +} + +void HalPowerManager::setPowerSaving(bool enabled) { + if (normalFreq <= 0) { + return; // invalid state + } + if (enabled && !isLowPower) { + Serial.printf("[%lu] [PWR] Going to low-power mode\n", millis()); + if (!setCpuFrequencyMhz(LOW_POWER_FREQ)) { + Serial.printf("[%lu] [PWR] Failed to set low-power CPU frequency\n", millis()); + return; + } + } + if (!enabled && isLowPower) { + Serial.printf("[%lu] [PWR] Restoring normal CPU frequency\n", millis()); + if (!setCpuFrequencyMhz(normalFreq)) { + Serial.printf("[%lu] [PWR] Failed to restore normal CPU frequency\n", millis()); + return; + } + } + isLowPower = enabled; +} + +void HalPowerManager::startDeepSleep(HalGPIO &gpio) const { + // Ensure that the power button has been released to avoid immediately turning back on if you're holding it + while (gpio.isPressed(HalGPIO::BTN_POWER)) { + delay(50); + gpio.update(); + } + // Arm the wakeup trigger *after* the button is released + esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); + // Enter Deep Sleep + esp_deep_sleep_start(); +} + +int HalPowerManager::getBatteryPercentage() const { + static const BatteryMonitor battery = BatteryMonitor(BAT_GPIO0); + return battery.readPercentage(); +} diff --git a/lib/hal/HalPowerManager.h b/lib/hal/HalPowerManager.h new file mode 100644 index 00000000..792bb449 --- /dev/null +++ b/lib/hal/HalPowerManager.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "HalGPIO.h" + +class HalPowerManager { + static constexpr int LOW_POWER_FREQ = 10; // MHz + + int normalFreq = 0; // MHz + bool isLowPower = false; + + public: + void begin(); + + // Control CPU frequency for power saving + void setPowerSaving(bool enabled); + + // Setup wake up GPIO and enter deep sleep + void startDeepSleep(HalGPIO &gpio) const; + + // Get battery percentage (range 0-100) + int getBatteryPercentage() const; +}; diff --git a/src/main.cpp b/src/main.cpp index 8098c925..85e0d1b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ HalDisplay display; HalGPIO gpio; +HalPowerManager powerManager; MappedInputManager mappedInputManager(gpio); GfxRenderer renderer(display); Activity* currentActivity; @@ -181,7 +183,7 @@ void verifyPowerButtonDuration() { if (abort) { // Button released too early. Returning to sleep. // IMPORTANT: Re-arm the wakeup trigger before sleeping again - gpio.startDeepSleep(); + powerManager.startDeepSleep(gpio); } } @@ -204,7 +206,7 @@ void enterDeepSleep() { Serial.printf("[%lu] [ ] Power button press calibration value: %lu ms\n", millis(), t2 - t1); Serial.printf("[%lu] [ ] Entering deep sleep.\n", millis()); - gpio.startDeepSleep(); + powerManager.startDeepSleep(gpio); } void onGoHome(); @@ -277,41 +279,11 @@ void setupDisplayAndFonts() { Serial.printf("[%lu] [ ] Fonts setup\n", millis()); } -// FOR TESTING ONLY -static bool isLowerFreq = false; -static int normalFreq = 160; // MHz -class HalPowerManager { - public: - static void setCpuFrequency(bool lower) { - bool changed = false; - if (lower && !isLowerFreq) { - bool success = setCpuFrequencyMhz(10); - if (!success) { - Serial.printf("[%lu] [PWR] Failed to set CPU frequency to 10 MHz\n", millis()); - return; - } - isLowerFreq = true; - changed = true; - } else if (!lower && isLowerFreq) { - bool success = setCpuFrequencyMhz(normalFreq); - if (!success) { - Serial.printf("[%lu] [PWR] Failed to set CPU frequency to %d MHz\n", millis(), normalFreq); - return; - } - isLowerFreq = false; - changed = true; - } - - if (changed) { - Serial.printf("[%lu] [PWR] CPU frequency set to %u MHz\n", millis(), getCpuFrequencyMhz()); - } - } -}; - void setup() { t1 = millis(); gpio.begin(); + powerManager.begin(); // Only start serial if USB connected if (gpio.isUsbConnected()) { @@ -333,8 +305,6 @@ void setup() { return; } - normalFreq = getCpuFrequencyMhz(); - SETTINGS.loadFromFile(); KOREADER_STORE.loadFromFile(); UITheme::getInstance().reload(); @@ -349,7 +319,7 @@ void setup() { case HalGPIO::WakeupReason::AfterUSBPower: // If USB power caused a cold boot, go back to sleep Serial.printf("[%lu] [ ] Wakeup reason: After USB Power\n", millis()); - gpio.startDeepSleep(); + powerManager.startDeepSleep(gpio); break; case HalGPIO::WakeupReason::AfterFlash: // After flashing, just proceed to boot @@ -405,8 +375,8 @@ void loop() { // Check for any user activity (button press or release) or active background work static unsigned long lastActivityTime = millis(); if (gpio.wasAnyPressed() || gpio.wasAnyReleased() || (currentActivity && currentActivity->preventAutoSleep())) { - lastActivityTime = millis(); // Reset inactivity timer - HalPowerManager::setCpuFrequency(false); // Set normal CPU frequency on user activity + lastActivityTime = millis(); // Reset inactivity timer + powerManager.setPowerSaving(false); // Restore normal CPU frequency on user activity } const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs(); @@ -447,7 +417,7 @@ void loop() { static constexpr unsigned long IDLE_POWER_SAVING_MS = 3000; // 3 seconds if (millis() - lastActivityTime >= IDLE_POWER_SAVING_MS) { // If we've been inactive for a while, increase the delay to save power - HalPowerManager::setCpuFrequency(true); // Lower CPU frequency after extended inactivity + powerManager.setPowerSaving(true); // Lower CPU frequency after extended inactivity delay(50); } else { // Short delay to prevent tight loop while still being responsive