Compare commits
4 Commits
530d43997b
...
d02e21a48f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d02e21a48f | ||
|
|
6ec5fc5603 | ||
|
|
9125a7ce68 | ||
|
|
dc6562a51c |
@@ -74,13 +74,14 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontFamily::Style style) const {
|
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontFamily::Style style) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
fontMap.at(fontId).getTextDimensions(text, &w, &h, style);
|
fontIt->second.getTextDimensions(text, &w, &h, style);
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,11 +101,12 @@ void GfxRenderer::drawText(const int fontId, const int x, const int y, const cha
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto font = fontMap.at(fontId);
|
const auto& font = fontIt->second;
|
||||||
|
|
||||||
// no printable characters
|
// no printable characters
|
||||||
if (!font.hasPrintableChars(text, style)) {
|
if (!font.hasPrintableChars(text, style)) {
|
||||||
@@ -709,52 +711,58 @@ int GfxRenderer::getScreenHeight() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getSpaceWidth(const int fontId) const {
|
int GfxRenderer::getSpaceWidth(const int fontId) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontMap.at(fontId).getGlyph(' ', EpdFontFamily::REGULAR)->advanceX;
|
return fontIt->second.getGlyph(' ', EpdFontFamily::REGULAR)->advanceX;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getTextAdvanceX(const int fontId, const char* text) const {
|
int GfxRenderer::getTextAdvanceX(const int fontId, const char* text) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t cp;
|
uint32_t cp;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
|
const auto& font = fontIt->second;
|
||||||
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
|
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
|
||||||
width += fontMap.at(fontId).getGlyph(cp, EpdFontFamily::REGULAR)->advanceX;
|
width += font.getGlyph(cp, EpdFontFamily::REGULAR)->advanceX;
|
||||||
}
|
}
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
return fontIt->second.getData(EpdFontFamily::REGULAR)->ascender;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getLineHeight(const int fontId) const {
|
int GfxRenderer::getLineHeight(const int fontId) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->advanceY;
|
return fontIt->second.getData(EpdFontFamily::REGULAR)->advanceY;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GfxRenderer::getTextHeight(const int fontId) const {
|
int GfxRenderer::getTextHeight(const int fontId) const {
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
return fontIt->second.getData(EpdFontFamily::REGULAR)->ascender;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y, const char* text, const bool black,
|
void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y, const char* text, const bool black,
|
||||||
@@ -764,11 +772,13 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontMap.count(fontId) == 0) {
|
const auto fontIt = fontMap.find(fontId);
|
||||||
|
if (fontIt == fontMap.end()) {
|
||||||
LOG_ERR("GFX", "Font %d not found", fontId);
|
LOG_ERR("GFX", "Font %d not found", fontId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto font = fontMap.at(fontId);
|
|
||||||
|
const auto& font = fontIt->second;
|
||||||
|
|
||||||
// No printable characters
|
// No printable characters
|
||||||
if (!font.hasPrintableChars(text, style)) {
|
if (!font.hasPrintableChars(text, style)) {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#include <HalGPIO.h>
|
#include <HalGPIO.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <esp_sleep.h>
|
|
||||||
|
|
||||||
void HalGPIO::begin() {
|
void HalGPIO::begin() {
|
||||||
inputMgr.begin();
|
inputMgr.begin();
|
||||||
SPI.begin(EPD_SCLK, SPI_MISO, EPD_MOSI, EPD_CS);
|
SPI.begin(EPD_SCLK, SPI_MISO, EPD_MOSI, EPD_CS);
|
||||||
pinMode(BAT_GPIO0, INPUT);
|
|
||||||
pinMode(UART0_RXD, INPUT);
|
pinMode(UART0_RXD, INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,23 +21,6 @@ bool HalGPIO::wasAnyReleased() const { return inputMgr.wasAnyReleased(); }
|
|||||||
|
|
||||||
unsigned long HalGPIO::getHeldTime() const { return inputMgr.getHeldTime(); }
|
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 {
|
bool HalGPIO::isUsbConnected() const {
|
||||||
// U0RXD/GPIO20 reads HIGH when USB is connected
|
// U0RXD/GPIO20 reads HIGH when USB is connected
|
||||||
return digitalRead(UART0_RXD) == HIGH;
|
return digitalRead(UART0_RXD) == HIGH;
|
||||||
|
|||||||
@@ -38,12 +38,6 @@ class HalGPIO {
|
|||||||
bool wasAnyReleased() const;
|
bool wasAnyReleased() const;
|
||||||
unsigned long getHeldTime() 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
|
// Check if USB is connected
|
||||||
bool isUsbConnected() const;
|
bool isUsbConnected() const;
|
||||||
|
|
||||||
|
|||||||
95
lib/hal/HalPowerManager.cpp
Normal file
95
lib/hal/HalPowerManager.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#include "HalPowerManager.h"
|
||||||
|
|
||||||
|
#include <Logging.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <esp_sleep.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "HalGPIO.h"
|
||||||
|
|
||||||
|
HalPowerManager powerManager; // Singleton instance
|
||||||
|
|
||||||
|
void HalPowerManager::begin() {
|
||||||
|
pinMode(BAT_GPIO0, INPUT);
|
||||||
|
normalFreq = getCpuFrequencyMhz();
|
||||||
|
modeMutex = xSemaphoreCreateMutex();
|
||||||
|
assert(modeMutex != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HalPowerManager::setPowerSaving(bool enabled) {
|
||||||
|
if (normalFreq <= 0) {
|
||||||
|
return; // invalid state
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wifiMode = WiFi.getMode();
|
||||||
|
if (wifiMode != WIFI_MODE_NULL) {
|
||||||
|
// Wifi is active, force disabling power saving
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: We don't use mutex here to avoid too much overhead,
|
||||||
|
// it's not very important if we read a slightly stale value for currentLockMode
|
||||||
|
const LockMode mode = currentLockMode;
|
||||||
|
|
||||||
|
if (mode == None && enabled && !isLowPower) {
|
||||||
|
LOG_DBG("PWR", "Going to low-power mode");
|
||||||
|
if (!setCpuFrequencyMhz(LOW_POWER_FREQ)) {
|
||||||
|
LOG_DBG("PWR", "Failed to set CPU frequency = %d MHz", LOW_POWER_FREQ);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isLowPower = true;
|
||||||
|
|
||||||
|
} else if ((!enabled || mode != None) && isLowPower) {
|
||||||
|
LOG_DBG("PWR", "Restoring normal CPU frequency");
|
||||||
|
if (!setCpuFrequencyMhz(normalFreq)) {
|
||||||
|
LOG_DBG("PWR", "Failed to set CPU frequency = %d MHz", normalFreq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isLowPower = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, no change needed
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
HalPowerManager::Lock::Lock() {
|
||||||
|
xSemaphoreTake(powerManager.modeMutex, portMAX_DELAY);
|
||||||
|
// Current limitation: only one lock at a time
|
||||||
|
if (powerManager.currentLockMode != None) {
|
||||||
|
LOG_ERR("PWR", "Lock already held, ignore");
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
powerManager.currentLockMode = NormalSpeed;
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(powerManager.modeMutex);
|
||||||
|
if (valid) {
|
||||||
|
// Immediately restore normal CPU frequency if currently in low-power mode
|
||||||
|
powerManager.setPowerSaving(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HalPowerManager::Lock::~Lock() {
|
||||||
|
xSemaphoreTake(powerManager.modeMutex, portMAX_DELAY);
|
||||||
|
if (valid) {
|
||||||
|
powerManager.currentLockMode = None;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(powerManager.modeMutex);
|
||||||
|
}
|
||||||
56
lib/hal/HalPowerManager.h
Normal file
56
lib/hal/HalPowerManager.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <InputManager.h>
|
||||||
|
#include <Logging.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "HalGPIO.h"
|
||||||
|
|
||||||
|
class HalPowerManager;
|
||||||
|
extern HalPowerManager powerManager; // Singleton
|
||||||
|
|
||||||
|
class HalPowerManager {
|
||||||
|
int normalFreq = 0; // MHz
|
||||||
|
bool isLowPower = false;
|
||||||
|
|
||||||
|
enum LockMode { None, NormalSpeed };
|
||||||
|
LockMode currentLockMode = None;
|
||||||
|
SemaphoreHandle_t modeMutex = nullptr; // Protect access to currentLockMode
|
||||||
|
|
||||||
|
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
|
||||||
|
// Should be called inside main loop() to handle the currentLockMode
|
||||||
|
void startDeepSleep(HalGPIO& gpio) const;
|
||||||
|
|
||||||
|
// Get battery percentage (range 0-100)
|
||||||
|
int getBatteryPercentage() const;
|
||||||
|
|
||||||
|
// RAII helper class to manage power saving locks
|
||||||
|
// Usage: create an instance of Lock in a scope to disable power saving, for example when running a task that needs
|
||||||
|
// full performance. When the Lock instance is destroyed (goes out of scope), power saving will be re-enabled.
|
||||||
|
class Lock {
|
||||||
|
friend class HalPowerManager;
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Lock();
|
||||||
|
~Lock();
|
||||||
|
|
||||||
|
// Non-copyable and non-movable
|
||||||
|
Lock(const Lock&) = delete;
|
||||||
|
Lock& operator=(const Lock&) = delete;
|
||||||
|
Lock(Lock&&) = delete;
|
||||||
|
Lock& operator=(Lock&&) = delete;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "Activity.h"
|
#include "Activity.h"
|
||||||
|
|
||||||
|
#include <HalPowerManager.h>
|
||||||
|
|
||||||
void Activity::renderTaskTrampoline(void* param) {
|
void Activity::renderTaskTrampoline(void* param) {
|
||||||
auto* self = static_cast<Activity*>(param);
|
auto* self = static_cast<Activity*>(param);
|
||||||
self->renderTaskLoop();
|
self->renderTaskLoop();
|
||||||
@@ -9,6 +11,7 @@ void Activity::renderTaskLoop() {
|
|||||||
while (true) {
|
while (true) {
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
{
|
{
|
||||||
|
HalPowerManager::Lock powerLock; // Ensure we don't go into low-power mode while rendering
|
||||||
RenderLock lock(*this);
|
RenderLock lock(*this);
|
||||||
render(std::move(lock));
|
render(std::move(lock));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#include "ActivityWithSubactivity.h"
|
#include "ActivityWithSubactivity.h"
|
||||||
|
|
||||||
|
#include <HalPowerManager.h>
|
||||||
|
|
||||||
void ActivityWithSubactivity::renderTaskLoop() {
|
void ActivityWithSubactivity::renderTaskLoop() {
|
||||||
while (true) {
|
while (true) {
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
{
|
{
|
||||||
|
HalPowerManager::Lock powerLock; // Ensure we don't go into low-power mode while rendering
|
||||||
RenderLock lock(*this);
|
RenderLock lock(*this);
|
||||||
if (!subActivity) {
|
if (!subActivity) {
|
||||||
render(std::move(lock));
|
render(std::move(lock));
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ void OpdsBookBrowserActivity::render(Activity::RenderLock&&) {
|
|||||||
if (!entries.empty() && entries[selectorIndex].type == OpdsEntryType::BOOK) {
|
if (!entries.empty() && entries[selectorIndex].type == OpdsEntryType::BOOK) {
|
||||||
confirmLabel = tr(STR_DOWNLOAD);
|
confirmLabel = tr(STR_DOWNLOAD);
|
||||||
}
|
}
|
||||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), confirmLabel, "", "");
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), confirmLabel, tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
if (entries.empty()) {
|
if (entries.empty()) {
|
||||||
|
|||||||
@@ -196,8 +196,8 @@ void MyLibraryActivity::render(Activity::RenderLock&&) {
|
|||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
auto metrics = UITheme::getInstance().getMetrics();
|
auto metrics = UITheme::getInstance().getMetrics();
|
||||||
|
|
||||||
auto folderName = basepath == "/" ? tr(STR_SD_CARD) : basepath.substr(basepath.rfind('/') + 1).c_str();
|
std::string folderName = (basepath == "/") ? tr(STR_SD_CARD) : basepath.substr(basepath.rfind('/') + 1);
|
||||||
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, folderName);
|
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, folderName.c_str());
|
||||||
|
|
||||||
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
||||||
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing;
|
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing;
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void NetworkModeSelectionActivity::render(Activity::RenderLock&&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw help text at bottom
|
// Draw help text at bottom
|
||||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", "");
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@@ -310,8 +310,8 @@ void KOReaderSyncActivity::render(Activity::RenderLock&&) {
|
|||||||
}
|
}
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, optionY + optionHeight, tr(STR_UPLOAD_LOCAL), selectedOption != 1);
|
renderer.drawText(UI_10_FONT_ID, 20, optionY + optionHeight, tr(STR_UPLOAD_LOCAL), selectedOption != 1);
|
||||||
|
|
||||||
// Bottom button hints: show Back and Select
|
// Bottom button hints
|
||||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", "");
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ void CalibreSettingsActivity::render(Activity::RenderLock&&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw button hints
|
// Draw button hints
|
||||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", "");
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class ClearCacheActivity final : public ActivityWithSubactivity {
|
|||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
void onExit() override;
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
bool skipLoopDelay() override { return true; } // Prevent power-saving mode
|
||||||
void render(Activity::RenderLock&&) override;
|
void render(Activity::RenderLock&&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ void KOReaderSettingsActivity::render(Activity::RenderLock&&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw button hints
|
// Draw button hints
|
||||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", "");
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ void LanguageSelectActivity::render(Activity::RenderLock&&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Button hints
|
// Button hints
|
||||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", "");
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@@ -34,4 +34,5 @@ class OtaUpdateActivity : public ActivityWithSubactivity {
|
|||||||
void loop() override;
|
void loop() override;
|
||||||
void render(Activity::RenderLock&&) override;
|
void render(Activity::RenderLock&&) override;
|
||||||
bool preventAutoSleep() override { return state == CHECKING_FOR_UPDATE || state == UPDATE_IN_PROGRESS; }
|
bool preventAutoSleep() override { return state == CHECKING_FOR_UPDATE || state == UPDATE_IN_PROGRESS; }
|
||||||
|
bool skipLoopDelay() override { return true; } // Prevent power-saving mode
|
||||||
};
|
};
|
||||||
|
|||||||
18
src/main.cpp
18
src/main.cpp
@@ -3,6 +3,7 @@
|
|||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalDisplay.h>
|
#include <HalDisplay.h>
|
||||||
#include <HalGPIO.h>
|
#include <HalGPIO.h>
|
||||||
|
#include <HalPowerManager.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
@@ -183,7 +184,7 @@ void verifyPowerButtonDuration() {
|
|||||||
if (abort) {
|
if (abort) {
|
||||||
// Button released too early. Returning to sleep.
|
// Button released too early. Returning to sleep.
|
||||||
// IMPORTANT: Re-arm the wakeup trigger before sleeping again
|
// IMPORTANT: Re-arm the wakeup trigger before sleeping again
|
||||||
gpio.startDeepSleep();
|
powerManager.startDeepSleep(gpio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ void enterDeepSleep() {
|
|||||||
LOG_DBG("MAIN", "Power button press calibration value: %lu ms", t2 - t1);
|
LOG_DBG("MAIN", "Power button press calibration value: %lu ms", t2 - t1);
|
||||||
LOG_DBG("MAIN", "Entering deep sleep");
|
LOG_DBG("MAIN", "Entering deep sleep");
|
||||||
|
|
||||||
gpio.startDeepSleep();
|
powerManager.startDeepSleep(gpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoHome();
|
void onGoHome();
|
||||||
@@ -283,6 +284,7 @@ void setup() {
|
|||||||
t1 = millis();
|
t1 = millis();
|
||||||
|
|
||||||
gpio.begin();
|
gpio.begin();
|
||||||
|
powerManager.begin();
|
||||||
|
|
||||||
// Only start serial if USB connected
|
// Only start serial if USB connected
|
||||||
if (gpio.isUsbConnected()) {
|
if (gpio.isUsbConnected()) {
|
||||||
@@ -319,7 +321,7 @@ void setup() {
|
|||||||
case HalGPIO::WakeupReason::AfterUSBPower:
|
case HalGPIO::WakeupReason::AfterUSBPower:
|
||||||
// If USB power caused a cold boot, go back to sleep
|
// If USB power caused a cold boot, go back to sleep
|
||||||
LOG_DBG("MAIN", "Wakeup reason: After USB Power");
|
LOG_DBG("MAIN", "Wakeup reason: After USB Power");
|
||||||
gpio.startDeepSleep();
|
powerManager.startDeepSleep(gpio);
|
||||||
break;
|
break;
|
||||||
case HalGPIO::WakeupReason::AfterFlash:
|
case HalGPIO::WakeupReason::AfterFlash:
|
||||||
// After flashing, just proceed to boot
|
// After flashing, just proceed to boot
|
||||||
@@ -391,7 +393,8 @@ void loop() {
|
|||||||
// Check for any user activity (button press or release) or active background work
|
// Check for any user activity (button press or release) or active background work
|
||||||
static unsigned long lastActivityTime = millis();
|
static unsigned long lastActivityTime = millis();
|
||||||
if (gpio.wasAnyPressed() || gpio.wasAnyReleased() || (currentActivity && currentActivity->preventAutoSleep())) {
|
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();
|
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
|
||||||
@@ -426,11 +429,12 @@ void loop() {
|
|||||||
// When an activity requests skip loop delay (e.g., webserver running), use yield() for faster response
|
// When an activity requests skip loop delay (e.g., webserver running), use yield() for faster response
|
||||||
// Otherwise, use longer delay to save power
|
// Otherwise, use longer delay to save power
|
||||||
if (currentActivity && currentActivity->skipLoopDelay()) {
|
if (currentActivity && currentActivity->skipLoopDelay()) {
|
||||||
yield(); // Give FreeRTOS a chance to run tasks, but return immediately
|
powerManager.setPowerSaving(false); // Make sure we're at full performance when skipLoopDelay is requested
|
||||||
|
yield(); // Give FreeRTOS a chance to run tasks, but return immediately
|
||||||
} else {
|
} else {
|
||||||
static constexpr unsigned long IDLE_POWER_SAVING_MS = 3000; // 3 seconds
|
if (millis() - lastActivityTime >= HalPowerManager::IDLE_POWER_SAVING_MS) {
|
||||||
if (millis() - lastActivityTime >= IDLE_POWER_SAVING_MS) {
|
|
||||||
// If we've been inactive for a while, increase the delay to save power
|
// 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);
|
delay(50);
|
||||||
} else {
|
} else {
|
||||||
// Short delay to prevent tight loop while still being responsive
|
// Short delay to prevent tight loop while still being responsive
|
||||||
|
|||||||
Reference in New Issue
Block a user