merge upstream PR #852: feat: lower CPU freq on idle, add HalPowerManager

This commit is contained in:
cottongin
2026-02-12 12:09:20 -05:00
5 changed files with 85 additions and 31 deletions

View File

@@ -1,11 +1,9 @@
#include <HalGPIO.h>
#include <SPI.h>
#include <esp_sleep.h>
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;

View File

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

View File

@@ -0,0 +1,48 @@
#include "HalPowerManager.h"
#include <esp_sleep.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();
}

27
lib/hal/HalPowerManager.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <Arduino.h>
#include <BatteryMonitor.h>
#include <InputManager.h>
#include "HalGPIO.h"
class HalPowerManager {
int normalFreq = 0; // MHz
bool isLowPower = false;
public:
static constexpr int LOW_POWER_FREQ = 10; // MHz
static constexpr unsigned long IDLE_POWER_SAVING_MS = 3000; // ms
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;
};

View File

@@ -3,6 +3,7 @@
#include <GfxRenderer.h>
#include <HalDisplay.h>
#include <HalGPIO.h>
#include <HalPowerManager.h>
#include <HalStorage.h>
#include <SPI.h>
#include <builtinFonts/all.h>
@@ -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();
@@ -281,6 +283,7 @@ void setup() {
t1 = millis();
gpio.begin();
powerManager.begin();
// Only start serial if USB connected
if (gpio.isUsbConnected()) {
@@ -316,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
@@ -372,7 +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
lastActivityTime = millis(); // Reset inactivity timer
powerManager.setPowerSaving(false); // Restore normal CPU frequency on user activity
}
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
@@ -410,9 +414,9 @@ void loop() {
if (currentActivity && currentActivity->skipLoopDelay()) {
yield(); // Give FreeRTOS a chance to run tasks, but return immediately
} else {
static constexpr unsigned long IDLE_POWER_SAVING_MS = 3000; // 3 seconds
if (millis() - lastActivityTime >= IDLE_POWER_SAVING_MS) {
if (millis() - lastActivityTime >= HalPowerManager::IDLE_POWER_SAVING_MS) {
// If we've been inactive for a while, increase the delay to save power
powerManager.setPowerSaving(true); // Lower CPU frequency after extended inactivity
delay(50);
} else {
// Short delay to prevent tight loop while still being responsive