Compare commits

...

5 Commits

Author SHA1 Message Date
961da2ba33 battery percentage 2025-10-25 23:31:11 +02:00
96f5b1f0ee fix status bar 2025-10-25 23:13:01 +02:00
f5a780c1c8 better lockscreen notifications 2025-10-25 23:10:13 +02:00
5c3cdaaae4 better lockscreen progress 2025-10-25 23:00:44 +02:00
f814c45532 remove font "normalization" 2025-10-25 22:51:02 +02:00
9 changed files with 249 additions and 255 deletions

View File

@@ -18,6 +18,7 @@ public:
float get_voltage() const; float get_voltage() const;
float get_charge() const; float get_charge() const;
float get_current() const; float get_current() const;
float get_percentage() const;
void pooler(); // FIXME: void pooler(); // FIXME:
private: private:
@@ -33,6 +34,7 @@ private:
volatile float _voltage; volatile float _voltage;
volatile float _current; volatile float _current;
volatile float _charge; volatile float _charge;
volatile float _percentage;
TaskHandle_t _pooler_task; TaskHandle_t _pooler_task;
}; };

View File

@@ -48,6 +48,8 @@ static constexpr uint16_t DesignCapMah = 180; // 100mOhm
constexpr float mahToCap(float mah) { return mah * (1000.0 / 5.0) * RSense; } constexpr float mahToCap(float mah) { return mah * (1000.0 / 5.0) * RSense; }
constexpr float capToMah(uint16_t cap) { return cap * (5.0 / 1000.0) / RSense; } constexpr float capToMah(uint16_t cap) { return cap * (5.0 / 1000.0) / RSense; }
// lsb is 1/256%
constexpr float regToPercent(uint16_t reg) { return static_cast<float>(reg) / 256.0f; }
constexpr float regToCurrent(uint16_t reg) { constexpr float regToCurrent(uint16_t reg) {
return static_cast<float>(static_cast<int16_t>(reg)) * 0.0015625f / RSense; // Convert to mA return static_cast<float>(static_cast<int16_t>(reg)) * 0.0015625f / RSense; // Convert to mA
} }
@@ -103,6 +105,7 @@ void BatMon::pooler() {
_charge = capToMah(ReadRegister(0x05)); _charge = capToMah(ReadRegister(0x05));
_current = regToCurrent(ReadRegister(0x0B)); _current = regToCurrent(ReadRegister(0x0B));
_voltage = regToVoltage(ReadRegister(0x09)); _voltage = regToVoltage(ReadRegister(0x09));
_percentage = regToPercent(ReadRegister(0x06));
vTaskDelay(pdMS_TO_TICKS(10000)); vTaskDelay(pdMS_TO_TICKS(10000));
if (_voltage < 3.0f) { if (_voltage < 3.0f) {
Shutdowner::get().shutdown(); Shutdowner::get().shutdown();
@@ -113,3 +116,4 @@ void BatMon::pooler() {
float BatMon::get_voltage() const { return _voltage; } float BatMon::get_voltage() const { return _voltage; }
float BatMon::get_charge() const { return _charge; } float BatMon::get_charge() const { return _charge; }
float BatMon::get_current() const { return _current; } float BatMon::get_current() const { return _current; }
float BatMon::get_percentage() const { return _percentage; }

View File

@@ -78,6 +78,7 @@ public:
[[nodiscard]] float voltage() const override { return BatMon::get().get_voltage(); } [[nodiscard]] float voltage() const override { return BatMon::get().get_voltage(); }
[[nodiscard]] float charge() const override { return BatMon::get().get_charge(); } [[nodiscard]] float charge() const override { return BatMon::get().get_charge(); }
[[nodiscard]] float current() const override { return BatMon::get().get_current(); } [[nodiscard]] float current() const override { return BatMon::get().get_current(); }
[[nodiscard]] float percentage() const override { return BatMon::get().get_percentage(); }
}; };
class EspRuntime::StorageService final : public cardboy::sdk::IStorage { class EspRuntime::StorageService final : public cardboy::sdk::IStorage {

View File

@@ -7,6 +7,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cmath>
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include <string> #include <string>
@@ -31,12 +32,12 @@ using Framebuffer = typename AppContext::Framebuffer;
using Clock = typename AppContext::Clock; using Clock = typename AppContext::Clock;
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowUpGlyph{ constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowUpGlyph{
0b00011000, 0b00111100, 0b01111110, 0b11111111, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00010000, 0b00111000, 0b01111100, 0b11111110, 0b00010000, 0b00010000, 0b00010000, 0b00010000,
0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00000000, 0b00000000}; 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00000000, 0b00000000};
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowDownGlyph{ constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowDownGlyph{
0b00000000, 0b00000000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00000000, 0b00000000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b00010000,
0b00011000, 0b00011000, 0b00011000, 0b11111111, 0b01111110, 0b00111100, 0b00011000, 0b00000000}; 0b00010000, 0b00010000, 0b00010000, 0b11111110, 0b01111100, 0b00111000, 0b00010000, 0b00000000};
struct TimeSnapshot { struct TimeSnapshot {
bool hasWallTime = false; bool hasWallTime = false;
@@ -130,12 +131,14 @@ private:
if (!notifications.empty() && (upPressed || downPressed)) { if (!notifications.empty() && (upPressed || downPressed)) {
const std::size_t count = notifications.size(); const std::size_t count = notifications.size();
lastNotificationInteractionMs = clock.millis(); lastNotificationInteractionMs = clock.millis();
navPressed = true;
if (count > 1) { if (count > 1) {
if (upPressed) if (upPressed && selectedNotification > 0) {
selectedNotification = (selectedNotification + count - 1) % count; selectedNotification--;
else if (downPressed) navPressed = true;
selectedNotification = (selectedNotification + 1) % count; } else if (downPressed && selectedNotification < count - 1) {
selectedNotification++;
navPressed = true;
}
} }
} }
@@ -444,6 +447,18 @@ private:
framebuffer.frameReady(); framebuffer.frameReady();
if (auto* battery = context.battery(); battery && battery->hasData()) {
const float percentage = battery->percentage();
if (std::isfinite(percentage) && percentage >= 0.0f) {
char pct[8];
std::snprintf(pct, sizeof(pct), "%.0f%%", static_cast<double>(percentage));
const int pctWidth = font16x8::measureText(pct, 1, 1);
const int pctX = framebuffer.width() - pctWidth - 4;
const int pctY = 4;
font16x8::drawText(framebuffer, pctX, pctY, pct, 1, true, 1);
}
}
const int scaleTime = 4; const int scaleTime = 4;
const int scaleSeconds = 2; const int scaleSeconds = 2;
const int scaleSmall = 1; const int scaleSmall = 1;
@@ -500,7 +515,9 @@ private:
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing; const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
const int arrowX = counterX + (counterWidth - arrowsTotalWide) / 2; const int arrowX = counterX + (counterWidth - arrowsTotalWide) / 2;
const int arrowY = cardMarginTop + cardPadding + textLineHeight + 1; const int arrowY = cardMarginTop + cardPadding + textLineHeight + 1;
if (selectedNotification > 0)
drawArrow(framebuffer, arrowX, arrowY, true, scaleSmall); drawArrow(framebuffer, arrowX, arrowY, true, scaleSmall);
if (selectedNotification < notifications.size() - 1)
drawArrow(framebuffer, arrowX + arrowWidth + arrowSpacing, arrowY, false, scaleSmall); drawArrow(framebuffer, arrowX + arrowWidth + arrowSpacing, arrowY, false, scaleSmall);
const int arrowHeight = font16x8::kGlyphHeight * scaleSmall; const int arrowHeight = font16x8::kGlyphHeight * scaleSmall;
cardHeight = std::max(cardHeight, arrowY + arrowHeight - cardMarginTop); cardHeight = std::max(cardHeight, arrowY + arrowHeight - cardMarginTop);
@@ -524,28 +541,13 @@ private:
const int summaryX = (framebuffer.width() - summaryWidth) / 2; const int summaryX = (framebuffer.width() - summaryWidth) / 2;
const int summaryY = cardMarginTop; const int summaryY = cardMarginTop;
font16x8::drawText(framebuffer, summaryX, summaryY, summary, scaleSmall, true, 1); font16x8::drawText(framebuffer, summaryX, summaryY, summary, scaleSmall, true, 1);
if (notifications.size() > 1) {
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
const int arrowSpacing = std::max(1, scaleSmall);
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
const int arrowX = (framebuffer.width() - arrowsTotalWide) / 2;
const int arrowY = summaryY + textLineHeight + 1;
drawArrow(framebuffer, arrowX, arrowY, true, scaleSmall);
drawArrow(framebuffer, arrowX + arrowWidth + arrowSpacing, arrowY, false, scaleSmall);
const int arrowHeight = font16x8::kGlyphHeight * scaleSmall;
cardHeight = std::max(cardHeight, arrowY + arrowHeight - cardMarginTop);
}
} }
} }
const int defaultTimeY = (framebuffer.height() - font16x8::kGlyphHeight * scaleTime) / 2 - 8; const int defaultTimeY = (framebuffer.height() - font16x8::kGlyphHeight * scaleTime) / 2 - 8;
int timeY = defaultTimeY;
if (cardHeight > 0)
timeY = cardMarginTop + cardHeight + 16;
const int minTimeY = (cardHeight > 0) ? (cardMarginTop + cardHeight + 12) : 16; const int minTimeY = (cardHeight > 0) ? (cardMarginTop + cardHeight + 12) : 16;
const int maxTimeY = std::max(minTimeY, framebuffer.height() - font16x8::kGlyphHeight * scaleTime - 48); const int maxTimeY = std::max(minTimeY, framebuffer.height() - font16x8::kGlyphHeight * scaleTime - 48);
timeY = std::clamp(timeY, minTimeY, maxTimeY); const int timeY = std::clamp(defaultTimeY, minTimeY, maxTimeY);
char hoursMinutes[6]; char hoursMinutes[6];
std::snprintf(hoursMinutes, sizeof(hoursMinutes), "%02d:%02d", snap.hour24, snap.minute); std::snprintf(hoursMinutes, sizeof(hoursMinutes), "%02d:%02d", snap.hour24, snap.minute);
@@ -562,24 +564,18 @@ private:
const std::string dateLine = formatDate(snap); const std::string dateLine = formatDate(snap);
drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleTime + 16, dateLine, scaleSmall, 1); drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleTime + 16, dateLine, scaleSmall, 1);
const std::string instruction = holdActive ? "KEEP HOLDING A+SELECT" : "HOLD A+SELECT"; const std::string instruction = "HOLD A+SELECT TO UNLOCK";
const int instructionWidth = font16x8::measureText(instruction, scaleSmall, 1); const int instructionWidth = font16x8::measureText(instruction, scaleSmall, 1);
const int barHeight = 14; const int barHeight = 18;
const int barY = framebuffer.height() - 24; const int barY = framebuffer.height() - 30;
const int textY = barY + (barHeight - textLineHeight) / 2; const int textY = barY + (barHeight - textLineHeight) / 2 + 1;
const int textX = 8; drawCenteredText(framebuffer, textY, instruction, scaleSmall, 1);
font16x8::drawText(framebuffer, textX, textY, instruction, scaleSmall, true, 1);
int barX = textX + instructionWidth + 12; const int barWidth = framebuffer.width() - 64;
int barWidth = framebuffer.width() - barX - 8; const int barX = 32;
if (barWidth < 40) {
barWidth = 40;
barX = std::min(barX, framebuffer.width() - barWidth - 8);
}
drawRectOutline(framebuffer, barX, barY, barWidth, barHeight);
if (holdActive || holdProgressMs > 0) { if (holdActive || holdProgressMs > 0) {
drawRectOutline(framebuffer, barX, barY, barWidth, barHeight);
const int innerWidth = barWidth - 2; const int innerWidth = barWidth - 2;
const int innerHeight = barHeight - 2; const int innerHeight = barHeight - 2;
const float ratio = std::clamp(holdProgressMs / static_cast<float>(kUnlockHoldMs), 0.0f, 1.0f); const float ratio = std::clamp(holdProgressMs / static_cast<float>(kUnlockHoldMs), 0.0f, 1.0f);

View File

@@ -39,6 +39,7 @@ public:
[[nodiscard]] virtual float voltage() const { return 0.0f; } [[nodiscard]] virtual float voltage() const { return 0.0f; }
[[nodiscard]] virtual float charge() const { return 0.0f; } [[nodiscard]] virtual float charge() const { return 0.0f; }
[[nodiscard]] virtual float current() const { return 0.0f; } [[nodiscard]] virtual float current() const { return 0.0f; }
[[nodiscard]] virtual float percentage() const { return 0.0f; }
}; };
class IStorage { class IStorage {

View File

@@ -40,7 +40,6 @@ public:
class DesktopBattery final : public cardboy::sdk::IBatteryMonitor { class DesktopBattery final : public cardboy::sdk::IBatteryMonitor {
public: public:
[[nodiscard]] bool hasData() const override { return false; } [[nodiscard]] bool hasData() const override { return false; }
};
class DesktopStorage final : public cardboy::sdk::IStorage { class DesktopStorage final : public cardboy::sdk::IStorage {
public: public:

View File

@@ -14,8 +14,6 @@ constexpr unsigned char kFallbackChar = '?';
inline unsigned char normalizeChar(char ch) { inline unsigned char normalizeChar(char ch) {
unsigned char uc = static_cast<unsigned char>(ch); unsigned char uc = static_cast<unsigned char>(ch);
if (uc >= 'a' && uc <= 'z')
uc = static_cast<unsigned char>(std::toupper(static_cast<unsigned char>(uc)));
if (!std::isprint(static_cast<unsigned char>(uc))) if (!std::isprint(static_cast<unsigned char>(uc)))
return kFallbackChar; return kFallbackChar;
return uc; return uc;

View File

@@ -56,9 +56,6 @@ private:
fb.drawPixel(x, y, true); fb.drawPixel(x, y, true);
} }
for (int x = 0; x < width; ++x)
fb.drawPixel(x, 0, false);
const int textY = 1; const int textY = 1;
const int bottomSeparatorY = textY + font16x8::kGlyphHeight + 1; const int bottomSeparatorY = textY + font16x8::kGlyphHeight + 1;
if (bottomSeparatorY < fillHeight) { if (bottomSeparatorY < fillHeight) {

View File

@@ -52,14 +52,10 @@ std::string StatusBar::prepareRightText() const {
if (_services->battery && _services->battery->hasData()) { if (_services->battery && _services->battery->hasData()) {
const float current = _services->battery->current(); const float current = _services->battery->current();
const float chargeMah = _services->battery->charge(); const float chargeMah = _services->battery->charge();
const float fallbackV = _services->battery->voltage(); const float percentage = _services->battery->percentage();
char buf[64]; char buf[64];
if (std::isfinite(current) && std::isfinite(chargeMah)) { std::snprintf(buf, sizeof(buf), "%.2fmA %.2fmAh %.0f%%", static_cast<double>(current),
std::snprintf(buf, sizeof(buf), "cur %.2fmA chr %.2fmAh", static_cast<double>(current), static_cast<double>(chargeMah), static_cast<double>(percentage));
static_cast<double>(chargeMah));
} else {
std::snprintf(buf, sizeof(buf), "vol %.2fV", static_cast<double>(fallbackV));
}
right.assign(buf); right.assign(buf);
} }