From 08e71bfcb079ab9c2f5671f7e69a19d99980fce9 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Fri, 23 Jan 2026 23:00:32 +0100 Subject: [PATCH 1/4] feat: add HalDisplay and HalGPIO --- lib/GfxRenderer/GfxRenderer.cpp | 63 +++++++------- lib/GfxRenderer/GfxRenderer.h | 12 +-- lib/hal/HalDisplay.cpp | 68 +++++++++++++++ lib/hal/HalDisplay.h | 51 ++++++++++++ lib/hal/HalGPIO.cpp | 59 +++++++++++++ lib/hal/HalGPIO.h | 68 +++++++++++++++ src/MappedInputManager.cpp | 48 +++++------ src/MappedInputManager.h | 8 +- src/activities/boot_sleep/SleepActivity.cpp | 6 +- src/activities/reader/EpubReaderActivity.cpp | 4 +- src/activities/reader/TxtReaderActivity.cpp | 4 +- src/activities/reader/XtcReaderActivity.cpp | 4 +- .../util/FullScreenMessageActivity.h | 6 +- src/main.cpp | 83 +++++++------------ 14 files changed, 352 insertions(+), 132 deletions(-) create mode 100644 lib/hal/HalDisplay.cpp create mode 100644 lib/hal/HalDisplay.h create mode 100644 lib/hal/HalGPIO.cpp create mode 100644 lib/hal/HalGPIO.h diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 08420bf9..7dd96dd7 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -10,19 +10,19 @@ void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int // Logical portrait (480x800) → panel (800x480) // Rotation: 90 degrees clockwise *rotatedX = y; - *rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x; + *rotatedY = HalDisplay::DISPLAY_HEIGHT - 1 - x; break; } case LandscapeClockwise: { // Logical landscape (800x480) rotated 180 degrees (swap top/bottom and left/right) - *rotatedX = EInkDisplay::DISPLAY_WIDTH - 1 - x; - *rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - y; + *rotatedX = HalDisplay::DISPLAY_WIDTH - 1 - x; + *rotatedY = HalDisplay::DISPLAY_HEIGHT - 1 - y; break; } case PortraitInverted: { // Logical portrait (480x800) → panel (800x480) // Rotation: 90 degrees counter-clockwise - *rotatedX = EInkDisplay::DISPLAY_WIDTH - 1 - y; + *rotatedX = HalDisplay::DISPLAY_WIDTH - 1 - y; *rotatedY = x; break; } @@ -36,7 +36,7 @@ void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int } void GfxRenderer::drawPixel(const int x, const int y, const bool state) const { - uint8_t* frameBuffer = einkDisplay.getFrameBuffer(); + uint8_t* frameBuffer = display.getFrameBuffer(); // Early return if no framebuffer is set if (!frameBuffer) { @@ -49,14 +49,14 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const { rotateCoordinates(x, y, &rotatedX, &rotatedY); // Bounds checking against physical panel dimensions - if (rotatedX < 0 || rotatedX >= EInkDisplay::DISPLAY_WIDTH || rotatedY < 0 || - rotatedY >= EInkDisplay::DISPLAY_HEIGHT) { + if (rotatedX < 0 || rotatedX >= HalDisplay::DISPLAY_WIDTH || rotatedY < 0 || + rotatedY >= HalDisplay::DISPLAY_HEIGHT) { Serial.printf("[%lu] [GFX] !! Outside range (%d, %d) -> (%d, %d)\n", millis(), x, y, rotatedX, rotatedY); return; } // Calculate byte position and bit position - const uint16_t byteIndex = rotatedY * EInkDisplay::DISPLAY_WIDTH_BYTES + (rotatedX / 8); + const uint16_t byteIndex = rotatedY * HalDisplay::DISPLAY_WIDTH_BYTES + (rotatedX / 8); const uint8_t bitPosition = 7 - (rotatedX % 8); // MSB first if (state) { @@ -149,7 +149,7 @@ void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, co int rotatedX = 0; int rotatedY = 0; rotateCoordinates(x, y, &rotatedX, &rotatedY); - einkDisplay.drawImage(bitmap, rotatedX, rotatedY, width, height); + display.drawImage(bitmap, rotatedX, rotatedY, width, height); } void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, const int maxWidth, const int maxHeight, @@ -384,21 +384,21 @@ void GfxRenderer::fillPolygon(const int* xPoints, const int* yPoints, int numPoi free(nodeX); } -void GfxRenderer::clearScreen(const uint8_t color) const { einkDisplay.clearScreen(color); } +void GfxRenderer::clearScreen(const uint8_t color) const { display.clearScreen(color); } void GfxRenderer::invertScreen() const { - uint8_t* buffer = einkDisplay.getFrameBuffer(); + uint8_t* buffer = display.getFrameBuffer(); if (!buffer) { Serial.printf("[%lu] [GFX] !! No framebuffer in invertScreen\n", millis()); return; } - for (int i = 0; i < EInkDisplay::BUFFER_SIZE; i++) { + for (int i = 0; i < HalDisplay::BUFFER_SIZE; i++) { buffer[i] = ~buffer[i]; } } -void GfxRenderer::displayBuffer(const EInkDisplay::RefreshMode refreshMode) const { - einkDisplay.displayBuffer(refreshMode); +void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const { + display.displayBuffer(refreshMode); } std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth, @@ -418,13 +418,13 @@ int GfxRenderer::getScreenWidth() const { case Portrait: case PortraitInverted: // 480px wide in portrait logical coordinates - return EInkDisplay::DISPLAY_HEIGHT; + return HalDisplay::DISPLAY_HEIGHT; case LandscapeClockwise: case LandscapeCounterClockwise: // 800px wide in landscape logical coordinates - return EInkDisplay::DISPLAY_WIDTH; + return HalDisplay::DISPLAY_WIDTH; } - return EInkDisplay::DISPLAY_HEIGHT; + return HalDisplay::DISPLAY_HEIGHT; } int GfxRenderer::getScreenHeight() const { @@ -432,13 +432,13 @@ int GfxRenderer::getScreenHeight() const { case Portrait: case PortraitInverted: // 800px tall in portrait logical coordinates - return EInkDisplay::DISPLAY_WIDTH; + return HalDisplay::DISPLAY_WIDTH; case LandscapeClockwise: case LandscapeCounterClockwise: // 480px tall in landscape logical coordinates - return EInkDisplay::DISPLAY_HEIGHT; + return HalDisplay::DISPLAY_HEIGHT; } - return EInkDisplay::DISPLAY_WIDTH; + return HalDisplay::DISPLAY_WIDTH; } int GfxRenderer::getSpaceWidth(const int fontId) const { @@ -638,17 +638,18 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y } } -uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); } +uint8_t* GfxRenderer::getFrameBuffer() const { return display.getFrameBuffer(); } -size_t GfxRenderer::getBufferSize() { return EInkDisplay::BUFFER_SIZE; } +size_t GfxRenderer::getBufferSize() { return HalDisplay::BUFFER_SIZE; } -void GfxRenderer::grayscaleRevert() const { einkDisplay.grayscaleRevert(); } +// unused +// void GfxRenderer::grayscaleRevert() const { display.grayscaleRevert(); } -void GfxRenderer::copyGrayscaleLsbBuffers() const { einkDisplay.copyGrayscaleLsbBuffers(einkDisplay.getFrameBuffer()); } +void GfxRenderer::copyGrayscaleLsbBuffers() const { display.copyGrayscaleLsbBuffers(display.getFrameBuffer()); } -void GfxRenderer::copyGrayscaleMsbBuffers() const { einkDisplay.copyGrayscaleMsbBuffers(einkDisplay.getFrameBuffer()); } +void GfxRenderer::copyGrayscaleMsbBuffers() const { display.copyGrayscaleMsbBuffers(display.getFrameBuffer()); } -void GfxRenderer::displayGrayBuffer() const { einkDisplay.displayGrayBuffer(); } +void GfxRenderer::displayGrayBuffer() const { display.displayGrayBuffer(); } void GfxRenderer::freeBwBufferChunks() { for (auto& bwBufferChunk : bwBufferChunks) { @@ -666,7 +667,7 @@ void GfxRenderer::freeBwBufferChunks() { * Returns true if buffer was stored successfully, false if allocation failed. */ bool GfxRenderer::storeBwBuffer() { - const uint8_t* frameBuffer = einkDisplay.getFrameBuffer(); + const uint8_t* frameBuffer = display.getFrameBuffer(); if (!frameBuffer) { Serial.printf("[%lu] [GFX] !! No framebuffer in storeBwBuffer\n", millis()); return false; @@ -721,7 +722,7 @@ void GfxRenderer::restoreBwBuffer() { return; } - uint8_t* frameBuffer = einkDisplay.getFrameBuffer(); + uint8_t* frameBuffer = display.getFrameBuffer(); if (!frameBuffer) { Serial.printf("[%lu] [GFX] !! No framebuffer in restoreBwBuffer\n", millis()); freeBwBufferChunks(); @@ -740,7 +741,7 @@ void GfxRenderer::restoreBwBuffer() { memcpy(frameBuffer + offset, bwBufferChunks[i], BW_BUFFER_CHUNK_SIZE); } - einkDisplay.cleanupGrayscaleBuffers(frameBuffer); + display.cleanupGrayscaleBuffers(frameBuffer); freeBwBufferChunks(); Serial.printf("[%lu] [GFX] Restored and freed BW buffer chunks\n", millis()); @@ -751,9 +752,9 @@ void GfxRenderer::restoreBwBuffer() { * Use this when BW buffer was re-rendered instead of stored/restored. */ void GfxRenderer::cleanupGrayscaleWithFrameBuffer() const { - uint8_t* frameBuffer = einkDisplay.getFrameBuffer(); + uint8_t* frameBuffer = display.getFrameBuffer(); if (frameBuffer) { - einkDisplay.cleanupGrayscaleBuffers(frameBuffer); + display.cleanupGrayscaleBuffers(frameBuffer); } } diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index b1fea69b..ebf194e5 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -21,11 +21,11 @@ class GfxRenderer { private: static constexpr size_t BW_BUFFER_CHUNK_SIZE = 8000; // 8KB chunks to allow for non-contiguous memory - static constexpr size_t BW_BUFFER_NUM_CHUNKS = EInkDisplay::BUFFER_SIZE / BW_BUFFER_CHUNK_SIZE; - static_assert(BW_BUFFER_CHUNK_SIZE * BW_BUFFER_NUM_CHUNKS == EInkDisplay::BUFFER_SIZE, + static constexpr size_t BW_BUFFER_NUM_CHUNKS = HalDisplay::BUFFER_SIZE / BW_BUFFER_CHUNK_SIZE; + static_assert(BW_BUFFER_CHUNK_SIZE * BW_BUFFER_NUM_CHUNKS == HalDisplay::BUFFER_SIZE, "BW buffer chunking does not line up with display buffer size"); - EInkDisplay& einkDisplay; + HalDisplay& display; RenderMode renderMode; Orientation orientation; uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr}; @@ -36,7 +36,7 @@ class GfxRenderer { void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) const; public: - explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {} + explicit GfxRenderer(HalDisplay& halDisplay) : display(halDisplay), renderMode(BW), orientation(Portrait) {} ~GfxRenderer() { freeBwBufferChunks(); } static constexpr int VIEWABLE_MARGIN_TOP = 9; @@ -54,7 +54,7 @@ class GfxRenderer { // Screen ops int getScreenWidth() const; int getScreenHeight() const; - void displayBuffer(EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const; + void displayBuffer(HalDisplay::RefreshMode refreshMode = HalDisplay::FAST_REFRESH) const; // EXPERIMENTAL: Windowed update - display only a rectangular region void displayWindow(int x, int y, int width, int height) const; void invertScreen() const; diff --git a/lib/hal/HalDisplay.cpp b/lib/hal/HalDisplay.cpp new file mode 100644 index 00000000..180b85be --- /dev/null +++ b/lib/hal/HalDisplay.cpp @@ -0,0 +1,68 @@ +#include +#include + +#define SD_SPI_MISO 7 + +HalDisplay::HalDisplay() : einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY) {} + +HalDisplay::~HalDisplay() {} + +void HalDisplay::begin() { + einkDisplay.begin(); +} + +void HalDisplay::clearScreen(uint8_t color) const { + einkDisplay.clearScreen(color); +} + +void HalDisplay::drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem) const { + einkDisplay.drawImage(imageData, x, y, w, h, fromProgmem); +} + +EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) { + switch (mode) { + case HalDisplay::FULL_REFRESH: + return EInkDisplay::FULL_REFRESH; + case HalDisplay::HALF_REFRESH: + return EInkDisplay::HALF_REFRESH; + case HalDisplay::FAST_REFRESH: + default: + return EInkDisplay::FAST_REFRESH; + } +} + +void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode) { + einkDisplay.displayBuffer(convertRefreshMode(mode)); +} + +void HalDisplay::refreshDisplay(HalDisplay::RefreshMode mode, bool turnOffScreen) { + einkDisplay.refreshDisplay(convertRefreshMode(mode), turnOffScreen); +} + +void HalDisplay::deepSleep() { + einkDisplay.deepSleep(); +} + +uint8_t* HalDisplay::getFrameBuffer() const { + return einkDisplay.getFrameBuffer(); +} + +void HalDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer) { + einkDisplay.copyGrayscaleBuffers(lsbBuffer, msbBuffer); +} + +void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) { + einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer); +} + +void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { + einkDisplay.copyGrayscaleMsbBuffers(msbBuffer); +} + +void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { + einkDisplay.cleanupGrayscaleBuffers(bwBuffer); +} + +void HalDisplay::displayGrayBuffer() { + einkDisplay.displayGrayBuffer(); +} diff --git a/lib/hal/HalDisplay.h b/lib/hal/HalDisplay.h new file mode 100644 index 00000000..72b9155e --- /dev/null +++ b/lib/hal/HalDisplay.h @@ -0,0 +1,51 @@ +#pragma once +#include +#include + +class HalDisplay { + public: + // Constructor with pin configuration + HalDisplay(); + + // Destructor + ~HalDisplay(); + + // Refresh modes + enum RefreshMode { + FULL_REFRESH, // Full refresh with complete waveform + HALF_REFRESH, // Half refresh (1720ms) - balanced quality and speed + FAST_REFRESH // Fast refresh using custom LUT + }; + + // Initialize the display hardware and driver + void begin(); + + // Display dimensions + static constexpr uint16_t DISPLAY_WIDTH = EInkDisplay::DISPLAY_WIDTH; + static constexpr uint16_t DISPLAY_HEIGHT = EInkDisplay::DISPLAY_HEIGHT; + static constexpr uint16_t DISPLAY_WIDTH_BYTES = DISPLAY_WIDTH / 8; + static constexpr uint32_t BUFFER_SIZE = DISPLAY_WIDTH_BYTES * DISPLAY_HEIGHT; + + // Frame buffer operations + void clearScreen(uint8_t color = 0xFF) const; + void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem = false) const; + + void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH); + void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); + + // Power management + void deepSleep(); + + // Access to frame buffer + uint8_t* getFrameBuffer() const; + + void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer); + void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer); + void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer); + void cleanupGrayscaleBuffers(const uint8_t* bwBuffer); + + void displayGrayBuffer(); + + private: + EInkDisplay einkDisplay; +}; diff --git a/lib/hal/HalGPIO.cpp b/lib/hal/HalGPIO.cpp new file mode 100644 index 00000000..20bd5577 --- /dev/null +++ b/lib/hal/HalGPIO.cpp @@ -0,0 +1,59 @@ +#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); +} + +void HalGPIO::update() { + inputMgr.update(); +} + +bool HalGPIO::isPressed(uint8_t buttonIndex) const { + return inputMgr.isPressed(buttonIndex); +} + +bool HalGPIO::wasPressed(uint8_t buttonIndex) const { + return inputMgr.wasPressed(buttonIndex); +} + +bool HalGPIO::wasAnyPressed() const { + return inputMgr.wasAnyPressed(); +} + +bool HalGPIO::wasReleased(uint8_t buttonIndex) const { + return inputMgr.wasReleased(buttonIndex); +} + +bool HalGPIO::wasAnyReleased() const { + return inputMgr.wasAnyReleased(); +} + +unsigned long HalGPIO::getHeldTime() const { + return inputMgr.getHeldTime(); +} + +void HalGPIO::startDeepSleep() { + esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); + // 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(); + } + // 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 new file mode 100644 index 00000000..dc8c0044 --- /dev/null +++ b/lib/hal/HalGPIO.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults) +#define EPD_SCLK 8 // SPI Clock +#define EPD_MOSI 10 // SPI MOSI (Master Out Slave In) +#define EPD_CS 21 // Chip Select +#define EPD_DC 4 // Data/Command +#define EPD_RST 5 // Reset +#define EPD_BUSY 6 // Busy + +#define SPI_MISO 7 // SPI MISO, shared between SD card and display (Master In Slave Out) + +#define BAT_GPIO0 0 // Battery voltage + +#define UART0_RXD 20 // Used for USB connection detection + +class HalGPIO { +#if CROSSPOINT_EMULATED == 0 + InputManager inputMgr; +#endif + + public: + HalGPIO() = default; + + // Start button GPIO and setup SPI for screen and SD card + void begin(); + + // Button input methods + void update(); + bool isPressed(uint8_t buttonIndex) const; + bool wasPressed(uint8_t buttonIndex) const; + bool wasAnyPressed() const; + bool wasReleased(uint8_t buttonIndex) const; + 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; + + // Button indices + static constexpr uint8_t BTN_BACK = 0; + static constexpr uint8_t BTN_CONFIRM = 1; + static constexpr uint8_t BTN_LEFT = 2; + static constexpr uint8_t BTN_RIGHT = 3; + static constexpr uint8_t BTN_UP = 4; + static constexpr uint8_t BTN_DOWN = 5; + static constexpr uint8_t BTN_POWER = 6; + + private: + // emulation state + uint8_t currentState; + uint8_t lastState; + uint8_t pressedEvents; + uint8_t releasedEvents; + unsigned long lastDebounceTime; + unsigned long buttonPressStart; + unsigned long buttonPressFinish; +}; diff --git a/src/MappedInputManager.cpp b/src/MappedInputManager.cpp index 1b038446..3d0fc664 100644 --- a/src/MappedInputManager.cpp +++ b/src/MappedInputManager.cpp @@ -10,79 +10,79 @@ decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button butt case Button::Back: switch (frontLayout) { case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - return InputManager::BTN_LEFT; + return HalGPIO::BTN_LEFT; case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - return InputManager::BTN_CONFIRM; + return HalGPIO::BTN_CONFIRM; case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: default: - return InputManager::BTN_BACK; + return HalGPIO::BTN_BACK; } case Button::Confirm: switch (frontLayout) { case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - return InputManager::BTN_RIGHT; + return HalGPIO::BTN_RIGHT; case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - return InputManager::BTN_LEFT; + return HalGPIO::BTN_LEFT; case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: default: - return InputManager::BTN_CONFIRM; + return HalGPIO::BTN_CONFIRM; } case Button::Left: switch (frontLayout) { case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - return InputManager::BTN_BACK; + return HalGPIO::BTN_BACK; case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: default: - return InputManager::BTN_LEFT; + return HalGPIO::BTN_LEFT; } case Button::Right: switch (frontLayout) { case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - return InputManager::BTN_CONFIRM; + return HalGPIO::BTN_CONFIRM; case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: default: - return InputManager::BTN_RIGHT; + return HalGPIO::BTN_RIGHT; } case Button::Up: - return InputManager::BTN_UP; + return HalGPIO::BTN_UP; case Button::Down: - return InputManager::BTN_DOWN; + return HalGPIO::BTN_DOWN; case Button::Power: - return InputManager::BTN_POWER; + return HalGPIO::BTN_POWER; case Button::PageBack: switch (sideLayout) { case CrossPointSettings::NEXT_PREV: - return InputManager::BTN_DOWN; + return HalGPIO::BTN_DOWN; case CrossPointSettings::PREV_NEXT: default: - return InputManager::BTN_UP; + return HalGPIO::BTN_UP; } case Button::PageForward: switch (sideLayout) { case CrossPointSettings::NEXT_PREV: - return InputManager::BTN_UP; + return HalGPIO::BTN_UP; case CrossPointSettings::PREV_NEXT: default: - return InputManager::BTN_DOWN; + return HalGPIO::BTN_DOWN; } } - return InputManager::BTN_BACK; + return HalGPIO::BTN_BACK; } -bool MappedInputManager::wasPressed(const Button button) const { return inputManager.wasPressed(mapButton(button)); } +bool MappedInputManager::wasPressed(const Button button) const { return gpio.wasPressed(mapButton(button)); } -bool MappedInputManager::wasReleased(const Button button) const { return inputManager.wasReleased(mapButton(button)); } +bool MappedInputManager::wasReleased(const Button button) const { return gpio.wasReleased(mapButton(button)); } -bool MappedInputManager::isPressed(const Button button) const { return inputManager.isPressed(mapButton(button)); } +bool MappedInputManager::isPressed(const Button button) const { return gpio.isPressed(mapButton(button)); } -bool MappedInputManager::wasAnyPressed() const { return inputManager.wasAnyPressed(); } +bool MappedInputManager::wasAnyPressed() const { return gpio.wasAnyPressed(); } -bool MappedInputManager::wasAnyReleased() const { return inputManager.wasAnyReleased(); } +bool MappedInputManager::wasAnyReleased() const { return gpio.wasAnyReleased(); } -unsigned long MappedInputManager::getHeldTime() const { return inputManager.getHeldTime(); } +unsigned long MappedInputManager::getHeldTime() const { return gpio.getHeldTime(); } MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous, const char* next) const { diff --git a/src/MappedInputManager.h b/src/MappedInputManager.h index 62065fe9..0dd2e90c 100644 --- a/src/MappedInputManager.h +++ b/src/MappedInputManager.h @@ -1,6 +1,6 @@ #pragma once -#include +#include class MappedInputManager { public: @@ -13,7 +13,7 @@ class MappedInputManager { const char* btn4; }; - explicit MappedInputManager(InputManager& inputManager) : inputManager(inputManager) {} + explicit MappedInputManager(HalGPIO& gpio) : gpio(gpio) {} bool wasPressed(Button button) const; bool wasReleased(Button button) const; @@ -24,6 +24,6 @@ class MappedInputManager { Labels mapLabels(const char* back, const char* confirm, const char* previous, const char* next) const; private: - InputManager& inputManager; - decltype(InputManager::BTN_BACK) mapButton(Button button) const; + HalGPIO& gpio; + decltype(HalGPIO::BTN_BACK) mapButton(Button button) const; }; diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index c0d6844f..7e291782 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -133,7 +133,7 @@ void SleepActivity::renderDefaultSleepScreen() const { renderer.invertScreen(); } - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); } void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { @@ -180,7 +180,7 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { Serial.printf("[%lu] [SLP] drawing to %d x %d\n", millis(), x, y); renderer.clearScreen(); renderer.drawBitmap(bitmap, x, y, pageWidth, pageHeight, cropX, cropY); - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); if (bitmap.hasGreyscale()) { bitmap.rewindToData(); @@ -270,5 +270,5 @@ void SleepActivity::renderCoverSleepScreen() const { void SleepActivity::renderBlankSleepScreen() const { renderer.clearScreen(); - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); } diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 6ff39c5e..0743df71 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -322,7 +322,7 @@ void EpubReaderActivity::renderScreen() { auto progressCallback = [this, barX, barY, barWidth, barHeight](int progress) { const int fillWidth = (barWidth - 2) * progress / 100; renderer.fillRect(barX + 1, barY + 1, fillWidth, barHeight - 2, true); - renderer.displayBuffer(EInkDisplay::FAST_REFRESH); + renderer.displayBuffer(HalDisplay::FAST_REFRESH); }; if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), @@ -392,7 +392,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr page, const int or page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop); renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft); if (pagesUntilFullRefresh <= 1) { - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); pagesUntilFullRefresh = SETTINGS.getRefreshFrequency(); } else { renderer.displayBuffer(); diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index db725320..7c3ea29f 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -237,7 +237,7 @@ void TxtReaderActivity::buildPageIndex() { // Fill progress bar const int fillWidth = (barWidth - 2) * progressPercent / 100; renderer.fillRect(barX + 1, barY + 1, fillWidth, barHeight - 2, true); - renderer.displayBuffer(EInkDisplay::FAST_REFRESH); + renderer.displayBuffer(HalDisplay::FAST_REFRESH); } // Yield to other tasks periodically @@ -465,7 +465,7 @@ void TxtReaderActivity::renderPage() { renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft); if (pagesUntilFullRefresh <= 1) { - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); pagesUntilFullRefresh = SETTINGS.getRefreshFrequency(); } else { renderer.displayBuffer(); diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 0a58d7b3..4aba7e66 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -269,7 +269,7 @@ void XtcReaderActivity::renderPage() { // Display BW with conditional refresh based on pagesUntilFullRefresh if (pagesUntilFullRefresh <= 1) { - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); pagesUntilFullRefresh = SETTINGS.getRefreshFrequency(); } else { renderer.displayBuffer(); @@ -349,7 +349,7 @@ void XtcReaderActivity::renderPage() { // Display with appropriate refresh if (pagesUntilFullRefresh <= 1) { - renderer.displayBuffer(EInkDisplay::HALF_REFRESH); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); pagesUntilFullRefresh = SETTINGS.getRefreshFrequency(); } else { renderer.displayBuffer(); diff --git a/src/activities/util/FullScreenMessageActivity.h b/src/activities/util/FullScreenMessageActivity.h index 3e975c91..b6cf8bed 100644 --- a/src/activities/util/FullScreenMessageActivity.h +++ b/src/activities/util/FullScreenMessageActivity.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -10,12 +10,12 @@ class FullScreenMessageActivity final : public Activity { std::string text; EpdFontFamily::Style style; - EInkDisplay::RefreshMode refreshMode; + HalDisplay::RefreshMode refreshMode; public: explicit FullScreenMessageActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string text, const EpdFontFamily::Style style = EpdFontFamily::REGULAR, - const EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) + const HalDisplay::RefreshMode refreshMode = HalDisplay::FAST_REFRESH) : Activity("FullScreenMessage", renderer, mappedInput), text(std::move(text)), style(style), diff --git a/src/main.cpp b/src/main.cpp index c0222e0d..7880a2c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,9 @@ #include -#include #include #include -#include #include +#include +#include #include #include @@ -26,23 +26,10 @@ #include "activities/util/FullScreenMessageActivity.h" #include "fontIds.h" -#define SPI_FQ 40000000 -// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults) -#define EPD_SCLK 8 // SPI Clock -#define EPD_MOSI 10 // SPI MOSI (Master Out Slave In) -#define EPD_CS 21 // Chip Select -#define EPD_DC 4 // Data/Command -#define EPD_RST 5 // Reset -#define EPD_BUSY 6 // Busy - -#define UART0_RXD 20 // Used for USB connection detection - -#define SD_SPI_MISO 7 - -EInkDisplay einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY); -InputManager inputManager; -MappedInputManager mappedInputManager(inputManager); -GfxRenderer renderer(einkDisplay); +HalDisplay display; +HalGPIO gpio; +MappedInputManager mappedInputManager(gpio); +GfxRenderer renderer(display); Activity* currentActivity; // Fonts @@ -163,20 +150,20 @@ void verifyWakeupLongPress() { const uint16_t calibratedPressDuration = (calibration < SETTINGS.getPowerButtonDuration()) ? SETTINGS.getPowerButtonDuration() - calibration : 1; - inputManager.update(); + gpio.update(); // Verify the user has actually pressed - while (!inputManager.isPressed(InputManager::BTN_POWER) && millis() - start < 1000) { + while (!gpio.isPressed(HalGPIO::BTN_POWER) && millis() - start < 1000) { delay(10); // only wait 10ms each iteration to not delay too much in case of short configured duration. - inputManager.update(); + gpio.update(); } t2 = millis(); - if (inputManager.isPressed(InputManager::BTN_POWER)) { + if (gpio.isPressed(HalGPIO::BTN_POWER)) { do { delay(10); - inputManager.update(); - } while (inputManager.isPressed(InputManager::BTN_POWER) && inputManager.getHeldTime() < calibratedPressDuration); - abort = inputManager.getHeldTime() < calibratedPressDuration; + gpio.update(); + } while (gpio.isPressed(HalGPIO::BTN_POWER) && gpio.getHeldTime() < calibratedPressDuration); + abort = gpio.getHeldTime() < calibratedPressDuration; } else { abort = true; } @@ -190,10 +177,10 @@ void verifyWakeupLongPress() { } void waitForPowerRelease() { - inputManager.update(); - while (inputManager.isPressed(InputManager::BTN_POWER)) { + gpio.update(); + while (gpio.isPressed(HalGPIO::BTN_POWER)) { delay(50); - inputManager.update(); + gpio.update(); } } @@ -202,14 +189,11 @@ void enterDeepSleep() { exitActivity(); enterNewActivity(new SleepActivity(renderer, mappedInputManager)); - einkDisplay.deepSleep(); + display.deepSleep(); Serial.printf("[%lu] [ ] Power button press calibration value: %lu ms\n", millis(), t2 - t1); Serial.printf("[%lu] [ ] Entering deep sleep.\n", millis()); - esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); - // Ensure that the power button has been released to avoid immediately turning back on if you're holding it - waitForPowerRelease(); - // Enter Deep Sleep - esp_deep_sleep_start(); + + gpio.startDeepSleep(); } void onGoHome(); @@ -253,7 +237,7 @@ void onGoHome() { } void setupDisplayAndFonts() { - einkDisplay.begin(); + display.begin(); Serial.printf("[%lu] [ ] Display initialized\n", millis()); renderer.insertFont(BOOKERLY_14_FONT_ID, bookerly14FontFamily); #ifndef OMIT_FONTS @@ -276,24 +260,20 @@ void setupDisplayAndFonts() { Serial.printf("[%lu] [ ] Fonts setup\n", millis()); } -bool isUsbConnected() { - // U0RXD/GPIO20 reads HIGH when USB is connected - return digitalRead(UART0_RXD) == HIGH; -} - bool isWakeupAfterFlashing() { const auto wakeupCause = esp_sleep_get_wakeup_cause(); const auto resetReason = esp_reset_reason(); - return isUsbConnected() && (wakeupCause == ESP_SLEEP_WAKEUP_UNDEFINED) && (resetReason == ESP_RST_UNKNOWN); + return gpio.isUsbConnected() && (wakeupCause == ESP_SLEEP_WAKEUP_UNDEFINED) && (resetReason == ESP_RST_UNKNOWN); } void setup() { t1 = millis(); + gpio.begin(); + // Only start serial if USB connected - pinMode(UART0_RXD, INPUT); - if (isUsbConnected()) { + if (gpio.isUsbConnected()) { Serial.begin(115200); // Wait up to 3 seconds for Serial to be ready to catch early logs unsigned long start = millis(); @@ -302,13 +282,6 @@ void setup() { } } - inputManager.begin(); - // Initialize pins - pinMode(BAT_GPIO0, INPUT); - - // Initialize SPI with custom pins - SPI.begin(EPD_SCLK, SD_SPI_MISO, EPD_MOSI, EPD_CS); - // SD Card Initialization // We need 6 open files concurrently when parsing a new chapter if (!SdMan.begin()) { @@ -358,7 +331,7 @@ void loop() { const unsigned long loopStartTime = millis(); static unsigned long lastMemPrint = 0; - inputManager.update(); + gpio.update(); if (Serial && millis() - lastMemPrint >= 10000) { Serial.printf("[%lu] [MEM] Free: %d bytes, Total: %d bytes, Min Free: %d bytes\n", millis(), ESP.getFreeHeap(), @@ -368,7 +341,7 @@ void loop() { // Check for any user activity (button press or release) or active background work static unsigned long lastActivityTime = millis(); - if (inputManager.wasAnyPressed() || inputManager.wasAnyReleased() || + if (gpio.wasAnyPressed() || gpio.wasAnyReleased() || (currentActivity && currentActivity->preventAutoSleep())) { lastActivityTime = millis(); // Reset inactivity timer } @@ -381,8 +354,8 @@ void loop() { return; } - if (inputManager.isPressed(InputManager::BTN_POWER) && - inputManager.getHeldTime() > SETTINGS.getPowerButtonDuration()) { + if (gpio.isPressed(HalGPIO::BTN_POWER) && + gpio.getHeldTime() > SETTINGS.getPowerButtonDuration()) { enterDeepSleep(); // This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start return; From 6abb4e3b3040201be78d58e1c8580248c2fb60f6 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Fri, 23 Jan 2026 23:10:04 +0100 Subject: [PATCH 2/4] fix build --- lib/hal/HalGPIO.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/hal/HalGPIO.h b/lib/hal/HalGPIO.h index dc8c0044..fc8b2cb6 100644 --- a/lib/hal/HalGPIO.h +++ b/lib/hal/HalGPIO.h @@ -55,14 +55,4 @@ class HalGPIO { static constexpr uint8_t BTN_UP = 4; static constexpr uint8_t BTN_DOWN = 5; static constexpr uint8_t BTN_POWER = 6; - - private: - // emulation state - uint8_t currentState; - uint8_t lastState; - uint8_t pressedEvents; - uint8_t releasedEvents; - unsigned long lastDebounceTime; - unsigned long buttonPressStart; - unsigned long buttonPressFinish; }; From 6db4f6008019cb76b08eb72ac663774f1085f9b9 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Fri, 23 Jan 2026 23:16:59 +0100 Subject: [PATCH 3/4] clang format --- lib/GfxRenderer/GfxRenderer.cpp | 7 +--- lib/GfxRenderer/GfxRenderer.h | 2 +- lib/hal/HalDisplay.cpp | 39 ++++++------------- lib/hal/HalDisplay.h | 5 ++- lib/hal/HalGPIO.cpp | 28 ++++--------- lib/hal/HalGPIO.h | 6 +-- .../util/FullScreenMessageActivity.h | 2 +- src/main.cpp | 10 ++--- 8 files changed, 32 insertions(+), 67 deletions(-) diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 7dd96dd7..5cb957a3 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -49,8 +49,7 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const { rotateCoordinates(x, y, &rotatedX, &rotatedY); // Bounds checking against physical panel dimensions - if (rotatedX < 0 || rotatedX >= HalDisplay::DISPLAY_WIDTH || rotatedY < 0 || - rotatedY >= HalDisplay::DISPLAY_HEIGHT) { + if (rotatedX < 0 || rotatedX >= HalDisplay::DISPLAY_WIDTH || rotatedY < 0 || rotatedY >= HalDisplay::DISPLAY_HEIGHT) { Serial.printf("[%lu] [GFX] !! Outside range (%d, %d) -> (%d, %d)\n", millis(), x, y, rotatedX, rotatedY); return; } @@ -397,9 +396,7 @@ void GfxRenderer::invertScreen() const { } } -void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const { - display.displayBuffer(refreshMode); -} +void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const { display.displayBuffer(refreshMode); } std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth, const EpdFontFamily::Style style) const { diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index ebf194e5..733975f4 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include diff --git a/lib/hal/HalDisplay.cpp b/lib/hal/HalDisplay.cpp index 180b85be..6f69d7fc 100644 --- a/lib/hal/HalDisplay.cpp +++ b/lib/hal/HalDisplay.cpp @@ -7,15 +7,12 @@ HalDisplay::HalDisplay() : einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_R HalDisplay::~HalDisplay() {} -void HalDisplay::begin() { - einkDisplay.begin(); -} +void HalDisplay::begin() { einkDisplay.begin(); } -void HalDisplay::clearScreen(uint8_t color) const { - einkDisplay.clearScreen(color); -} +void HalDisplay::clearScreen(uint8_t color) const { einkDisplay.clearScreen(color); } -void HalDisplay::drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem) const { +void HalDisplay::drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + bool fromProgmem) const { einkDisplay.drawImage(imageData, x, y, w, h, fromProgmem); } @@ -31,38 +28,24 @@ EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) { } } -void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode) { - einkDisplay.displayBuffer(convertRefreshMode(mode)); -} +void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode) { einkDisplay.displayBuffer(convertRefreshMode(mode)); } void HalDisplay::refreshDisplay(HalDisplay::RefreshMode mode, bool turnOffScreen) { einkDisplay.refreshDisplay(convertRefreshMode(mode), turnOffScreen); } -void HalDisplay::deepSleep() { - einkDisplay.deepSleep(); -} +void HalDisplay::deepSleep() { einkDisplay.deepSleep(); } -uint8_t* HalDisplay::getFrameBuffer() const { - return einkDisplay.getFrameBuffer(); -} +uint8_t* HalDisplay::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); } void HalDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer) { einkDisplay.copyGrayscaleBuffers(lsbBuffer, msbBuffer); } -void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) { - einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer); -} +void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) { einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer); } -void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { - einkDisplay.copyGrayscaleMsbBuffers(msbBuffer); -} +void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { einkDisplay.copyGrayscaleMsbBuffers(msbBuffer); } -void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { - einkDisplay.cleanupGrayscaleBuffers(bwBuffer); -} +void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { einkDisplay.cleanupGrayscaleBuffers(bwBuffer); } -void HalDisplay::displayGrayBuffer() { - einkDisplay.displayGrayBuffer(); -} +void HalDisplay::displayGrayBuffer() { einkDisplay.displayGrayBuffer(); } diff --git a/lib/hal/HalDisplay.h b/lib/hal/HalDisplay.h index 72b9155e..6eb7156b 100644 --- a/lib/hal/HalDisplay.h +++ b/lib/hal/HalDisplay.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include class HalDisplay { public: @@ -28,7 +28,8 @@ class HalDisplay { // Frame buffer operations void clearScreen(uint8_t color = 0xFF) const; - void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem = false) const; + void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, + bool fromProgmem = false) const; void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH); void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); diff --git a/lib/hal/HalGPIO.cpp b/lib/hal/HalGPIO.cpp index 20bd5577..6d2510a5 100644 --- a/lib/hal/HalGPIO.cpp +++ b/lib/hal/HalGPIO.cpp @@ -9,33 +9,19 @@ void HalGPIO::begin() { pinMode(UART0_RXD, INPUT); } -void HalGPIO::update() { - inputMgr.update(); -} +void HalGPIO::update() { inputMgr.update(); } -bool HalGPIO::isPressed(uint8_t buttonIndex) const { - return inputMgr.isPressed(buttonIndex); -} +bool HalGPIO::isPressed(uint8_t buttonIndex) const { return inputMgr.isPressed(buttonIndex); } -bool HalGPIO::wasPressed(uint8_t buttonIndex) const { - return inputMgr.wasPressed(buttonIndex); -} +bool HalGPIO::wasPressed(uint8_t buttonIndex) const { return inputMgr.wasPressed(buttonIndex); } -bool HalGPIO::wasAnyPressed() const { - return inputMgr.wasAnyPressed(); -} +bool HalGPIO::wasAnyPressed() const { return inputMgr.wasAnyPressed(); } -bool HalGPIO::wasReleased(uint8_t buttonIndex) const { - return inputMgr.wasReleased(buttonIndex); -} +bool HalGPIO::wasReleased(uint8_t buttonIndex) const { return inputMgr.wasReleased(buttonIndex); } -bool HalGPIO::wasAnyReleased() const { - return inputMgr.wasAnyReleased(); -} +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() { esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); diff --git a/lib/hal/HalGPIO.h b/lib/hal/HalGPIO.h index fc8b2cb6..83244f5b 100644 --- a/lib/hal/HalGPIO.h +++ b/lib/hal/HalGPIO.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include // Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults) #define EPD_SCLK 8 // SPI Clock @@ -12,11 +12,11 @@ #define EPD_RST 5 // Reset #define EPD_BUSY 6 // Busy -#define SPI_MISO 7 // SPI MISO, shared between SD card and display (Master In Slave Out) +#define SPI_MISO 7 // SPI MISO, shared between SD card and display (Master In Slave Out) #define BAT_GPIO0 0 // Battery voltage -#define UART0_RXD 20 // Used for USB connection detection +#define UART0_RXD 20 // Used for USB connection detection class HalGPIO { #if CROSSPOINT_EMULATED == 0 diff --git a/src/activities/util/FullScreenMessageActivity.h b/src/activities/util/FullScreenMessageActivity.h index b6cf8bed..93909503 100644 --- a/src/activities/util/FullScreenMessageActivity.h +++ b/src/activities/util/FullScreenMessageActivity.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include #include #include diff --git a/src/main.cpp b/src/main.cpp index 7880a2c5..2de730e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,9 @@ #include #include #include -#include -#include #include +#include +#include #include #include @@ -341,8 +341,7 @@ 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())) { + if (gpio.wasAnyPressed() || gpio.wasAnyReleased() || (currentActivity && currentActivity->preventAutoSleep())) { lastActivityTime = millis(); // Reset inactivity timer } @@ -354,8 +353,7 @@ void loop() { return; } - if (gpio.isPressed(HalGPIO::BTN_POWER) && - gpio.getHeldTime() > SETTINGS.getPowerButtonDuration()) { + if (gpio.isPressed(HalGPIO::BTN_POWER) && gpio.getHeldTime() > SETTINGS.getPowerButtonDuration()) { enterDeepSleep(); // This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start return; From 181588c068f332ed8ea116f91c05ed043abdb97b Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Fri, 23 Jan 2026 23:25:20 +0100 Subject: [PATCH 4/4] missing startDeepSleep in one place --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2de730e8..b38beb0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -171,8 +171,7 @@ void verifyWakeupLongPress() { if (abort) { // Button released too early. Returning to sleep. // IMPORTANT: Re-arm the wakeup trigger before sleeping again - esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW); - esp_deep_sleep_start(); + gpio.startDeepSleep(); } }