From 344a46d0664eb75d232eacea91a4957a25e071f6 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 3 May 2024 16:40:39 +1000 Subject: [PATCH] Respond to sd card mounts and unmounts within lua Includes no longer blocking the main menu on an sd card being inserted!! --- lua/main.lua | 16 ++-- src/tangara/audio/audio_fsm.cpp | 13 ++- src/tangara/audio/audio_fsm.hpp | 10 ++- src/tangara/system_fsm/running.cpp | 60 ++++++++++---- src/tangara/system_fsm/system_events.hpp | 10 +-- src/tangara/system_fsm/system_fsm.hpp | 8 +- src/tangara/ui/ui_fsm.cpp | 101 ++++++++++++++--------- src/tangara/ui/ui_fsm.hpp | 20 +++-- 8 files changed, 153 insertions(+), 85 deletions(-) diff --git a/lua/main.lua b/lua/main.lua index dc73c964..e5adcaae 100644 --- a/lua/main.lua +++ b/lua/main.lua @@ -3,12 +3,15 @@ local vol = require("volume") local theme = require("theme") local controls = require("controls") local time = require("time") - -local lock_time = time.ticks() +local sd_card = require("sd_card") +local backstack = require("backstack") +local main_menu = require("main_menu") local theme_dark = require("theme_dark") theme.set(theme_dark) +local lock_time = time.ticks() + -- Set up property bindings that are used across every screen. GLOBAL_BINDINGS = { -- Show an alert with the current volume whenever the volume changes @@ -52,9 +55,8 @@ GLOBAL_BINDINGS = { end end end), + sd_card.mounted:bind(function(mounted) + print("reset ui stack") + backstack.reset(main_menu:new()) + end), } - -local backstack = require("backstack") -local main_menu = require("main_menu") - -backstack.push(main_menu) diff --git a/src/tangara/audio/audio_fsm.cpp b/src/tangara/audio/audio_fsm.cpp index f504da2e..7e74b706 100644 --- a/src/tangara/audio/audio_fsm.cpp +++ b/src/tangara/audio/audio_fsm.cpp @@ -15,6 +15,7 @@ #include "cppbor.h" #include "cppbor_parse.h" #include "drivers/bluetooth_types.hpp" +#include "drivers/storage.hpp" #include "esp_heap_caps.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" @@ -500,7 +501,11 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) { }); } -void Standby::react(const system_fsm::StorageMounted& ev) { +void Standby::react(const system_fsm::SdStateChanged& ev) { + auto state = sServices->sd(); + if (state != drivers::SdState::kMounted) { + return; + } sServices->bg_worker().Dispatch([]() { auto db = sServices->database().lock(); if (!db) { @@ -568,6 +573,12 @@ void Playback::exit() { events::Ui().Dispatch(event); } +void Playback::react(const system_fsm::SdStateChanged& ev) { + if (sServices->sd() != drivers::SdState::kMounted) { + transit(); + } +} + } // namespace states } // namespace audio diff --git a/src/tangara/audio/audio_fsm.hpp b/src/tangara/audio/audio_fsm.hpp index b3d64719..7a3aa56e 100644 --- a/src/tangara/audio/audio_fsm.hpp +++ b/src/tangara/audio/audio_fsm.hpp @@ -61,8 +61,8 @@ class AudioState : public tinyfsm::Fsm { void react(const OutputModeChanged&); virtual void react(const system_fsm::BootComplete&) {} - virtual void react(const system_fsm::KeyLockChanged&) {}; - virtual void react(const system_fsm::StorageMounted&) {} + virtual void react(const system_fsm::KeyLockChanged&){}; + virtual void react(const system_fsm::SdStateChanged&) {} virtual void react(const system_fsm::BluetoothEvent&); protected: @@ -103,7 +103,7 @@ namespace states { class Uninitialised : public AudioState { public: void react(const system_fsm::BootComplete&) override; - void react(const system_fsm::BluetoothEvent&) override {}; + void react(const system_fsm::BluetoothEvent&) override{}; using AudioState::react; }; @@ -111,7 +111,7 @@ class Uninitialised : public AudioState { class Standby : public AudioState { public: void react(const system_fsm::KeyLockChanged&) override; - void react(const system_fsm::StorageMounted&) override; + void react(const system_fsm::SdStateChanged&) override; using AudioState::react; }; @@ -121,6 +121,8 @@ class Playback : public AudioState { void entry() override; void exit() override; + void react(const system_fsm::SdStateChanged&) override; + using AudioState::react; }; diff --git a/src/tangara/system_fsm/running.cpp b/src/tangara/system_fsm/running.cpp index 82edd018..ac36ec64 100644 --- a/src/tangara/system_fsm/running.cpp +++ b/src/tangara/system_fsm/running.cpp @@ -10,6 +10,7 @@ #include "database/db_events.hpp" #include "database/file_gatherer.hpp" #include "drivers/gpios.hpp" +#include "drivers/spi.hpp" #include "ff.h" #include "freertos/portmacro.h" #include "freertos/projdefs.h" @@ -42,11 +43,7 @@ void Running::entry() { sUnmountTimer = xTimerCreate("unmount_timeout", kTicksBeforeUnmount, false, NULL, timer_callback); } - // Only mount our storage immediately if we know it's not currently in use - // by the SAMD. - if (!sServices->samd().UsbMassStorage()) { - mountStorage(); - } + mountStorage(); } void Running::exit() { @@ -80,10 +77,28 @@ void Running::react(const SdDetectChanged& ev) { if (ev.has_sd_card && !sStorage) { mountStorage(); } + // Don't automatically unmount, since this event seems to occasionally happen // supriously. FIXME: Why? - // (It doesn't matter too much; by the time we get this event the SD card has - // already been disconnected electrically.) + // Instead, check whether or not the card has actually gone away. + if (sStorage) { + FRESULT res; + FF_DIR dir; + { + auto lock = drivers::acquire_spi(); + res = f_opendir(&dir, "/"); + } + + if (res != FR_OK) { + ESP_LOGW(kTag, "sd card ejected unsafely!"); + unmountStorage(); + } + + { + auto lock = drivers::acquire_spi(); + f_closedir(&dir); + } + } } void Running::react(const SamdUsbMscChanged& ev) { @@ -134,25 +149,37 @@ auto Running::checkIdle() -> void { } } -auto Running::mountStorage() -> bool { +auto Running::updateSdState(drivers::SdState state) -> void { + sServices->sd(state); + events::Ui().Dispatch(SdStateChanged{}); + events::Audio().Dispatch(SdStateChanged{}); + events::System().Dispatch(SdStateChanged{}); +} + +auto Running::mountStorage() -> void { + // Only mount our storage if we know it's not currently in use by the SAMD. + if (sServices->samd().UsbMassStorage()) { + updateSdState(drivers::SdState::kNotMounted); + return; + } + ESP_LOGI(kTag, "mounting sd card"); auto storage_res = drivers::SdStorage::Create(sServices->gpios()); if (storage_res.has_error()) { ESP_LOGW(kTag, "failed to mount!"); switch (storage_res.error()) { case drivers::SdStorage::FAILED_TO_MOUNT: - sServices->sd(drivers::SdState::kNotFormatted); + updateSdState(drivers::SdState::kNotFormatted); break; case drivers::SdStorage::FAILED_TO_READ: default: - sServices->sd(drivers::SdState::kNotPresent); + updateSdState(drivers::SdState::kNotPresent); break; } - return false; + return; } sStorage.reset(storage_res.value()); - sServices->sd(drivers::SdState::kMounted); ESP_LOGI(kTag, "opening database"); sFileGatherer = new database::FileGathererImpl(); @@ -161,16 +188,14 @@ auto Running::mountStorage() -> bool { sServices->collator(), sServices->bg_worker()); if (database_res.has_error()) { unmountStorage(); - return false; + return; } sServices->database( std::unique_ptr{database_res.value()}); ESP_LOGI(kTag, "storage loaded okay"); - events::Ui().Dispatch(StorageMounted{}); - events::Audio().Dispatch(StorageMounted{}); - events::System().Dispatch(StorageMounted{}); + updateSdState(drivers::SdState::kMounted); // Tell the database to refresh so that we pick up any changes from the newly // mounted card. @@ -183,14 +208,13 @@ auto Running::mountStorage() -> bool { db->updateIndexes(); }); } - - return true; } auto Running::unmountStorage() -> void { ESP_LOGW(kTag, "unmounting storage"); sServices->database({}); sStorage.reset(); + updateSdState(drivers::SdState::kNotMounted); } } // namespace states diff --git a/src/tangara/system_fsm/system_events.hpp b/src/tangara/system_fsm/system_events.hpp index c174a1da..3452e58e 100644 --- a/src/tangara/system_fsm/system_events.hpp +++ b/src/tangara/system_fsm/system_events.hpp @@ -9,11 +9,12 @@ #include #include "battery/battery.hpp" -#include "drivers/bluetooth_types.hpp" #include "database/database.hpp" -#include "ff.h" +#include "drivers/bluetooth_types.hpp" #include "drivers/haptics.hpp" #include "drivers/samd.hpp" +#include "drivers/storage.hpp" +#include "ff.h" #include "system_fsm/service_locator.hpp" #include "tinyfsm.hpp" @@ -38,10 +39,7 @@ struct FatalError : tinyfsm::Event {}; struct OnIdle : tinyfsm::Event {}; -/* - * Sent by SysState when the system storage has been successfully mounted. - */ -struct StorageMounted : tinyfsm::Event {}; +struct SdStateChanged : tinyfsm::Event {}; struct StorageError : tinyfsm::Event { FRESULT error; diff --git a/src/tangara/system_fsm/system_fsm.hpp b/src/tangara/system_fsm/system_fsm.hpp index d69141dd..5c4157cd 100644 --- a/src/tangara/system_fsm/system_fsm.hpp +++ b/src/tangara/system_fsm/system_fsm.hpp @@ -15,19 +15,19 @@ #include "audio/audio_events.hpp" #include "audio/track_queue.hpp" #include "battery/battery.hpp" -#include "drivers/bluetooth.hpp" #include "database/database.hpp" #include "database/db_events.hpp" #include "database/tag_parser.hpp" +#include "drivers/bluetooth.hpp" #include "drivers/display.hpp" #include "drivers/gpios.hpp" #include "drivers/nvs.hpp" #include "drivers/samd.hpp" #include "drivers/storage.hpp" +#include "drivers/touchwheel.hpp" #include "system_fsm/service_locator.hpp" #include "system_fsm/system_events.hpp" #include "tinyfsm.hpp" -#include "drivers/touchwheel.hpp" namespace system_fsm { @@ -55,7 +55,6 @@ class SystemState : public tinyfsm::Fsm { virtual void react(const DisplayReady&) {} virtual void react(const BootComplete&) {} - virtual void react(const StorageMounted&) {} virtual void react(const StorageError&) {} virtual void react(const KeyLockChanged&) {} virtual void react(const SdDetectChanged&) {} @@ -110,7 +109,8 @@ class Running : public SystemState { private: auto checkIdle() -> void; - auto mountStorage() -> bool; + auto updateSdState(drivers::SdState) -> void; + auto mountStorage() -> void; auto unmountStorage() -> void; bool storage_mounted_; diff --git a/src/tangara/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp index 17967e20..17d6c511 100644 --- a/src/tangara/ui/ui_fsm.cpp +++ b/src/tangara/ui/ui_fsm.cpp @@ -10,56 +10,56 @@ #include #include -#include "database/db_events.hpp" -#include "drivers/bluetooth_types.hpp" -#include "drivers/display_init.hpp" -#include "esp_spp_api.h" -#include "freertos/portmacro.h" -#include "freertos/projdefs.h" -#include "input/device_factory.hpp" -#include "input/feedback_haptics.hpp" -#include "input/input_device.hpp" -#include "input/input_touch_wheel.hpp" -#include "input/input_volume_buttons.hpp" -#include "lua.h" -#include "lua.hpp" +#include "FreeRTOSConfig.h" +#include "lvgl.h" -#include "audio/audio_fsm.hpp" -#include "battery/battery.hpp" #include "core/lv_group.h" #include "core/lv_obj.h" #include "core/lv_obj_tree.h" -#include "database/database.hpp" -#include "drivers/haptics.hpp" #include "esp_heap_caps.h" +#include "esp_spp_api.h" #include "esp_timer.h" -#include "input/lvgl_input_driver.hpp" -#include "lauxlib.h" -#include "lua/lua_thread.hpp" +#include "freertos/portmacro.h" +#include "freertos/projdefs.h" +#include "lua.hpp" #include "luavgl.h" -#include "memory_resource.hpp" #include "misc/lv_gc.h" +#include "tinyfsm.hpp" +#include "widgets/lv_label.h" #include "audio/audio_events.hpp" +#include "audio/audio_fsm.hpp" #include "audio/track_queue.hpp" +#include "battery/battery.hpp" +#include "database/database.hpp" +#include "database/db_events.hpp" +#include "drivers/bluetooth_types.hpp" #include "drivers/display.hpp" +#include "drivers/display_init.hpp" #include "drivers/gpios.hpp" +#include "drivers/haptics.hpp" #include "drivers/nvs.hpp" #include "drivers/samd.hpp" #include "drivers/spiffs.hpp" #include "drivers/storage.hpp" #include "drivers/touchwheel.hpp" #include "events/event_queue.hpp" +#include "input/device_factory.hpp" +#include "input/feedback_haptics.hpp" +#include "input/input_device.hpp" +#include "input/input_touch_wheel.hpp" +#include "input/input_volume_buttons.hpp" +#include "input/lvgl_input_driver.hpp" #include "lua/lua_registry.hpp" +#include "lua/lua_thread.hpp" #include "lua/property.hpp" +#include "memory_resource.hpp" #include "system_fsm/system_events.hpp" -#include "tinyfsm.hpp" #include "ui/lvgl_task.hpp" #include "ui/screen.hpp" #include "ui/screen_lua.hpp" #include "ui/screen_splash.hpp" #include "ui/ui_events.hpp" -#include "widgets/lv_label.h" namespace ui { @@ -253,6 +253,8 @@ lua::Property UiState::sDatabaseAutoUpdate{ return true; }}; +lua::Property UiState::sSdMounted{false}; + lua::Property UiState::sUsbMassStorageEnabled{ false, [](const lua::LuaValue& val) { if (!std::holds_alternative(val)) { @@ -266,8 +268,8 @@ lua::Property UiState::sUsbMassStorageEnabled{ lua::Property UiState::sUsbMassStorageBusy{false}; -auto UiState::InitBootSplash(drivers::IGpios& gpios, - drivers::NvsStorage& nvs) -> bool { +auto UiState::InitBootSplash(drivers::IGpios& gpios, drivers::NvsStorage& nvs) + -> bool { events::Ui().Dispatch(internal::InitDisplay{ .gpios = gpios, .nvs = nvs, @@ -334,6 +336,10 @@ void UiState::react(const system_fsm::SamdUsbStatusChanged& ev) { drivers::Samd::UsbStatus::kAttachedBusy); } +void UiState::react(const system_fsm::SdStateChanged&) { + sSdMounted.setDirect(sServices->sd() == drivers::SdState::kMounted); +} + void UiState::react(const database::event::UpdateStarted&) { sDatabaseUpdating.setDirect(true); } @@ -444,7 +450,8 @@ void Splash::react(const system_fsm::BootComplete& ev) { sTask->input(sInput); } -void Splash::react(const system_fsm::StorageMounted&) { +void Splash::react(const system_fsm::SdStateChanged& ev) { + UiState::react(ev); transit(); } @@ -517,6 +524,7 @@ void Lua::entry() { { {"push", [&](lua_State* s) { return PushLuaScreen(s); }}, {"pop", [&](lua_State* s) { return PopLuaScreen(s); }}, + {"reset", [&](lua_State* s) { return ResetLuaScreen(s); }}, }); registry.AddPropertyModule( "alerts", { @@ -533,6 +541,9 @@ void Lua::entry() { {"updating", &sDatabaseUpdating}, {"auto_update", &sDatabaseAutoUpdate}, }); + registry.AddPropertyModule("sd_card", { + {"mounted", &sSdMounted}, + }); registry.AddPropertyModule("usb", { {"msc_enabled", &sUsbMassStorageEnabled}, @@ -547,7 +558,9 @@ void Lua::entry() { sBluetoothDevices.setDirect(bt.KnownDevices()); sCurrentScreen.reset(); - sLua->RunScript("/sdcard/config.lua"); + if (sServices->sd() == drivers::SdState::kMounted) { + sLua->RunScript("/sdcard/config.lua"); + } sLua->RunScript("/lua/main.lua"); } } @@ -587,16 +600,6 @@ auto Lua::PushLuaScreen(lua_State* s) -> int { return 0; } -auto Lua::QueueNext(lua_State*) -> int { - sServices->track_queue().next(); - return 0; -} - -auto Lua::QueuePrevious(lua_State*) -> int { - sServices->track_queue().previous(); - return 0; -} - auto Lua::PopLuaScreen(lua_State* s) -> int { if (!sCurrentScreen->canPop()) { return 0; @@ -607,6 +610,30 @@ auto Lua::PopLuaScreen(lua_State* s) -> int { return 0; } +auto Lua::ResetLuaScreen(lua_State* s) -> int { + if (sCurrentScreen) { + if (!sCurrentScreen->canPop()) { + ESP_LOGW(kTag, "ignoring reset as popping is blocked"); + return 0; + } + sCurrentScreen->onHidden(); + } + while (!sScreens.empty()) { + sScreens.pop(); + } + return PushLuaScreen(s); +} + +auto Lua::QueueNext(lua_State*) -> int { + sServices->track_queue().next(); + return 0; +} + +auto Lua::QueuePrevious(lua_State*) -> int { + sServices->track_queue().previous(); + return 0; +} + auto Lua::Ticks(lua_State* s) -> int { lua_pushinteger(s, esp_timer_get_time() / 1000); return 1; diff --git a/src/tangara/ui/ui_fsm.hpp b/src/tangara/ui/ui_fsm.hpp index 54d8a9ac..af8d75fb 100644 --- a/src/tangara/ui/ui_fsm.hpp +++ b/src/tangara/ui/ui_fsm.hpp @@ -10,6 +10,8 @@ #include #include +#include "tinyfsm.hpp" + #include "audio/audio_events.hpp" #include "audio/track_queue.hpp" #include "battery/battery.hpp" @@ -17,6 +19,9 @@ #include "database/track.hpp" #include "drivers/display.hpp" #include "drivers/gpios.hpp" +#include "drivers/nvs.hpp" +#include "drivers/storage.hpp" +#include "drivers/touchwheel.hpp" #include "input/device_factory.hpp" #include "input/feedback_haptics.hpp" #include "input/input_touch_wheel.hpp" @@ -24,12 +29,8 @@ #include "input/lvgl_input_driver.hpp" #include "lua/lua_thread.hpp" #include "lua/property.hpp" -#include "drivers/nvs.hpp" -#include "drivers/storage.hpp" #include "system_fsm/service_locator.hpp" #include "system_fsm/system_events.hpp" -#include "tinyfsm.hpp" -#include "drivers/touchwheel.hpp" #include "ui/lvgl_task.hpp" #include "ui/modal.hpp" #include "ui/screen.hpp" @@ -57,7 +58,7 @@ class UiState : public tinyfsm::Fsm { virtual void react(const DumpLuaStack&) {} virtual void react(const internal::BackPressed&) {} virtual void react(const system_fsm::BootComplete&) {} - virtual void react(const system_fsm::StorageMounted&) {} + virtual void react(const system_fsm::SdStateChanged&); void react(const system_fsm::BatteryStateChanged&); void react(const audio::PlaybackUpdate&); @@ -74,7 +75,7 @@ class UiState : public tinyfsm::Fsm { void react(const internal::DismissAlerts&); void react(const database::event::UpdateStarted&); - void react(const database::event::UpdateProgress&) {}; + void react(const database::event::UpdateProgress&){}; void react(const database::event::UpdateFinished&); void react(const system_fsm::BluetoothEvent&); @@ -86,7 +87,7 @@ class UiState : public tinyfsm::Fsm { sCurrentModal.reset(); } - void react(const internal::ReindexDatabase&) {}; + void react(const internal::ReindexDatabase&){}; protected: void PushScreen(std::shared_ptr); @@ -136,6 +137,8 @@ class UiState : public tinyfsm::Fsm { static lua::Property sDatabaseUpdating; static lua::Property sDatabaseAutoUpdate; + static lua::Property sSdMounted; + static lua::Property sUsbMassStorageEnabled; static lua::Property sUsbMassStorageBusy; }; @@ -147,7 +150,7 @@ class Splash : public UiState { void exit() override; void react(const system_fsm::BootComplete&) override; - void react(const system_fsm::StorageMounted&) override; + void react(const system_fsm::SdStateChanged&) override; using UiState::react; }; @@ -166,6 +169,7 @@ class Lua : public UiState { private: auto PushLuaScreen(lua_State*) -> int; auto PopLuaScreen(lua_State*) -> int; + auto ResetLuaScreen(lua_State*) -> int; auto ShowAlert(lua_State*) -> int; auto HideAlert(lua_State*) -> int;