summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-05-03 16:40:39 +1000
committerjacqueline <me@jacqueline.id.au>2024-05-03 16:40:39 +1000
commit344a46d0664eb75d232eacea91a4957a25e071f6 (patch)
treee6ee54d9c8060af3d3bb94ec4b79e9d505b270e5
parent3ceb8025ee4330c177101ed30ec17dfb0002f41e (diff)
downloadtangara-fw-344a46d0664eb75d232eacea91a4957a25e071f6.tar.gz
Respond to sd card mounts and unmounts within lua
Includes no longer blocking the main menu on an sd card being inserted!!
-rw-r--r--lua/main.lua16
-rw-r--r--src/tangara/audio/audio_fsm.cpp13
-rw-r--r--src/tangara/audio/audio_fsm.hpp10
-rw-r--r--src/tangara/system_fsm/running.cpp60
-rw-r--r--src/tangara/system_fsm/system_events.hpp10
-rw-r--r--src/tangara/system_fsm/system_fsm.hpp8
-rw-r--r--src/tangara/ui/ui_fsm.cpp101
-rw-r--r--src/tangara/ui/ui_fsm.hpp20
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<void>([]() {
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<Standby>();
+ }
+}
+
} // 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<AudioState> {
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::Database>{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 <memory>
#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<SystemState> {
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 <memory_resource>
#include <variant>
-#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<bool>(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<Lua>();
}
@@ -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 <memory>
#include <stack>
+#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<UiState> {
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<UiState> {
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<UiState> {
sCurrentModal.reset();
}
- void react(const internal::ReindexDatabase&) {};
+ void react(const internal::ReindexDatabase&){};
protected:
void PushScreen(std::shared_ptr<Screen>);
@@ -136,6 +137,8 @@ class UiState : public tinyfsm::Fsm<UiState> {
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;