mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
add ringbuffer for battery percentage smoothing
This commit is contained in:
parent
f67c544e16
commit
ee806390f3
40
src/Battery.cpp
Normal file
40
src/Battery.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "Battery.h"
|
||||
|
||||
void BatteryPercentageRingBuffer::init(uint16_t v) {
|
||||
for (size_t i = 0; i < MAX_SAMPLES; i++) {
|
||||
buf[i] = v;
|
||||
}
|
||||
sum = v * MAX_SAMPLES;
|
||||
prev_val = v;
|
||||
head = 0;
|
||||
}
|
||||
|
||||
void BatteryPercentageRingBuffer::update(uint16_t v) {
|
||||
// Previous percentage is set to 161 only if buffer was constructed but not initialized yet
|
||||
if (!prev_val) {
|
||||
init(v);
|
||||
}
|
||||
|
||||
// Recalculate rolling sum
|
||||
sum -= buf[head];
|
||||
buf[head] = v;
|
||||
sum += v;
|
||||
|
||||
// Shift head
|
||||
head++;
|
||||
if (head >= MAX_SAMPLES) head = 0;
|
||||
}
|
||||
|
||||
uint16_t BatteryPercentageRingBuffer::evaluate() {
|
||||
// We 'round' (only works for pos numbers but oh well good enough for battery percentages) to an int
|
||||
float avg = (sum / MAX_SAMPLES) + 0.5;
|
||||
uint16_t new_val = (int)avg;
|
||||
|
||||
// Battery percentage should not increse when not charging so we just cap it to be lower than the last value
|
||||
if (new_val < prev_val) {
|
||||
prev_val = new_val;
|
||||
return new_val;
|
||||
} else {
|
||||
return prev_val;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,21 @@
|
||||
#pragma once
|
||||
#include <BatteryMonitor.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#define BAT_GPIO0 0 // Battery voltage
|
||||
|
||||
static BatteryMonitor battery(BAT_GPIO0);
|
||||
|
||||
struct BatteryPercentageRingBuffer {
|
||||
static constexpr uint16_t MAX_SAMPLES = 10;
|
||||
|
||||
uint16_t buf[MAX_SAMPLES];
|
||||
uint16_t head = 0;
|
||||
uint16_t sum = 0;
|
||||
uint16_t prev_val = 0;
|
||||
|
||||
void init(uint16_t value);
|
||||
void update(uint16_t value);
|
||||
uint16_t evaluate();
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "ScreenComponents.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <GfxRenderer.h>
|
||||
|
||||
#include <cstdint>
|
||||
@ -8,10 +9,24 @@
|
||||
#include "Battery.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
BatteryPercentageRingBuffer ScreenComponents::batteryBuffer;
|
||||
|
||||
void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top,
|
||||
const bool showPercentage) {
|
||||
const bool charging = (digitalRead(20) == HIGH);
|
||||
|
||||
// Left aligned battery icon and percentage
|
||||
const uint16_t percentage = battery.readPercentage();
|
||||
uint16_t percentage = battery.readPercentage();
|
||||
|
||||
if (charging) {
|
||||
// If charging reinitialize buffer with current percentage and display this value
|
||||
batteryBuffer.init(percentage);
|
||||
} else {
|
||||
// Else update buffer with new percentage and return smoothed validated percentage to display
|
||||
batteryBuffer.update(percentage);
|
||||
percentage = batteryBuffer.evaluate();
|
||||
}
|
||||
|
||||
const auto percentageText = showPercentage ? std::to_string(percentage) + "%" : "";
|
||||
renderer.drawText(SMALL_FONT_ID, left + 20, top, percentageText.c_str());
|
||||
|
||||
@ -34,12 +49,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);
|
||||
}
|
||||
}
|
||||
|
||||
ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message) {
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "Battery.h"
|
||||
|
||||
class GfxRenderer;
|
||||
|
||||
struct TabInfo {
|
||||
@ -15,6 +17,8 @@ class ScreenComponents {
|
||||
public:
|
||||
static const int BOOK_PROGRESS_BAR_HEIGHT = 4;
|
||||
|
||||
static BatteryPercentageRingBuffer batteryBuffer;
|
||||
|
||||
struct PopupLayout {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user