From 0f29de49f8acf3a92d90838c33a1ac0ef7825e34 Mon Sep 17 00:00:00 2001 From: didacta Date: Sat, 24 Jan 2026 20:13:19 -0500 Subject: [PATCH] feat: add charging indicator to battery icon (#516) --- src/Battery.h | 7 ++++ src/ScreenComponents.cpp | 34 +++++++++++++++++--- src/ScreenComponents.h | 3 +- src/activities/home/HomeActivity.cpp | 2 +- src/activities/reader/EpubReaderActivity.cpp | 3 +- src/activities/reader/TxtReaderActivity.cpp | 3 +- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/Battery.h b/src/Battery.h index dcfcbf79..ff88fd41 100644 --- a/src/Battery.h +++ b/src/Battery.h @@ -1,6 +1,13 @@ #pragma once +#include #include #define BAT_GPIO0 0 // Battery voltage static BatteryMonitor battery(BAT_GPIO0); + +// Returns true when USB is connected (device is charging) +// Uses GPIO20 (U0RXD) which reads HIGH when USB is connected +inline bool isCharging() { + return digitalRead(20) == HIGH; +} diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index 2e8d9e7c..a231b381 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -9,7 +9,7 @@ #include "fontIds.h" void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top, - const bool showPercentage) { + const bool showPercentage, const bool charging) { // Left aligned battery icon and percentage const uint16_t percentage = battery.readPercentage(); const auto percentageText = showPercentage ? std::to_string(percentage) + "%" : ""; @@ -34,12 +34,38 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, renderer.drawLine(x + batteryWidth - 0, y + 4, x + batteryWidth - 0, y + batteryHeight - 5); // The +1 is to round up, so that we always fill at least one pixel - int filledWidth = percentage * (batteryWidth - 5) / 100 + 1; - if (filledWidth > batteryWidth - 5) { - filledWidth = batteryWidth - 5; // Ensure we don't overflow + constexpr int maxFillWidth = batteryWidth - 5; + int filledWidth = percentage * maxFillWidth / 100 + 1; + if (filledWidth > maxFillWidth) { + filledWidth = maxFillWidth; + } + + // When charging, ensure minimum fill so lightning bolt is fully visible + constexpr int minFillForBolt = 8; // Bolt extends 6px wide, needs padding + if (charging && filledWidth < minFillForBolt) { + filledWidth = minFillForBolt; } renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4); + + // Draw lightning bolt when charging (white/inverted on black fill for visibility) + if (charging) { + // Lightning bolt: 6px wide, 8px tall, centered in battery + const int boltX = x + 4; + const int boltY = y + 2; + + // Draw bolt in white (state=false) for visibility on black fill + // Upper diagonal pointing right + renderer.drawLine(boltX + 4, boltY + 0, boltX + 5, boltY + 0, false); + renderer.drawLine(boltX + 3, boltY + 1, boltX + 4, boltY + 1, false); + renderer.drawLine(boltX + 2, boltY + 2, boltX + 5, boltY + 2, false); // Wide middle + renderer.drawLine(boltX + 3, boltY + 3, boltX + 4, boltY + 3, false); + // Lower diagonal pointing left + renderer.drawLine(boltX + 2, boltY + 4, boltX + 3, boltY + 4, false); + renderer.drawLine(boltX + 1, boltY + 5, boltX + 4, boltY + 5, false); // Wide middle + renderer.drawLine(boltX + 2, boltY + 6, boltX + 3, boltY + 6, false); + renderer.drawLine(boltX + 1, boltY + 7, boltX + 2, boltY + 7, false); + } } int ScreenComponents::drawTabBar(const GfxRenderer& renderer, const int y, const std::vector& tabs) { diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index 48c40f42..cb736c60 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -13,7 +13,8 @@ struct TabInfo { class ScreenComponents { public: - static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true); + static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true, + bool charging = false); // Draw a horizontal tab bar with underline indicator for selected tab // Returns the height of the tab bar (for positioning content below) diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index eb11ba95..b136c94c 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -550,7 +550,7 @@ void HomeActivity::render() { const uint16_t percentage = battery.readPercentage(); const auto percentageText = showBatteryPercentage ? std::to_string(percentage) + "%" : ""; const auto batteryX = pageWidth - 25 - renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str()); - ScreenComponents::drawBattery(renderer, batteryX, 10, showBatteryPercentage); + ScreenComponents::drawBattery(renderer, batteryX, 10, showBatteryPercentage, isCharging()); renderer.displayBuffer(); } diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 6ff39c5e..2845ad0c 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -5,6 +5,7 @@ #include #include +#include "Battery.h" #include "CrossPointSettings.h" #include "CrossPointState.h" #include "EpubReaderChapterSelectionActivity.h" @@ -457,7 +458,7 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in } if (showBattery) { - ScreenComponents::drawBattery(renderer, orientedMarginLeft + 1, textY, showBatteryPercentage); + ScreenComponents::drawBattery(renderer, orientedMarginLeft + 1, textY, showBatteryPercentage, isCharging()); } if (showChapterTitle) { diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index db725320..aba05f25 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -5,6 +5,7 @@ #include #include +#include "Battery.h" #include "CrossPointSettings.h" #include "CrossPointState.h" #include "MappedInputManager.h" @@ -517,7 +518,7 @@ void TxtReaderActivity::renderStatusBar(const int orientedMarginRight, const int } if (showBattery) { - ScreenComponents::drawBattery(renderer, orientedMarginLeft, textY); + ScreenComponents::drawBattery(renderer, orientedMarginLeft, textY, true, isCharging()); } if (showTitle) {