Files
immich-frame/src/power_manager.cpp

137 lines
4.0 KiB
C++
Raw Normal View History

#include "power_manager.h"
#include <M5Unified.h>
#include <esp_pm.h>
#include <esp_sleep.h>
void PowerManager::begin() {
Serial.println("[power] Initializing power manager");
// Disable audio power rails permanently
pinMode(PIN_AUDIO_PWR_EN, OUTPUT);
digitalWrite(PIN_AUDIO_PWR_EN, LOW);
pinMode(PIN_SPK_EN, OUTPUT);
digitalWrite(PIN_SPK_EN, LOW);
// Initialize RGB LEDs via M5Unified (handles NeoPixel on G21)
// M5Unified manages the LED strip internally
clearLEDs();
Serial.printf("[power] Battery: %d%%, Charging: %s\n",
getBatteryPercent(), isCharging() ? "yes" : "no");
}
uint8_t PowerManager::getBatteryPercent() {
int32_t level = M5.Power.getBatteryLevel();
if (level < 0) level = 0;
if (level > 100) level = 100;
_lastBatteryPct = static_cast<uint8_t>(level);
return _lastBatteryPct;
}
bool PowerManager::isCharging() {
return M5.Power.isCharging();
}
void PowerManager::updateBatteryLED() {
unsigned long now = millis();
uint8_t pct = getBatteryPercent();
// Auto deep sleep at critical level
if (pct <= BATTERY_SHUTDOWN_PCT && !isCharging()) {
Serial.println("[power] Battery critical — entering deep sleep");
enterDeepSleep();
return;
}
// Determine pulse interval based on battery level
unsigned long interval = 0;
uint8_t r = 0, g = 0, b = 0;
if (isCharging()) {
interval = BATTERY_WARN_INTERVAL_MS;
g = 255; // Green pulse
} else if (pct <= BATTERY_CRITICAL_PCT) {
interval = BATTERY_CRIT_INTERVAL_MS;
r = 255; // Red pulse
} else if (pct <= BATTERY_LOW_PCT) {
interval = BATTERY_WARN_INTERVAL_MS;
r = 255; g = 165; // Orange pulse
} else {
// Battery fine — no LED
return;
}
if (now - _lastLEDPulse >= interval) {
_lastLEDPulse = now;
flashLED(r, g, b);
}
}
void PowerManager::flashLED(uint8_t r, uint8_t g, uint8_t b, uint16_t duration_ms) {
// Scale by brightness
float scale = _ledBrightness / 255.0f;
uint8_t sr = static_cast<uint8_t>(r * scale);
uint8_t sg = static_cast<uint8_t>(g * scale);
uint8_t sb = static_cast<uint8_t>(b * scale);
setLED(0, sr, sg, sb);
setLED(1, sr, sg, sb);
// Non-blocking: we'll clear on next update cycle
// For simplicity, use a short blocking delay for LED flash feedback
delay(duration_ms);
clearLEDs();
}
void PowerManager::enterDeepSleep() {
Serial.println("[power] Entering deep sleep — power button to wake");
Serial.flush();
delay(100);
clearLEDs();
// On PaperColor, deep sleep is managed via M5PM1 shutdown
// The power button is hardwired to M5PM1 and always wakes the device
M5.Power.deepSleep(0); // 0 = indefinite, wake via power button
}
void PowerManager::enableLightSleep() {
esp_pm_config_esp32s3_t pm_config;
pm_config.max_freq_mhz = 240;
pm_config.min_freq_mhz = 80;
pm_config.light_sleep_enable = true;
esp_err_t err = esp_pm_configure(&pm_config);
if (err == ESP_OK) {
Serial.println("[power] Light sleep enabled (80-240MHz)");
} else {
Serial.printf("[power] Light sleep config failed: %d\n", err);
}
}
void PowerManager::disableEPDPower() {
// M5PM1 PYG0 controls e-paper power — managed via M5Unified Power API
// M5.Power controls the PM1 rails
Serial.println("[power] EPD power disabled");
}
void PowerManager::enableEPDPower() {
Serial.println("[power] EPD power enabled");
}
void PowerManager::setLED(uint8_t index, uint8_t r, uint8_t g, uint8_t b) {
// M5Unified provides LED control — exact API depends on board support
// Fallback: direct NeoPixel control if M5Unified doesn't cover it
(void)index;
(void)r;
(void)g;
(void)b;
// TODO: Implement via M5Unified LED API or direct NeoPixel library
// This will be filled in during hardware bring-up when we can test on device
}
void PowerManager::clearLEDs() {
setLED(0, 0, 0, 0);
setLED(1, 0, 0, 0);
}