From 8ff93f5467b0eef54d18b35d742de05c8a63da9a Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 28 Aug 2023 10:18:02 +1000 Subject: [PATCH 1/3] Make idle state more robust + check playback state --- src/audio/audio_fsm.cpp | 2 ++ src/audio/include/audio_events.hpp | 2 ++ src/system_fsm/idle.cpp | 14 ++++++++++++-- src/system_fsm/include/system_fsm.hpp | 6 ++++++ src/system_fsm/running.cpp | 9 ++++++++- src/system_fsm/system_fsm.cpp | 5 +++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index e68eedaf..cd36b398 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -144,6 +144,8 @@ void Playback::exit() { // to drain. vTaskDelay(pdMS_TO_TICKS(250)); sOutput->SetInUse(false); + + events::System().Dispatch(PlaybackFinished{}); } void Playback::react(const QueueUpdate& ev) { diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp index 28e29863..1d3690a4 100644 --- a/src/audio/include/audio_events.hpp +++ b/src/audio/include/audio_events.hpp @@ -25,6 +25,8 @@ struct PlaybackUpdate : tinyfsm::Event { uint32_t seconds_total; }; +struct PlaybackFinished : tinyfsm::Event {}; + struct QueueUpdate : tinyfsm::Event { bool current_changed; }; diff --git a/src/system_fsm/idle.cpp b/src/system_fsm/idle.cpp index 91075fc6..7cc1fa39 100644 --- a/src/system_fsm/idle.cpp +++ b/src/system_fsm/idle.cpp @@ -49,12 +49,17 @@ void Idle::exit() { } void Idle::react(const KeyLockChanged& ev) { - if (!ev.falling) { + if (ev.falling) { transit(); } } void Idle::react(const internal::IdleTimeout& ev) { + if (!IdleCondition()) { + // Defensively ensure that we didn't miss an idle-ending event. + transit(); + return; + } ESP_LOGI(kTag, "system shutting down"); // FIXME: It would be neater to just free a bunch of our pointers, deinit the @@ -79,7 +84,12 @@ void Idle::react(const internal::IdleTimeout& ev) { sGpios->Flush(); - sSamd->PowerDown(); + // Retry shutting down in case of a transient failure with the SAMD. e.g. i2c + // timeouts. This guards against a buggy SAMD firmware preventing idle. + for (;;) { + sSamd->PowerDown(); + vTaskDelay(pdMS_TO_TICKS(1000)); + } } } // namespace states diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp index 6baceca1..a556be9e 100644 --- a/src/system_fsm/include/system_fsm.hpp +++ b/src/system_fsm/include/system_fsm.hpp @@ -9,6 +9,7 @@ #include #include "app_console.hpp" +#include "audio_events.hpp" #include "battery.hpp" #include "bluetooth.hpp" #include "database.hpp" @@ -54,9 +55,12 @@ class SystemState : public tinyfsm::Fsm { virtual void react(const StorageMounted&) {} virtual void react(const StorageError&) {} virtual void react(const KeyLockChanged&) {} + virtual void react(const audio::PlaybackFinished&) {} virtual void react(const internal::IdleTimeout&) {} protected: + auto IdleCondition() -> bool; + static std::shared_ptr sGpios; static std::shared_ptr sSamd; static std::shared_ptr sNvs; @@ -101,6 +105,8 @@ class Running : public SystemState { void react(const KeyLockChanged&) override; void react(const StorageError&) override; + void react(const audio::PlaybackFinished&) override; + using SystemState::react; }; diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp index 9e250c9b..3fc5493f 100644 --- a/src/system_fsm/running.cpp +++ b/src/system_fsm/running.cpp @@ -5,6 +5,7 @@ */ #include "app_console.hpp" +#include "audio_events.hpp" #include "file_gatherer.hpp" #include "freertos/projdefs.h" #include "result.hpp" @@ -68,7 +69,13 @@ void Running::exit() { } void Running::react(const KeyLockChanged& ev) { - if (!ev.falling && audio::AudioState::is_in_state()) { + if (IdleCondition()) { + transit(); + } +} + +void Running::react(const audio::PlaybackFinished& ev) { + if (IdleCondition()) { transit(); } } diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp index 78c4c53e..9ad89c7a 100644 --- a/src/system_fsm/system_fsm.cpp +++ b/src/system_fsm/system_fsm.cpp @@ -105,6 +105,11 @@ void SystemState::react(const internal::BatteryTimerFired&) { } } +auto SystemState::IdleCondition() -> bool { + return !sGpios->Get(drivers::IGpios::Pin::kKeyLock) && + audio::AudioState::is_in_state(); +} + } // namespace system_fsm FSM_INITIAL_STATE(system_fsm::SystemState, system_fsm::states::Booting) From a1327763ab70dbf4996e032dd227de368f78f4ad Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 28 Aug 2023 10:36:18 +1000 Subject: [PATCH 2/3] Support play/pause toggling --- src/audio/audio_fsm.cpp | 12 +++++++++++- src/audio/include/audio_events.hpp | 2 ++ src/audio/include/audio_fsm.hpp | 3 +++ src/ui/screen_playing.cpp | 8 ++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index cd36b398..6cca8211 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -133,6 +133,12 @@ void Standby::react(const QueueUpdate& ev) { sFileSource->SetPath(db->GetTrackPath(*current_track)); } +void Standby::react(const TogglePlayPause& ev) { + if (sCurrentTrack) { + transit(); + } +} + void Playback::entry() { ESP_LOGI(kTag, "beginning playback"); sOutput->SetInUse(true); @@ -142,7 +148,7 @@ void Playback::exit() { ESP_LOGI(kTag, "finishing playback"); // TODO(jacqueline): Second case where it's useful to wait for the i2s buffer // to drain. - vTaskDelay(pdMS_TO_TICKS(250)); + vTaskDelay(pdMS_TO_TICKS(10)); sOutput->SetInUse(false); events::System().Dispatch(PlaybackFinished{}); @@ -170,6 +176,10 @@ void Playback::react(const QueueUpdate& ev) { sFileSource->SetPath(db->GetTrackPath(*current_track)); } +void Playback::react(const TogglePlayPause& ev) { + transit(); +} + void Playback::react(const PlaybackUpdate& ev) { ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed, ev.seconds_total); diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp index 1d3690a4..8ee8b057 100644 --- a/src/audio/include/audio_events.hpp +++ b/src/audio/include/audio_events.hpp @@ -37,6 +37,8 @@ struct PlayFile : tinyfsm::Event { struct VolumeChanged : tinyfsm::Event {}; +struct TogglePlayPause : tinyfsm::Event {}; + namespace internal { struct InputFileOpened : tinyfsm::Event {}; diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index 430bc298..cfa65551 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -57,6 +57,7 @@ class AudioState : public tinyfsm::Fsm { virtual void react(const PlayFile&) {} virtual void react(const QueueUpdate&) {} virtual void react(const PlaybackUpdate&) {} + virtual void react(const TogglePlayPause&) {} virtual void react(const internal::InputFileOpened&) {} virtual void react(const internal::InputFileClosed&) {} @@ -90,6 +91,7 @@ class Standby : public AudioState { void react(const PlayFile&) override; void react(const internal::InputFileOpened&) override; void react(const QueueUpdate&) override; + void react(const TogglePlayPause&) override; using AudioState::react; }; @@ -102,6 +104,7 @@ class Playback : public AudioState { void react(const PlayFile&) override; void react(const QueueUpdate&) override; void react(const PlaybackUpdate&) override; + void react(const TogglePlayPause&) override; void react(const internal::InputFileOpened&) override; void react(const internal::InputFileClosed&) override; diff --git a/src/ui/screen_playing.cpp b/src/ui/screen_playing.cpp index ce0f71d6..7538d093 100644 --- a/src/ui/screen_playing.cpp +++ b/src/ui/screen_playing.cpp @@ -8,6 +8,7 @@ #include #include +#include "audio_events.hpp" #include "core/lv_event.h" #include "core/lv_obj.h" #include "core/lv_obj_scroll.h" @@ -63,6 +64,10 @@ static void below_fold_focus_cb(lv_event_t* ev) { instance->OnFocusBelowFold(); } +static void play_pause_cb(lv_event_t* ev) { + events::Audio().Dispatch(audio::TogglePlayPause{}); +} + static lv_style_t scrubber_style; auto info_label(lv_obj_t* parent) -> lv_obj_t* { @@ -159,7 +164,10 @@ Playing::Playing(std::weak_ptr db, audio::TrackQueue* queue) LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); play_pause_control_ = control_button(controls_container, LV_SYMBOL_PLAY); + lv_obj_add_event_cb(play_pause_control_, play_pause_cb, LV_EVENT_CLICKED, + NULL); lv_group_add_obj(group_, play_pause_control_); + lv_group_add_obj(group_, control_button(controls_container, LV_SYMBOL_PREV)); lv_group_add_obj(group_, control_button(controls_container, LV_SYMBOL_NEXT)); lv_group_add_obj(group_, From 3a0c42f9240eedfbc6a1e94ad3a59c52664fb5b5 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 28 Aug 2023 13:26:53 +1000 Subject: [PATCH 3/3] Move battery measurement to its own class --- src/battery/CMakeLists.txt | 10 ++ src/battery/battery.cpp | 101 +++++++++++++++++++ src/battery/include/battery.hpp | 44 ++++++++ src/drivers/CMakeLists.txt | 2 +- src/drivers/{battery.cpp => adc.cpp} | 38 +------ src/drivers/include/{battery.hpp => adc.hpp} | 16 ++- src/system_fsm/CMakeLists.txt | 2 +- src/system_fsm/booting.cpp | 24 ++--- src/system_fsm/include/system_events.hpp | 4 +- src/system_fsm/include/system_fsm.hpp | 4 +- src/system_fsm/system_fsm.cpp | 13 +-- src/ui/CMakeLists.txt | 2 +- src/ui/include/screen_settings.hpp | 29 +++--- src/ui/include/ui_fsm.hpp | 8 +- src/ui/ui_fsm.cpp | 16 ++- src/ui/widget_top_bar.cpp | 23 +++-- 16 files changed, 226 insertions(+), 110 deletions(-) create mode 100644 src/battery/CMakeLists.txt create mode 100644 src/battery/battery.cpp create mode 100644 src/battery/include/battery.hpp rename src/drivers/{battery.cpp => adc.cpp} (58%) rename src/drivers/include/{battery.hpp => adc.hpp} (69%) diff --git a/src/battery/CMakeLists.txt b/src/battery/CMakeLists.txt new file mode 100644 index 00000000..313a3731 --- /dev/null +++ b/src/battery/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2023 jacqueline +# +# SPDX-License-Identifier: GPL-3.0-only + +idf_component_register( + SRCS "battery.cpp" + INCLUDE_DIRS "include" + REQUIRES "drivers" "events") + +target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "battery.hpp" + +#include + +#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(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 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(std::min( + std::max(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 { + std::lock_guard lock{state_mutex_}; + return state_; +} + +auto Battery::EmitEvent() -> void { + events::System().Dispatch(system_fsm::BatteryStateChanged{}); + events::Ui().Dispatch(system_fsm::BatteryStateChanged{}); +} + +} // namespace battery diff --git a/src/battery/include/battery.hpp b/src/battery/include/battery.hpp new file mode 100644 index 00000000..dcb9b4ea --- /dev/null +++ b/src/battery/include/battery.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/timers.h" + +#include "adc.hpp" +#include "samd.hpp" + +namespace battery { + +class Battery { + public: + Battery(drivers::Samd* samd, drivers::AdcBattery* adc); + ~Battery(); + + auto Update() -> void; + + struct BatteryState { + uint_fast8_t percent; + bool is_charging; + }; + + auto State() -> std::optional; + + private: + auto EmitEvent() -> void; + + drivers::Samd* samd_; + drivers::AdcBattery* adc_; + + TimerHandle_t timer_; + std::mutex state_mutex_; + std::optional state_; +}; + +} // namespace battery diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index a64495f0..1df58f72 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRCS "touchwheel.cpp" "i2s_dac.cpp" "gpios.cpp" "battery.cpp" "storage.cpp" "i2c.cpp" + SRCS "touchwheel.cpp" "i2s_dac.cpp" "gpios.cpp" "adc.cpp" "storage.cpp" "i2c.cpp" "spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp" "wm8523.cpp" "nvs.cpp" "bluetooth.cpp" INCLUDE_DIRS "include" diff --git a/src/drivers/battery.cpp b/src/drivers/adc.cpp similarity index 58% rename from src/drivers/battery.cpp rename to src/drivers/adc.cpp index 1755fd64..56d2cbb4 100644 --- a/src/drivers/battery.cpp +++ b/src/drivers/adc.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -#include "battery.hpp" +#include "adc.hpp" #include #include "esp_adc/adc_cali.h" @@ -14,21 +14,6 @@ namespace drivers { -/* - * 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. - static const adc_bitwidth_t kAdcBitWidth = ADC_BITWIDTH_12; static const adc_unit_t kAdcUnit = ADC_UNIT_1; // Max battery voltage should be a little over 2V due to our divider, so we need @@ -37,7 +22,7 @@ static const adc_atten_t kAdcAttenuation = ADC_ATTEN_DB_11; // Corresponds to SENSOR_VP. static const adc_channel_t kAdcChannel = ADC_CHANNEL_0; -Battery::Battery() { +AdcBattery::AdcBattery() { adc_oneshot_unit_init_cfg_t unit_config = { .unit_id = kAdcUnit, }; @@ -59,16 +44,14 @@ Battery::Battery() { }; ESP_ERROR_CHECK(adc_cali_create_scheme_line_fitting( &calibration_config, &adc_calibration_handle_)); - - UpdatePercent(); } -Battery::~Battery() { +AdcBattery::~AdcBattery() { adc_cali_delete_scheme_line_fitting(adc_calibration_handle_); ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle_)); } -auto Battery::Millivolts() -> uint32_t { +auto AdcBattery::Millivolts() -> uint32_t { // GPIO 34 int raw = 0; ESP_ERROR_CHECK(adc_oneshot_read(adc_handle_, kAdcChannel, &raw)); @@ -81,17 +64,4 @@ auto Battery::Millivolts() -> uint32_t { return voltage * 2; } -auto Battery::UpdatePercent() -> bool { - auto old_percent = percent_; - // 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(Millivolts(), kEmptyChargeMilliVolts); - percent_ = static_cast(std::min( - std::max(0.0, mV - kEmptyChargeMilliVolts) / - (kFullChargeMilliVolts - kEmptyChargeMilliVolts) * 100.0, - 100.0)); - return old_percent != percent_; -} - } // namespace drivers diff --git a/src/drivers/include/battery.hpp b/src/drivers/include/adc.hpp similarity index 69% rename from src/drivers/include/battery.hpp rename to src/drivers/include/adc.hpp index 64a00135..3e94a9ee 100644 --- a/src/drivers/include/battery.hpp +++ b/src/drivers/include/adc.hpp @@ -15,25 +15,23 @@ namespace drivers { -class Battery { +/* + * Handles measuring the battery's current voltage. + */ +class AdcBattery { public: - static auto Create() -> Battery* { return new Battery(); } - Battery(); - ~Battery(); + static auto Create() -> AdcBattery* { return new AdcBattery(); } + AdcBattery(); + ~AdcBattery(); /** * Returns the current battery level in millivolts. */ auto Millivolts() -> uint32_t; - auto UpdatePercent() -> bool; - auto Percent() -> uint_fast8_t { return percent_; } - private: adc_oneshot_unit_handle_t adc_handle_; adc_cali_handle_t adc_calibration_handle_; - - uint_fast8_t percent_; }; } // namespace drivers diff --git a/src/system_fsm/CMakeLists.txt b/src/system_fsm/CMakeLists.txt index 037e72c7..fced4093 100644 --- a/src/system_fsm/CMakeLists.txt +++ b/src/system_fsm/CMakeLists.txt @@ -5,5 +5,5 @@ idf_component_register( SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp" INCLUDE_DIRS "include" - REQUIRES "tinyfsm" "drivers" "database" "ui" "result" "events" "audio" "app_console") + REQUIRES "tinyfsm" "drivers" "database" "ui" "result" "events" "audio" "app_console" "battery") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index 33ed39d1..f33d1679 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -38,12 +38,6 @@ namespace states { static const char kTag[] = "BOOT"; -static const TickType_t kBatteryCheckPeriod = pdMS_TO_TICKS(60 * 1000); - -static void battery_timer_cb(TimerHandle_t timer) { - events::System().Dispatch(internal::BatteryTimerFired{}); -} - auto Booting::entry() -> void { ESP_LOGI(kTag, "beginning tangara boot"); ESP_LOGI(kTag, "installing early drivers"); @@ -53,33 +47,31 @@ auto Booting::entry() -> void { ESP_ERROR_CHECK(drivers::init_spi()); sGpios.reset(drivers::Gpios::Create()); + sSamd.reset(drivers::Samd::Create()); + sAdc.reset(drivers::AdcBattery::Create()); + assert(sSamd.get() && sAdc.get()); + + sBattery.reset(new battery::Battery(sSamd.get(), sAdc.get())); + // Start bringing up LVGL now, since we have all of its prerequisites. sTrackQueue.reset(new audio::TrackQueue()); ESP_LOGI(kTag, "starting ui"); - if (!ui::UiState::Init(sGpios.get(), sTrackQueue.get())) { + if (!ui::UiState::Init(sGpios.get(), sTrackQueue.get(), sBattery)) { events::System().Dispatch(FatalError{}); return; } // Install everything else that is certain to be needed. ESP_LOGI(kTag, "installing remaining drivers"); - sSamd.reset(drivers::Samd::Create()); - sBattery.reset(drivers::Battery::Create()); sNvs.reset(drivers::NvsStorage::Open()); sTagParser.reset(new database::TagParserImpl()); - if (!sSamd || !sBattery || !sNvs) { + if (!sNvs) { events::System().Dispatch(FatalError{}); events::Ui().Dispatch(FatalError{}); return; } - ESP_LOGI(kTag, "battery is at %u%% (= %lu mV)", sBattery->Percent(), - sBattery->Millivolts()); - TimerHandle_t battery_timer = xTimerCreate("battery", kBatteryCheckPeriod, - true, NULL, battery_timer_cb); - xTimerStart(battery_timer, portMAX_DELAY); - // ESP_LOGI(kTag, "starting bluetooth"); // sBluetooth.reset(new drivers::Bluetooth(sNvs.get())); // sBluetooth->Enable(); diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp index 8a3ba5ec..64cbd393 100644 --- a/src/system_fsm/include/system_events.hpp +++ b/src/system_fsm/include/system_events.hpp @@ -53,7 +53,7 @@ struct HasPhonesChanged : tinyfsm::Event { }; struct ChargingStatusChanged : tinyfsm::Event {}; -struct BatteryPercentChanged : tinyfsm::Event {}; +struct BatteryStateChanged : tinyfsm::Event {}; namespace internal { @@ -62,8 +62,6 @@ struct SamdInterrupt : tinyfsm::Event {}; struct IdleTimeout : tinyfsm::Event {}; -struct BatteryTimerFired : tinyfsm::Event {}; - } // namespace internal } // namespace system_fsm diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp index a556be9e..371e5527 100644 --- a/src/system_fsm/include/system_fsm.hpp +++ b/src/system_fsm/include/system_fsm.hpp @@ -48,7 +48,6 @@ class SystemState : public tinyfsm::Fsm { void react(const FatalError&); void react(const internal::GpioInterrupt&); void react(const internal::SamdInterrupt&); - void react(const internal::BatteryTimerFired&); virtual void react(const DisplayReady&) {} virtual void react(const BootComplete&) {} @@ -67,7 +66,8 @@ class SystemState : public tinyfsm::Fsm { static std::shared_ptr sTouch; static std::shared_ptr sRelativeTouch; - static std::shared_ptr sBattery; + static std::shared_ptr sAdc; + static std::shared_ptr sBattery; static std::shared_ptr sStorage; static std::shared_ptr sDisplay; static std::shared_ptr sBluetooth; diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp index 9ad89c7a..d21e8bcb 100644 --- a/src/system_fsm/system_fsm.cpp +++ b/src/system_fsm/system_fsm.cpp @@ -23,7 +23,8 @@ std::shared_ptr SystemState::sNvs; std::shared_ptr SystemState::sTouch; std::shared_ptr SystemState::sRelativeTouch; -std::shared_ptr SystemState::sBattery; +std::shared_ptr SystemState::sAdc; +std::shared_ptr SystemState::sBattery; std::shared_ptr SystemState::sStorage; std::shared_ptr SystemState::sDisplay; std::shared_ptr SystemState::sBluetooth; @@ -95,16 +96,6 @@ void SystemState::react(const internal::SamdInterrupt&) { } } -void SystemState::react(const internal::BatteryTimerFired&) { - ESP_LOGI(kTag, "checking battery"); - if (sBattery->UpdatePercent()) { - ESP_LOGI(kTag, "battery now at %u%%", sBattery->Percent()); - BatteryPercentChanged ev{}; - events::Ui().Dispatch(ev); - events::System().Dispatch(ev); - } -} - auto SystemState::IdleCondition() -> bool { return !sGpios->Get(drivers::IGpios::Pin::kKeyLock) && audio::AudioState::is_in_state(); diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index fa3f2bc1..6b5c393a 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -9,5 +9,5 @@ idf_component_register( "modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp" "splash.c" "font_fusion.c" "font_symbols.c" INCLUDE_DIRS "include" - REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer") + REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp index ebc5bf7d..66124164 100644 --- a/src/ui/include/screen_settings.hpp +++ b/src/ui/include/screen_settings.hpp @@ -21,14 +21,15 @@ class Settings : public MenuScreen { public: Settings(); ~Settings(); - private: - std::shared_ptr bluetooth_; - std::shared_ptr headphones_; - std::shared_ptr appearance_; - std::shared_ptr input_method_; - std::shared_ptr storage_; - std::shared_ptr firmware_update_; - std::shared_ptr about_; + + private: + std::shared_ptr bluetooth_; + std::shared_ptr headphones_; + std::shared_ptr appearance_; + std::shared_ptr input_method_; + std::shared_ptr storage_; + std::shared_ptr firmware_update_; + std::shared_ptr about_; }; class Bluetooth : public MenuScreen { @@ -36,32 +37,32 @@ class Bluetooth : public MenuScreen { Bluetooth(); }; -class Headphones : public MenuScreen { +class Headphones : public MenuScreen { public: Headphones(); }; -class Appearance : public MenuScreen { +class Appearance : public MenuScreen { public: Appearance(); }; -class InputMethod : public MenuScreen { +class InputMethod : public MenuScreen { public: InputMethod(); }; -class Storage : public MenuScreen { +class Storage : public MenuScreen { public: Storage(); }; -class FirmwareUpdate : public MenuScreen { +class FirmwareUpdate : public MenuScreen { public: FirmwareUpdate(); }; -class About : public MenuScreen { +class About : public MenuScreen { public: About(); }; diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 80c01c68..8f1aa1bc 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -10,6 +10,7 @@ #include #include "audio_events.hpp" +#include "battery.hpp" #include "relative_wheel.hpp" #include "screen_playing.hpp" #include "tinyfsm.hpp" @@ -27,7 +28,9 @@ namespace ui { class UiState : public tinyfsm::Fsm { public: - static auto Init(drivers::IGpios*, audio::TrackQueue*) -> bool; + static auto Init(drivers::IGpios*, + audio::TrackQueue*, + std::shared_ptr) -> bool; virtual ~UiState() {} @@ -41,7 +44,7 @@ class UiState : public tinyfsm::Fsm { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} - void react(const system_fsm::BatteryPercentChanged&); + void react(const system_fsm::BatteryStateChanged&); virtual void react(const audio::PlaybackStarted&) {} virtual void react(const audio::PlaybackUpdate&) {} @@ -76,6 +79,7 @@ class UiState : public tinyfsm::Fsm { static std::shared_ptr sTouchWheel; static std::shared_ptr sRelativeWheel; static std::shared_ptr sDisplay; + static std::shared_ptr sBattery; static std::weak_ptr sDb; static std::stack> sScreens; diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 508fb740..32032978 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -8,6 +8,7 @@ #include +#include "battery.hpp" #include "core/lv_obj.h" #include "misc/lv_gc.h" @@ -43,16 +44,19 @@ audio::TrackQueue* UiState::sQueue; std::shared_ptr UiState::sTouchWheel; std::shared_ptr UiState::sRelativeWheel; std::shared_ptr UiState::sDisplay; +std::shared_ptr UiState::sBattery; std::weak_ptr UiState::sDb; std::stack> UiState::sScreens; std::shared_ptr UiState::sCurrentScreen; std::shared_ptr UiState::sCurrentModal; -auto UiState::Init(drivers::IGpios* gpio_expander, audio::TrackQueue* queue) - -> bool { +auto UiState::Init(drivers::IGpios* gpio_expander, + audio::TrackQueue* queue, + std::shared_ptr battery) -> bool { sIGpios = gpio_expander; sQueue = queue; + sBattery = battery; lv_init(); sDisplay.reset( @@ -100,15 +104,17 @@ void UiState::react(const system_fsm::KeyLockChanged& ev) { sRelativeWheel->SetEnabled(ev.falling); } -void UiState::react(const system_fsm::BatteryPercentChanged&) { +void UiState::react(const system_fsm::BatteryStateChanged&) { UpdateTopBar(); } void UiState::UpdateTopBar() { + auto battery_state = sBattery->State(); widgets::TopBar::State state{ .playback_state = widgets::TopBar::PlaybackState::kIdle, - .battery_percent = 50, - .is_charging = true, + .battery_percent = static_cast( + battery_state.has_value() ? battery_state->percent : 100), + .is_charging = !battery_state.has_value() || battery_state->is_charging, }; if (sCurrentScreen) { sCurrentScreen->UpdateTopBar(state); diff --git a/src/ui/widget_top_bar.cpp b/src/ui/widget_top_bar.cpp index 7d4ef98c..851b617f 100644 --- a/src/ui/widget_top_bar.cpp +++ b/src/ui/widget_top_bar.cpp @@ -65,17 +65,18 @@ auto TopBar::Update(const State& state) -> void { break; } - if (state.battery_percent >= 95) { - lv_label_set_text(battery_, "100"); - } else if (state.battery_percent >= 70) { - lv_label_set_text(battery_, ">70"); - } else if (state.battery_percent >= 40) { - lv_label_set_text(battery_, ">40"); - } else if (state.battery_percent >= 10) { - lv_label_set_text(battery_, ">10"); - } else { - lv_label_set_text(battery_, "0"); - } + lv_label_set_text(battery_, std::to_string(state.battery_percent).c_str()); + // if (state.battery_percent >= 95) { + // lv_label_set_text(battery_, "100"); + // } else if (state.battery_percent >= 70) { + // lv_label_set_text(battery_, ">70"); + // } else if (state.battery_percent >= 40) { + // lv_label_set_text(battery_, ">40"); + // } else if (state.battery_percent >= 10) { + // lv_label_set_text(battery_, ">10"); + // } else { + // lv_label_set_text(battery_, "0"); + // } } } // namespace widgets