Respond to sd card mounts and unmounts within lua

Includes no longer blocking the main menu on an sd card being inserted!!
custom
jacqueline 12 months ago
parent 3ceb8025ee
commit 344a46d066
  1. 16
      lua/main.lua
  2. 13
      src/tangara/audio/audio_fsm.cpp
  3. 10
      src/tangara/audio/audio_fsm.hpp
  4. 58
      src/tangara/system_fsm/running.cpp
  5. 10
      src/tangara/system_fsm/system_events.hpp
  6. 8
      src/tangara/system_fsm/system_fsm.hpp
  7. 99
      src/tangara/ui/ui_fsm.cpp
  8. 20
      src/tangara/ui/ui_fsm.hpp

@ -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)

@ -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

@ -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;
};

@ -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();
}
}
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

@ -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;

@ -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_;

@ -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();
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;

@ -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;

Loading…
Cancel
Save