summaryrefslogtreecommitdiff
path: root/src/battery/battery.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-08-28 13:26:53 +1000
committerjacqueline <me@jacqueline.id.au>2023-08-28 13:26:53 +1000
commit3a0c42f9240eedfbc6a1e94ad3a59c52664fb5b5 (patch)
tree0505db40de6fceaf5829548ef86f4cb53b739bcb /src/battery/battery.cpp
parenta1327763ab70dbf4996e032dd227de368f78f4ad (diff)
downloadtangara-fw-3a0c42f9240eedfbc6a1e94ad3a59c52664fb5b5.tar.gz
Move battery measurement to its own class
Diffstat (limited to 'src/battery/battery.cpp')
-rw-r--r--src/battery/battery.cpp101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/battery/battery.cpp b/src/battery/battery.cpp
new file mode 100644
index 00000000..d73f4f29
--- /dev/null
+++ b/src/battery/battery.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "battery.hpp"
+
+#include <cstdint>
+
+#include "adc.hpp"
+#include "event_queue.hpp"
+#include "freertos/portmacro.h"
+#include "samd.hpp"
+#include "system_events.hpp"
+
+namespace battery {
+
+static const TickType_t kBatteryCheckPeriod = pdMS_TO_TICKS(60 * 1000);
+
+/*
+ * Battery voltage, in millivolts, at which the battery charger IC will stop
+ * charging.
+ */
+static const uint32_t kFullChargeMilliVolts = 4200;
+
+/*
+ * Battery voltage, in millivolts, at which *we* will consider the battery to
+ * be completely discharged. This is intentionally higher than the charger IC
+ * cut-off and the protection on the battery itself; we want to make sure we
+ * finish up and have everything unmounted and snoozing before the BMS cuts us
+ * off.
+ */
+static const uint32_t kEmptyChargeMilliVolts = 3200; // BMS limit is 3100.
+
+using ChargeStatus = drivers::Samd::ChargeStatus;
+
+void check_voltage_cb(TimerHandle_t timer) {
+ Battery* instance = reinterpret_cast<Battery*>(pvTimerGetTimerID(timer));
+ instance->Update();
+}
+
+Battery::Battery(drivers::Samd* samd, drivers::AdcBattery* adc)
+ : samd_(samd), adc_(adc) {
+ timer_ = xTimerCreate("BATTERY", kBatteryCheckPeriod, true, this,
+ check_voltage_cb);
+ xTimerStart(timer_, portMAX_DELAY);
+ Update();
+}
+
+Battery::~Battery() {
+ xTimerStop(timer_, portMAX_DELAY);
+ xTimerDelete(timer_, portMAX_DELAY);
+}
+
+auto Battery::Update() -> void {
+ std::lock_guard<std::mutex> lock{state_mutex_};
+
+ auto charge_state = samd_->GetChargeStatus();
+ if (!charge_state || *charge_state == ChargeStatus::kNoBattery) {
+ if (state_) {
+ EmitEvent();
+ }
+ state_.reset();
+ return;
+ }
+ // FIXME: So what we *should* do here is measure the actual real-life
+ // time from full battery -> empty battery, store it in NVS, then rely on
+ // that. If someone could please do this, it would be lovely. Thanks!
+ uint32_t mV = std::max(adc_->Millivolts(), kEmptyChargeMilliVolts);
+ uint_fast8_t percent = static_cast<uint_fast8_t>(std::min<double>(
+ std::max<double>(0.0, mV - kEmptyChargeMilliVolts) /
+ (kFullChargeMilliVolts - kEmptyChargeMilliVolts) * 100.0,
+ 100.0));
+
+ bool is_charging = *charge_state == ChargeStatus::kChargingRegular ||
+ *charge_state == ChargeStatus::kChargingFast ||
+ *charge_state == ChargeStatus::kFullCharge;
+
+ if (!state_ || state_->is_charging != is_charging ||
+ state_->percent != percent) {
+ EmitEvent();
+ }
+
+ state_ = BatteryState{
+ .percent = percent,
+ .is_charging = is_charging,
+ };
+}
+
+auto Battery::State() -> std::optional<BatteryState> {
+ std::lock_guard<std::mutex> lock{state_mutex_};
+ return state_;
+}
+
+auto Battery::EmitEvent() -> void {
+ events::System().Dispatch(system_fsm::BatteryStateChanged{});
+ events::Ui().Dispatch(system_fsm::BatteryStateChanged{});
+}
+
+} // namespace battery