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)