From 0cc75366848e9205ac88884afcc128925024ccec Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 24 Jul 2024 15:29:45 +1000 Subject: Add a settings screen with power+battery info Mostly for debugging, but also u can toggle fast charging off and on now --- src/drivers/include/drivers/nvs.hpp | 4 ++ src/drivers/include/drivers/samd.hpp | 13 ++++-- src/drivers/nvs.cpp | 12 ++++++ src/drivers/samd.cpp | 74 ++++++++++++++++++++++++++++----- src/tangara/app_console/app_console.cpp | 21 +--------- src/tangara/battery/battery.cpp | 2 + src/tangara/battery/battery.hpp | 1 + src/tangara/system_fsm/booting.cpp | 6 +-- src/tangara/ui/ui_fsm.cpp | 34 ++++++++++++--- src/tangara/ui/ui_fsm.hpp | 2 + 10 files changed, 126 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/drivers/include/drivers/nvs.hpp b/src/drivers/include/drivers/nvs.hpp index 8eb28cc9..e298ffc3 100644 --- a/src/drivers/include/drivers/nvs.hpp +++ b/src/drivers/include/drivers/nvs.hpp @@ -90,6 +90,9 @@ class NvsStorage { auto LraCalibration() -> std::optional; auto LraCalibration(const LraData&) -> void; + auto FastCharge() -> bool; + auto FastCharge(bool) -> void; + auto PreferredBluetoothDevice() -> std::optional; auto PreferredBluetoothDevice(std::optional) -> void; @@ -150,6 +153,7 @@ class NvsStorage { Setting display_rows_; Setting haptic_motor_type_; Setting lra_calibration_; + Setting fast_charge_; Setting brightness_; Setting sensitivity_; diff --git a/src/drivers/include/drivers/samd.hpp b/src/drivers/include/drivers/samd.hpp index 897e78d6..ff479225 100644 --- a/src/drivers/include/drivers/samd.hpp +++ b/src/drivers/include/drivers/samd.hpp @@ -10,6 +10,7 @@ #include #include +#include "drivers/nvs.hpp" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -17,9 +18,7 @@ namespace drivers { class Samd { public: - static auto Create() -> Samd* { return new Samd(); } - - Samd(); + Samd(NvsStorage& nvs); ~Samd(); auto Version() -> std::string; @@ -37,8 +36,14 @@ class Samd { kChargingFast, // The battery is full charged, and we are still plugged in. kFullCharge, + // Charging failed. + kFault, + // The battery status returned isn't a known enum value. + kUnknown, }; + static auto chargeStatusToString(ChargeStatus) -> std::string; + auto GetChargeStatus() -> std::optional; auto UpdateChargeStatus() -> void; @@ -68,6 +73,8 @@ class Samd { Samd& operator=(const Samd&) = delete; private: + NvsStorage& nvs_; + uint8_t version_; std::optional charge_status_; UsbStatus usb_status_; diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index e3c4aa06..6fac8c61 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -40,6 +40,7 @@ static constexpr char kKeyDisplayRows[] = "disprows"; static constexpr char kKeyHapticMotorType[] = "hapticmtype"; static constexpr char kKeyLraCalibration[] = "lra_cali"; static constexpr char kKeyDbAutoIndex[] = "dbautoindex"; +static constexpr char kKeyFastCharge[] = "fastchg"; static auto nvs_get_string(nvs_handle_t nvs, const char* key) -> std::optional { @@ -239,6 +240,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle) display_rows_(kKeyDisplayRows), haptic_motor_type_(kKeyHapticMotorType), lra_calibration_(kKeyLraCalibration), + fast_charge_(kKeyFastCharge), brightness_(kKeyBrightness), sensitivity_(kKeyScrollSensitivity), amp_max_vol_(kKeyAmpMaxVolume), @@ -444,6 +446,16 @@ auto NvsStorage::OutputMode(Output out) -> void { nvs_commit(handle_); } +auto NvsStorage::FastCharge() -> bool { + std::lock_guard lock{mutex_}; + return fast_charge_.get().value_or(true); +} + +auto NvsStorage::FastCharge(bool en) -> void { + std::lock_guard lock{mutex_}; + fast_charge_.set(en); +} + auto NvsStorage::ScreenBrightness() -> uint_fast8_t { std::lock_guard lock{mutex_}; return std::clamp(brightness_.get().value_or(50), 0, 100); diff --git a/src/drivers/samd.cpp b/src/drivers/samd.cpp index e4aa73ad..c2308760 100644 --- a/src/drivers/samd.cpp +++ b/src/drivers/samd.cpp @@ -5,11 +5,13 @@ */ #include "drivers/samd.hpp" +#include #include #include #include +#include "drivers/nvs.hpp" #include "esp_err.h" #include "esp_log.h" #include "hal/gpio_types.h" @@ -32,7 +34,29 @@ namespace drivers { static constexpr gpio_num_t kIntPin = GPIO_NUM_35; -Samd::Samd() { +auto Samd::chargeStatusToString(ChargeStatus status) -> std::string { + switch (status) { + case ChargeStatus::kNoBattery: + return "no_battery"; + case ChargeStatus::kBatteryCritical: + return "critical"; + case ChargeStatus::kDischarging: + return "discharging"; + case ChargeStatus::kChargingRegular: + return "charge_regular"; + case ChargeStatus::kChargingFast: + return "charge_fast"; + case ChargeStatus::kFullCharge: + return "full_charge"; + case ChargeStatus::kFault: + return "fault"; + case ChargeStatus::kUnknown: + default: + return "unknown"; + } +} + +Samd::Samd(NvsStorage& nvs) : nvs_(nvs) { gpio_set_direction(kIntPin, GPIO_MODE_INPUT); // Being able to interface with the SAMD properly is critical. To ensure we @@ -51,7 +75,7 @@ Samd::Samd() { UpdateChargeStatus(); UpdateUsbStatus(); - SetFastChargeEnabled(true); + SetFastChargeEnabled(nvs.FastCharge()); } Samd::~Samd() {} @@ -78,16 +102,38 @@ auto Samd::UpdateChargeStatus() -> void { return; } - // FIXME: Ideally we should be using the three 'charge status' bits to work - // out whether we're actually charging, or if we've got a full charge, - // critically low charge, etc. + // Lower two bits are the usb power status, next three are the BMS status. + // See 'gpio.c' in the SAMD21 firmware for how these bits get packed. + uint8_t charge_state = (raw_res & 0b11100) >> 2; uint8_t usb_state = raw_res & 0b11; - if (usb_state == 0) { - charge_status_ = ChargeStatus::kDischarging; - } else if (usb_state == 1) { - charge_status_ = ChargeStatus::kChargingRegular; - } else { - charge_status_ = ChargeStatus::kChargingFast; + switch (charge_state) { + case 0b000: + charge_status_ = ChargeStatus::kNoBattery; + break; + case 0b001: + // BMS says we're charging; work out how fast we're charging. + if (usb_state >= 0b10 && nvs_.FastCharge()) { + charge_status_ = ChargeStatus::kChargingFast; + } else { + charge_status_ = ChargeStatus::kChargingRegular; + } + break; + case 0b010: + charge_status_ = ChargeStatus::kFullCharge; + break; + case 0b011: + charge_status_ = ChargeStatus::kFault; + break; + case 0b100: + charge_status_ = ChargeStatus::kBatteryCritical; + break; + case 0b101: + charge_status_ = ChargeStatus::kDischarging; + break; + case 0b110: + case 0b111: + charge_status_ = ChargeStatus::kUnknown; + break; } } @@ -127,9 +173,15 @@ auto Samd::ResetToFlashSamd() -> void { } auto Samd::SetFastChargeEnabled(bool en) -> void { + // Always update NVS, so that the setting is right after the SAMD firmware is + // updated. + nvs_.FastCharge(en); + if (version_ < 4) { return; } + ESP_LOGI(kTag, "set fast charge %u", en); + I2CTransaction transaction; transaction.start() .write_addr(kAddress, I2C_MASTER_WRITE) diff --git a/src/tangara/app_console/app_console.cpp b/src/tangara/app_console/app_console.cpp index 11862143..21dec56a 100644 --- a/src/tangara/app_console/app_console.cpp +++ b/src/tangara/app_console/app_console.cpp @@ -465,26 +465,7 @@ int CmdSamd(int argc, char** argv) { } else if (cmd == "charge") { auto res = samd.GetChargeStatus(); if (res) { - switch (res.value()) { - case drivers::Samd::ChargeStatus::kNoBattery: - std::cout << "kNoBattery" << std::endl; - break; - case drivers::Samd::ChargeStatus::kBatteryCritical: - std::cout << "kBatteryCritical" << std::endl; - break; - case drivers::Samd::ChargeStatus::kDischarging: - std::cout << "kDischarging" << std::endl; - break; - case drivers::Samd::ChargeStatus::kChargingRegular: - std::cout << "kChargingRegular" << std::endl; - break; - case drivers::Samd::ChargeStatus::kChargingFast: - std::cout << "kChargingFast" << std::endl; - break; - case drivers::Samd::ChargeStatus::kFullCharge: - std::cout << "kFullCharge" << std::endl; - break; - } + std::cout << drivers::Samd::chargeStatusToString(*res) << std::endl; } else { std::cout << "unknown" << std::endl; } diff --git a/src/tangara/battery/battery.cpp b/src/tangara/battery/battery.cpp index 3cfdb20c..9bde86a8 100644 --- a/src/tangara/battery/battery.cpp +++ b/src/tangara/battery/battery.cpp @@ -93,6 +93,8 @@ auto Battery::Update() -> void { .percent = percent, .millivolts = mV, .is_charging = is_charging, + .raw_status = + charge_state.value_or(drivers::Samd::ChargeStatus::kUnknown), }; EmitEvent(); } diff --git a/src/tangara/battery/battery.hpp b/src/tangara/battery/battery.hpp index 80b0f2d2..c4f631e0 100644 --- a/src/tangara/battery/battery.hpp +++ b/src/tangara/battery/battery.hpp @@ -27,6 +27,7 @@ class Battery { uint_fast8_t percent; uint32_t millivolts; bool is_charging; + drivers::Samd::ChargeStatus raw_status; bool operator==(const BatteryState& other) const { return percent == other.percent && is_charging == other.is_charging; diff --git a/src/tangara/system_fsm/booting.cpp b/src/tangara/system_fsm/booting.cpp index a3fed9fa..1f99e3ab 100644 --- a/src/tangara/system_fsm/booting.cpp +++ b/src/tangara/system_fsm/booting.cpp @@ -87,7 +87,7 @@ auto Booting::entry() -> void { ESP_LOGI(kTag, "installing remaining drivers"); drivers::spiffs_mount(); - sServices->samd(std::unique_ptr(drivers::Samd::Create())); + sServices->samd(std::make_unique(sServices->nvs())); sServices->touchwheel( std::unique_ptr{drivers::TouchWheel::Create()}); sServices->haptics(std::make_unique(sServices->nvs())); @@ -96,8 +96,8 @@ auto Booting::entry() -> void { sServices->battery(std::make_unique( sServices->samd(), std::unique_ptr(adc))); - sServices->track_queue( - std::make_unique(sServices->bg_worker(), sServices->database())); + sServices->track_queue(std::make_unique( + sServices->bg_worker(), sServices->database())); sServices->tag_parser(std::make_unique()); sServices->collator(locale::CreateCollator()); sServices->tts(std::make_unique()); diff --git a/src/tangara/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp index cd39dc9c..38e9b8e1 100644 --- a/src/tangara/ui/ui_fsm.cpp +++ b/src/tangara/ui/ui_fsm.cpp @@ -101,6 +101,15 @@ static auto lvgl_delay_cb(uint32_t ms) -> void { lua::Property UiState::sBatteryPct{0}; lua::Property UiState::sBatteryMv{0}; lua::Property UiState::sBatteryCharging{false}; +lua::Property UiState::sPowerChargeState{"unknown"}; +lua::Property UiState::sPowerFastChargeEnabled{ + false, [](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + sServices->samd().SetFastChargeEnabled(std::get(val)); + return true; + }}; lua::Property UiState::sBluetoothEnabled{ false, [](const lua::LuaValue& val) { @@ -406,6 +415,13 @@ void UiState::react(const system_fsm::BatteryStateChanged& ev) { sBatteryPct.setDirect(static_cast(ev.new_state.percent)); sBatteryMv.setDirect(static_cast(ev.new_state.millivolts)); sBatteryCharging.setDirect(ev.new_state.is_charging); + sPowerChargeState.setDirect( + drivers::Samd::chargeStatusToString(ev.new_state.raw_status)); + + // FIXME: Avoid calling these event handlers before boot. + if (sServices) { + sPowerFastChargeEnabled.setDirect(sServices->nvs().FastCharge()); + } } void UiState::react(const audio::QueueUpdate&) { @@ -414,7 +430,8 @@ void UiState::react(const audio::QueueUpdate&) { sQueueSize.setDirect(static_cast(queue_size)); int current_pos = queue.currentPosition(); - // If there is nothing in the queue, the position should be 0, otherwise, add one because lua + // If there is nothing in the queue, the position should be 0, otherwise, add + // one because lua if (queue_size > 0) { current_pos++; } @@ -564,11 +581,14 @@ void Lua::entry() { auto& registry = lua::Registry::instance(*sServices); sLua = registry.uiThread(); - registry.AddPropertyModule("power", { - {"battery_pct", &sBatteryPct}, - {"battery_millivolts", &sBatteryMv}, - {"plugged_in", &sBatteryCharging}, - }); + registry.AddPropertyModule("power", + { + {"battery_pct", &sBatteryPct}, + {"battery_millivolts", &sBatteryMv}, + {"plugged_in", &sBatteryCharging}, + {"charge_state", &sPowerChargeState}, + {"fast_charge", &sPowerFastChargeEnabled}, + }); registry.AddPropertyModule( "bluetooth", { {"enabled", &sBluetoothEnabled}, @@ -663,6 +683,8 @@ void Lua::entry() { } sBluetoothKnownDevices.setDirect(bt.knownDevices()); + sPowerFastChargeEnabled.setDirect(sServices->nvs().FastCharge()); + if (sServices->sd() == drivers::SdState::kMounted) { sLua->RunScript("/sdcard/config.lua"); } diff --git a/src/tangara/ui/ui_fsm.hpp b/src/tangara/ui/ui_fsm.hpp index cef9a13a..41f0db3a 100644 --- a/src/tangara/ui/ui_fsm.hpp +++ b/src/tangara/ui/ui_fsm.hpp @@ -101,6 +101,8 @@ class UiState : public tinyfsm::Fsm { static lua::Property sBatteryPct; static lua::Property sBatteryMv; static lua::Property sBatteryCharging; + static lua::Property sPowerChargeState; + static lua::Property sPowerFastChargeEnabled; static lua::Property sBluetoothEnabled; static lua::Property sBluetoothConnecting; -- cgit v1.2.3