diff options
Diffstat (limited to 'src/tangara/system_fsm/idle.cpp')
| -rw-r--r-- | src/tangara/system_fsm/idle.cpp | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/tangara/system_fsm/idle.cpp b/src/tangara/system_fsm/idle.cpp new file mode 100644 index 00000000..e28864b3 --- /dev/null +++ b/src/tangara/system_fsm/idle.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "app_console.hpp" +#include "file_gatherer.hpp" +#include "freertos/portmacro.h" +#include "freertos/projdefs.h" +#include "gpios.hpp" +#include "result.hpp" + +#include "audio_fsm.hpp" +#include "event_queue.hpp" +#include "samd.hpp" +#include "storage.hpp" +#include "system_events.hpp" +#include "system_fsm.hpp" +#include "ui_fsm.hpp" + +namespace system_fsm { +namespace states { + +[[maybe_unused]] static const char kTag[] = "IDLE"; +static const TickType_t kTicksBeforeSleep = pdMS_TO_TICKS(10000); + +static void timer_callback(TimerHandle_t timer) { + events::System().Dispatch(internal::IdleTimeout{}); +} + +/* + * Ensure the storage and database are both available. If either of these fails + * to open, then we assume it's an issue with the underlying SD card. + */ +void Idle::entry() { + ESP_LOGI(kTag, "system became idle"); + + sServices->nvs().Write(); + + events::Audio().Dispatch(OnIdle{}); + events::Ui().Dispatch(OnIdle{}); + + sIdleTimeout = xTimerCreate("idle_timeout", kTicksBeforeSleep, true, NULL, + timer_callback); + xTimerStart(sIdleTimeout, portMAX_DELAY); +} + +void Idle::exit() { + xTimerStop(sIdleTimeout, portMAX_DELAY); + xTimerDelete(sIdleTimeout, portMAX_DELAY); + ESP_LOGI(kTag, "system left idle"); +} + +void Idle::react(const KeyLockChanged& ev) { + if (!ev.locking) { + transit<Running>(); + } +} + +void Idle::react(const internal::IdleTimeout& ev) { + if (!IdleCondition()) { + // Defensively ensure that we didn't miss an idle-ending event. + transit<Running>(); + return; + } + if (sServices->samd().GetChargeStatus() != + drivers::Samd::ChargeStatus::kDischarging) { + // Stay powered on if we're plugged in, in order to charge faster, sync + // files, flash updates, etc. + return; + } + ESP_LOGI(kTag, "system shutting down"); + + // FIXME: It would be neater to just free a bunch of our pointers, deinit the + // other state machines, etc. + auto touchwheel = sServices->touchwheel(); + if (touchwheel) { + touchwheel.value()->PowerDown(); + } + + auto& gpios = sServices->gpios(); + // Pull down to turn things off + gpios.WriteBuffered(drivers::IGpios::Pin::kAmplifierEnable, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kSdPowerEnable, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kDisplayEnable, false); + + // Leave up to match the external pullups + gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxSwitch, true); + gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxDisable, true); + + // Pull down to prevent sourcing current uselessly from input pins. + gpios.WriteBuffered(drivers::IGpios::Pin::kSdCardDetect, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kKeyUp, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kKeyDown, false); + + gpios.Flush(); + + // 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 (;;) { + sServices->samd().PowerDown(); + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +} // namespace states +} // namespace system_fsm |
