battery percentage

This commit is contained in:
2025-10-25 23:31:11 +02:00
parent 96f5b1f0ee
commit 961da2ba33
7 changed files with 227 additions and 211 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>
@@ -446,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;

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,55 +40,54 @@ 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:
[[nodiscard]] bool readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) override; [[nodiscard]] bool readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) override;
void writeUint32(std::string_view ns, std::string_view key, std::uint32_t value) override; void writeUint32(std::string_view ns, std::string_view key, std::uint32_t value) override;
private: private:
std::unordered_map<std::string, std::uint32_t> data; std::unordered_map<std::string, std::uint32_t> data;
static std::string composeKey(std::string_view ns, std::string_view key); static std::string composeKey(std::string_view ns, std::string_view key);
}; };
class DesktopRandom final : public cardboy::sdk::IRandom { class DesktopRandom final : public cardboy::sdk::IRandom {
public: public:
DesktopRandom(); DesktopRandom();
[[nodiscard]] std::uint32_t nextUint32() override; [[nodiscard]] std::uint32_t nextUint32() override;
private: private:
std::mt19937 rng; std::mt19937 rng;
std::uniform_int_distribution<std::uint32_t> dist; std::uniform_int_distribution<std::uint32_t> dist;
}; };
class DesktopHighResClock final : public cardboy::sdk::IHighResClock { class DesktopHighResClock final : public cardboy::sdk::IHighResClock {
public: public:
DesktopHighResClock(); DesktopHighResClock();
[[nodiscard]] std::uint64_t micros() override; [[nodiscard]] std::uint64_t micros() override;
private: private:
const std::chrono::steady_clock::time_point start; const std::chrono::steady_clock::time_point start;
}; };
class DesktopFilesystem final : public cardboy::sdk::IFilesystem { class DesktopFilesystem final : public cardboy::sdk::IFilesystem {
public: public:
DesktopFilesystem(); DesktopFilesystem();
bool mount() override; bool mount() override;
[[nodiscard]] bool isMounted() const override { return mounted; } [[nodiscard]] bool isMounted() const override { return mounted; }
[[nodiscard]] std::string basePath() const override { return basePathPath.string(); } [[nodiscard]] std::string basePath() const override { return basePathPath.string(); }
private: private:
std::filesystem::path basePathPath; std::filesystem::path basePathPath;
bool mounted = false; bool mounted = false;
}; };
class DesktopNotificationCenter final : public cardboy::sdk::INotificationCenter { class DesktopNotificationCenter final : public cardboy::sdk::INotificationCenter {
public: public:
void pushNotification(Notification notification) override; void pushNotification(Notification notification) override;
[[nodiscard]] std::uint32_t revision() const override; [[nodiscard]] std::uint32_t revision() const override;
[[nodiscard]] std::vector<Notification> recent(std::size_t limit) const override; [[nodiscard]] std::vector<Notification> recent(std::size_t limit) const override;
@@ -97,38 +96,38 @@ public:
void removeById(std::uint64_t id) override; void removeById(std::uint64_t id) override;
void removeByExternalId(std::uint64_t externalId) override; void removeByExternalId(std::uint64_t externalId) override;
private: private:
static constexpr std::size_t kMaxEntries = 8; static constexpr std::size_t kMaxEntries = 8;
mutable std::mutex mutex; mutable std::mutex mutex;
std::vector<Notification> entries; std::vector<Notification> entries;
std::uint64_t nextId = 1; std::uint64_t nextId = 1;
std::uint32_t revisionCounter = 0; std::uint32_t revisionCounter = 0;
}; };
class DesktopLoopHooks final : public cardboy::sdk::ILoopHooks { class DesktopLoopHooks final : public cardboy::sdk::ILoopHooks {
public: public:
explicit DesktopLoopHooks(DesktopRuntime& owner); explicit DesktopLoopHooks(DesktopRuntime& owner);
void onLoopIteration() override; void onLoopIteration() override;
private: private:
DesktopRuntime& runtime; DesktopRuntime& runtime;
}; };
class DesktopEventBus final : public cardboy::sdk::IEventBus { class DesktopEventBus final : public cardboy::sdk::IEventBus {
public: public:
void post(const cardboy::sdk::AppEvent& event) override; void post(const cardboy::sdk::AppEvent& event) override;
std::optional<cardboy::sdk::AppEvent> pop(std::optional<std::uint32_t> timeout_ms = std::nullopt) override; std::optional<cardboy::sdk::AppEvent> pop(std::optional<std::uint32_t> timeout_ms = std::nullopt) override;
private: private:
std::mutex mutex; std::mutex mutex;
std::condition_variable cv; std::condition_variable cv;
std::deque<cardboy::sdk::AppEvent> queue; std::deque<cardboy::sdk::AppEvent> queue;
}; };
class DesktopTimerService final : public cardboy::sdk::ITimerService { class DesktopTimerService final : public cardboy::sdk::ITimerService {
public: public:
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& eventBus); DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& eventBus);
~DesktopTimerService() override; ~DesktopTimerService() override;
@@ -136,7 +135,7 @@ public:
void cancelTimer(cardboy::sdk::AppTimerHandle handle) override; void cancelTimer(cardboy::sdk::AppTimerHandle handle) override;
void cancelAllTimers() override; void cancelAllTimers() override;
private: private:
struct TimerRecord { struct TimerRecord {
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer; cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
std::chrono::steady_clock::time_point due; std::chrono::steady_clock::time_point due;
@@ -157,26 +156,26 @@ private:
bool stopWorker = false; bool stopWorker = false;
std::thread worker; std::thread worker;
cardboy::sdk::AppTimerHandle nextHandle = 1; cardboy::sdk::AppTimerHandle nextHandle = 1;
}; };
class DesktopAppServiceProvider final : public cardboy::sdk::IAppServiceProvider { class DesktopAppServiceProvider final : public cardboy::sdk::IAppServiceProvider {
public: public:
DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus); DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus);
[[nodiscard]] std::unique_ptr<cardboy::sdk::AppScopedServices> [[nodiscard]] std::unique_ptr<cardboy::sdk::AppScopedServices>
createScopedServices(std::uint64_t generation) override; createScopedServices(std::uint64_t generation) override;
private: private:
struct ScopedServices final : cardboy::sdk::AppScopedServices { struct ScopedServices final : cardboy::sdk::AppScopedServices {
std::unique_ptr<DesktopTimerService> ownedTimer; std::unique_ptr<DesktopTimerService> ownedTimer;
}; };
DesktopRuntime& runtime; DesktopRuntime& runtime;
cardboy::sdk::IEventBus& eventBus; cardboy::sdk::IEventBus& eventBus;
}; };
class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> { class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> {
public: public:
explicit DesktopFramebuffer(DesktopRuntime& runtime); explicit DesktopFramebuffer(DesktopRuntime& runtime);
[[nodiscard]] int width_impl() const; [[nodiscard]] int width_impl() const;
@@ -187,36 +186,36 @@ public:
void sendFrame_impl(bool clearAfterSend); void sendFrame_impl(bool clearAfterSend);
[[nodiscard]] bool frameInFlight_impl() const { return false; } [[nodiscard]] bool frameInFlight_impl() const { return false; }
private: private:
DesktopRuntime& runtime; DesktopRuntime& runtime;
}; };
class DesktopInput final : public cardboy::sdk::InputFacade<DesktopInput> { class DesktopInput final : public cardboy::sdk::InputFacade<DesktopInput> {
public: public:
explicit DesktopInput(DesktopRuntime& runtime); explicit DesktopInput(DesktopRuntime& runtime);
cardboy::sdk::InputState readState_impl(); cardboy::sdk::InputState readState_impl();
void handleKey(sf::Keyboard::Key key, bool pressed); void handleKey(sf::Keyboard::Key key, bool pressed);
private: private:
DesktopRuntime& runtime; DesktopRuntime& runtime;
cardboy::sdk::InputState state{}; cardboy::sdk::InputState state{};
}; };
class DesktopClock final : public cardboy::sdk::ClockFacade<DesktopClock> { class DesktopClock final : public cardboy::sdk::ClockFacade<DesktopClock> {
public: public:
explicit DesktopClock(DesktopRuntime& runtime); explicit DesktopClock(DesktopRuntime& runtime);
std::uint32_t millis_impl(); std::uint32_t millis_impl();
void sleep_ms_impl(std::uint32_t ms); void sleep_ms_impl(std::uint32_t ms);
private: private:
DesktopRuntime& runtime; DesktopRuntime& runtime;
const std::chrono::steady_clock::time_point start; const std::chrono::steady_clock::time_point start;
}; };
class DesktopRuntime { class DesktopRuntime {
public: public:
DesktopRuntime(); DesktopRuntime();
cardboy::sdk::Services& serviceRegistry(); cardboy::sdk::Services& serviceRegistry();
@@ -230,7 +229,7 @@ public:
DesktopInput input; DesktopInput input;
DesktopClock clock; DesktopClock clock;
private: private:
friend class DesktopFramebuffer; friend class DesktopFramebuffer;
friend class DesktopInput; friend class DesktopInput;
friend class DesktopClock; friend class DesktopClock;
@@ -257,16 +256,16 @@ private:
DesktopNotificationCenter notificationService; DesktopNotificationCenter notificationService;
DesktopLoopHooks loopHooksService; DesktopLoopHooks loopHooksService;
cardboy::sdk::Services services{}; cardboy::sdk::Services services{};
}; };
struct Backend { struct Backend {
using Framebuffer = DesktopFramebuffer; using Framebuffer = DesktopFramebuffer;
using Input = DesktopInput; using Input = DesktopInput;
using Clock = DesktopClock; using Clock = DesktopClock;
}; };
} // namespace cardboy::backend::desktop } // namespace cardboy::backend::desktop
namespace cardboy::backend { namespace cardboy::backend {
using DesktopBackend = desktop::Backend; using DesktopBackend = desktop::Backend;
} // namespace cardboy::backend } // namespace cardboy::backend

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);
} }