From 6586819171b635e3b17effb3bfab096da695a0e9 Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 1 Mar 2024 19:08:54 +1100 Subject: [PATCH 01/10] WIP Commit- Lua setters for volume, bluetooth todo --- src/audio/audio_fsm.cpp | 19 ++++++++++++++++++- src/audio/bt_audio_output.cpp | 12 ++++++++++++ src/audio/i2s_audio_output.cpp | 14 ++++++++++++++ src/audio/include/audio_sink.hpp | 3 +++ src/audio/include/bt_audio_output.hpp | 2 ++ src/audio/include/i2s_audio_output.hpp | 2 ++ 6 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index ea0315eb..7800802e 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -98,7 +98,24 @@ void AudioState::react(const system_fsm::HasPhonesChanged& ev) { } void AudioState::react(const SetVolume& ev) { - // TODO. + if (ev.db.has_value()) { + if (sOutput->SetVolumeDb(ev.db.value())) { + commitVolume(); + events::Ui().Dispatch(VolumeChanged{ + .percent = sOutput->GetVolumePct(), + .db = sOutput->GetVolumeDb(), + }); + } + + } else if (ev.percent.has_value()) { + if (sOutput->SetVolumePct(ev.percent.value())) { + commitVolume(); + events::Ui().Dispatch(VolumeChanged{ + .percent = sOutput->GetVolumePct(), + .db = sOutput->GetVolumeDb(), + }); + } + } } void AudioState::react(const SetVolumeLimit& ev) { diff --git a/src/audio/bt_audio_output.cpp b/src/audio/bt_audio_output.cpp index 41c89069..6deb1f71 100644 --- a/src/audio/bt_audio_output.cpp +++ b/src/audio/bt_audio_output.cpp @@ -57,10 +57,22 @@ auto BluetoothAudioOutput::GetVolumePct() -> uint_fast8_t { return static_cast(static_cast(volume_) * 100 / 0x7f); } +auto BluetoothAudioOutput::SetVolumePct(uint_fast8_t val) -> bool { + // TODO + ESP_LOGE(kTag, "Not implemented"); + return false; +} + auto BluetoothAudioOutput::GetVolumeDb() -> int_fast16_t { return 0; } +auto BluetoothAudioOutput::SetVolumeDb(int_fast16_t) -> bool { + // TODO + ESP_LOGE(kTag, "Not implemented"); + return false; +} + auto BluetoothAudioOutput::AdjustVolumeUp() -> bool { if (volume_ == 0x7f) { return false; diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index 4043574e..7739fa17 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -111,6 +111,15 @@ auto I2SAudioOutput::GetVolumePct() -> uint_fast8_t { return (current_volume_ - kMinVolume) * 100 / (max_volume_ - kMinVolume); } +auto I2SAudioOutput::SetVolumePct(uint_fast8_t val) -> bool { + if (val > 100) { + return false; + } + uint16_t vol = (val * (max_volume_ - kMinVolume))/100 + kMinVolume; + SetVolume(vol); + return true; +} + auto I2SAudioOutput::GetVolumeDb() -> int_fast16_t { // Add two before dividing in order to round correctly. return (static_cast(current_volume_) - @@ -118,6 +127,11 @@ auto I2SAudioOutput::GetVolumeDb() -> int_fast16_t { 4; } +auto I2SAudioOutput::SetVolumeDb(int_fast16_t val) -> bool { + SetVolume(val * 4 + static_cast(drivers::wm8523::kLineLevelReferenceVolume) - 2); + return true; +} + auto I2SAudioOutput::AdjustVolumeUp() -> bool { if (GetVolume() >= max_volume_) { return false; diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index eba55eb5..116410f6 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -52,6 +52,9 @@ class IAudioOutput { virtual auto GetVolumePct() -> uint_fast8_t = 0; virtual auto GetVolumeDb() -> int_fast16_t = 0; + virtual auto SetVolumePct(uint_fast8_t) -> bool = 0; + virtual auto SetVolumeDb(int_fast16_t) -> bool = 0; + virtual auto AdjustVolumeUp() -> bool = 0; virtual auto AdjustVolumeDown() -> bool = 0; diff --git a/src/audio/include/bt_audio_output.hpp b/src/audio/include/bt_audio_output.hpp index f6d2200c..dff25131 100644 --- a/src/audio/include/bt_audio_output.hpp +++ b/src/audio/include/bt_audio_output.hpp @@ -37,7 +37,9 @@ class BluetoothAudioOutput : public IAudioOutput { auto GetVolume() -> uint16_t override; auto GetVolumePct() -> uint_fast8_t override; + auto SetVolumePct(uint_fast8_t val) -> bool override; auto GetVolumeDb() -> int_fast16_t override; + auto SetVolumeDb(int_fast16_t) -> bool override; auto AdjustVolumeUp() -> bool override; auto AdjustVolumeDown() -> bool override; diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 7c297106..538eafb6 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -35,7 +35,9 @@ class I2SAudioOutput : public IAudioOutput { auto GetVolume() -> uint16_t override; auto GetVolumePct() -> uint_fast8_t override; + auto SetVolumePct(uint_fast8_t val) -> bool override; auto GetVolumeDb() -> int_fast16_t override; + auto SetVolumeDb(int_fast16_t) -> bool override; auto AdjustVolumeUp() -> bool override; auto AdjustVolumeDown() -> bool override; From a750af35aa6afda40aadca8f7cf8db75f41a43b2 Mon Sep 17 00:00:00 2001 From: ailurux Date: Tue, 2 Apr 2024 11:11:44 +1100 Subject: [PATCH 02/10] Added bluetooth db impl --- src/audio/bt_audio_output.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/audio/bt_audio_output.cpp b/src/audio/bt_audio_output.cpp index 6deb1f71..f431a49b 100644 --- a/src/audio/bt_audio_output.cpp +++ b/src/audio/bt_audio_output.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -58,19 +59,22 @@ auto BluetoothAudioOutput::GetVolumePct() -> uint_fast8_t { } auto BluetoothAudioOutput::SetVolumePct(uint_fast8_t val) -> bool { - // TODO - ESP_LOGE(kTag, "Not implemented"); - return false; + if (val < 100) { + return false; + } + SetVolume(val / 100 * 0x7f); + return true; } auto BluetoothAudioOutput::GetVolumeDb() -> int_fast16_t { - return 0; + return log(GetVolumePct()/100) * 20; } -auto BluetoothAudioOutput::SetVolumeDb(int_fast16_t) -> bool { - // TODO - ESP_LOGE(kTag, "Not implemented"); - return false; +auto BluetoothAudioOutput::SetVolumeDb(int_fast16_t val) -> bool { + auto pct = pow(2, val / 20.0) * 100; + ESP_LOGI("Audio", "Bluetooth audio pct: %d", (int)pct); + SetVolumePct(pct); + return true; } auto BluetoothAudioOutput::AdjustVolumeUp() -> bool { From 25dca40e5dab82c5a814233813c24180fcf975eb Mon Sep 17 00:00:00 2001 From: ailurux Date: Tue, 2 Apr 2024 15:03:49 +1100 Subject: [PATCH 03/10] Fix volume getters/setters for bluetooth --- src/audio/bt_audio_output.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/audio/bt_audio_output.cpp b/src/audio/bt_audio_output.cpp index 20ed7bb3..7d6bade2 100644 --- a/src/audio/bt_audio_output.cpp +++ b/src/audio/bt_audio_output.cpp @@ -55,26 +55,30 @@ auto BluetoothAudioOutput::GetVolume() -> uint16_t { } auto BluetoothAudioOutput::GetVolumePct() -> uint_fast8_t { - return static_cast(static_cast(volume_) * 100 / 0x7f); + return static_cast(round(static_cast(volume_) * 100.0 / 0x7f)); } auto BluetoothAudioOutput::SetVolumePct(uint_fast8_t val) -> bool { - if (val < 100) { + if (val > 100) { return false; } - SetVolume(val / 100 * 0x7f); + uint16_t vol = (val * (0x7f))/100; + SetVolume(vol); return true; } auto BluetoothAudioOutput::GetVolumeDb() -> int_fast16_t { - return log(GetVolumePct()/100) * 20; + double pct = GetVolumePct()/100.0; + if (pct <= 0) { + pct = 0.01; + } + int_fast16_t db = log(pct) * 20; + return db; } auto BluetoothAudioOutput::SetVolumeDb(int_fast16_t val) -> bool { - auto pct = pow(2, val / 20.0) * 100; - ESP_LOGI("Audio", "Bluetooth audio pct: %d", (int)pct); - SetVolumePct(pct); - return true; + double pct = exp(val / 20.0) * 100; + return SetVolumePct(pct); } auto BluetoothAudioOutput::AdjustVolumeUp() -> bool { From 1e278d55c4dc0a9ae7b5b4511904202824e5c5df Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 2 Apr 2024 19:36:35 +1100 Subject: [PATCH 04/10] tweak db performance - leveldb cache pinned to spiram - actually use it during indexing lol - all up, saves about 10ms per file (amortised) for an incremental reindex --- lib/leveldb/util/cache.cc | 21 +++++++++++++++------ src/database/database.cpp | 8 ++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/leveldb/util/cache.cc b/lib/leveldb/util/cache.cc index ad1e9a28..043b37a3 100644 --- a/lib/leveldb/util/cache.cc +++ b/lib/leveldb/util/cache.cc @@ -8,6 +8,7 @@ #include #include +#include "esp_heap_caps.h" #include "port/port.h" #include "port/thread_annotations.h" #include "util/hash.h" @@ -157,7 +158,9 @@ class LRUCache { void SetCapacity(size_t capacity) { capacity_ = capacity; } // Like Cache methods, but with an extra "hash" parameter. - Cache::Handle* Insert(const Slice& key, uint32_t hash, void* value, + Cache::Handle* Insert(const Slice& key, + uint32_t hash, + void* value, size_t charge, void (*deleter)(const Slice& key, void* value)); Cache::Handle* Lookup(const Slice& key, uint32_t hash); @@ -264,14 +267,16 @@ void LRUCache::Release(Cache::Handle* handle) { Unref(reinterpret_cast(handle)); } -Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value, +Cache::Handle* LRUCache::Insert(const Slice& key, + uint32_t hash, + void* value, size_t charge, void (*deleter)(const Slice& key, void* value)) { MutexLock l(&mutex_); - LRUHandle* e = - reinterpret_cast(malloc(sizeof(LRUHandle) - 1 + key.size())); + LRUHandle* e = reinterpret_cast( + heap_caps_malloc(sizeof(LRUHandle) - 1 + key.size(), MALLOC_CAP_SPIRAM)); e->value = value; e->deleter = deleter; e->charge = charge; @@ -356,7 +361,9 @@ class ShardedLRUCache : public Cache { } } ~ShardedLRUCache() override {} - Handle* Insert(const Slice& key, void* value, size_t charge, + Handle* Insert(const Slice& key, + void* value, + size_t charge, void (*deleter)(const Slice& key, void* value)) override { const uint32_t hash = HashSlice(key); return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); @@ -396,6 +403,8 @@ class ShardedLRUCache : public Cache { } // end anonymous namespace -Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); } +Cache* NewLRUCache(size_t capacity) { + return new ShardedLRUCache(capacity); +} } // namespace leveldb diff --git a/src/database/database.cpp b/src/database/database.cpp index 06138983..48fb0c63 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -139,14 +139,14 @@ auto Database::Open(IFileGatherer& gatherer, [&]() -> cpp::result { leveldb::DB* db; std::unique_ptr cache{ - leveldb::NewLRUCache(4 * 1024)}; + leveldb::NewLRUCache(256 * 1024)}; leveldb::Options options; options.env = sEnv.env(); options.write_buffer_size = 4 * 1024; - options.max_file_size = 32; + options.max_file_size = 16 * 1024; options.block_cache = cache.get(); - options.block_size = 512; + options.block_size = 2048; auto status = leveldb::DB::Open(options, kDbPath, &db); if (!status.ok()) { @@ -299,7 +299,7 @@ auto Database::updateIndexes() -> void { UpdateNotifier notifier{is_updating_}; leveldb::ReadOptions read_options; - read_options.fill_cache = false; + read_options.fill_cache = true; // Stage 1: verify all existing tracks are still valid. ESP_LOGI(kTag, "verifying existing tracks"); From 654fde5f6819cb52a198a524ab78d97e51ab97c7 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 2 Apr 2024 20:41:36 +1100 Subject: [PATCH 05/10] Support disabling automatic database updates --- lua/settings.lua | 136 ++++++++++++++++++++++-------------- src/drivers/include/nvs.hpp | 4 ++ src/drivers/nvs.cpp | 14 ++++ src/system_fsm/running.cpp | 16 +++-- src/ui/include/ui_fsm.hpp | 1 + src/ui/ui_fsm.cpp | 18 ++++- 6 files changed, 125 insertions(+), 64 deletions(-) diff --git a/lua/settings.lua b/lua/settings.lua index d19a6180..1e2debea 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -54,10 +54,10 @@ local BluetoothSettings = screen:new { bluetooth.enabled:set(enabled) end) - theme.set_style(self.menu.content:Label { - text = "Paired Device", - pad_bottom = 1, - }, "settings_title") + theme.set_style(self.menu.content:Label { + text = "Paired Device", + pad_bottom = 1, + }, "settings_title") local paired_container = self.menu.content:Object { flex = { @@ -80,10 +80,10 @@ local BluetoothSettings = screen:new { bluetooth.paired_device:set() end) - theme.set_style(self.menu.content:Label { - text = "Nearby Devices", - pad_bottom = 1, - }, "settings_title") + theme.set_style(self.menu.content:Label { + text = "Nearby Devices", + pad_bottom = 1, + }, "settings_title") local devices = self.menu.content:List { w = lvgl.PCT(100), @@ -123,9 +123,9 @@ local HeadphonesSettings = screen:new { createUi = function(self) self.menu = SettingsScreen("Headphones") - theme.set_style(self.menu.content:Label { - text = "Maxiumum volume limit", - }, "settings_title") + theme.set_style(self.menu.content:Label { + text = "Maxiumum volume limit", + }, "settings_title") local volume_chooser = self.menu.content:Dropdown { options = "Line Level (-10 dB)\nCD Level (+6 dB)\nMaximum (+10dB)", @@ -138,9 +138,9 @@ local HeadphonesSettings = screen:new { volume.limit_db:set(limits[selection]) end) - theme.set_style(self.menu.content:Label { - text = "Left/Right balance", - }, "settings_title") + theme.set_style(self.menu.content:Label { + text = "Left/Right balance", + }, "settings_title") local balance = self.menu.content:Slider { w = lvgl.PCT(100), @@ -186,20 +186,20 @@ local DisplaySettings = screen:new { createUi = function(self) self.menu = SettingsScreen("Display") - local brightness_title = self.menu.content:Object { - flex = { - flex_direction = "row", - justify_content = "flex-start", - align_items = "flex-start", - align_content = "flex-start", - }, - w = lvgl.PCT(100), - h = lvgl.SIZE_CONTENT, + local brightness_title = self.menu.content:Object { + flex = { + flex_direction = "row", + justify_content = "flex-start", + align_items = "flex-start", + align_content = "flex-start", + }, + w = lvgl.PCT(100), + h = lvgl.SIZE_CONTENT, - } - brightness_title:Label { text = "Brightness", flex_grow = 1 } - local brightness_pct = brightness_title:Label {} - theme.set_style(brightness_pct, "settings_title") + } + brightness_title:Label { text = "Brightness", flex_grow = 1 } + local brightness_pct = brightness_title:Label {} + theme.set_style(brightness_pct, "settings_title") local brightness = self.menu.content:Slider { w = lvgl.PCT(100), @@ -223,9 +223,9 @@ local InputSettings = screen:new { createUi = function(self) self.menu = SettingsScreen("Input Method") - theme.set_style(self.menu.content:Label { - text = "Control scheme", - }, "settings_title") + theme.set_style(self.menu.content:Label { + text = "Control scheme", + }, "settings_title") local schemes = controls.schemes() local option_to_scheme = {} @@ -261,9 +261,9 @@ local InputSettings = screen:new { controls.scheme:set(scheme) end) - theme.set_style(self.menu.content:Label { - text = "Scroll Sensitivity", - }, "settings_title") + theme.set_style(self.menu.content:Label { + text = "Scroll Sensitivity", + }, "settings_title") local slider_scale = 4; -- Power steering local sensitivity = self.menu.content:Slider { @@ -333,25 +333,53 @@ local DatabaseSettings = screen:new { widgets.Row(self.menu.content, "Schema version", db.version()) widgets.Row(self.menu.content, "Size on disk", string.format("%.1f KiB", db.size() / 1024)) - local actions_container = self.menu.content:Object { - w = lvgl.PCT(100), - h = lvgl.SIZE_CONTENT, - flex = { - flex_direction = "row", - justify_content = "center", - align_items = "space-evenly", - align_content = "center", - }, - pad_top = 4, - pad_column = 4, - } - actions_container:add_style(styles.list_item) + local auto_update_container = self.menu.content:Object { + flex = { + flex_direction = "row", + justify_content = "flex-start", + align_items = "flex-start", + align_content = "flex-start", + }, + w = lvgl.PCT(100), + h = lvgl.SIZE_CONTENT, + } + auto_update_container:add_style(styles.list_item) + auto_update_container:Label { text = "Auto update", flex_grow = 1 } + local auto_update_sw = auto_update_container:Switch {} + + auto_update_sw:onevent(lvgl.EVENT.VALUE_CHANGED, function() + database.auto_update:set(auto_update_sw:enabled()) + end) + + local actions_container = self.menu.content:Object { + w = lvgl.PCT(100), + h = lvgl.SIZE_CONTENT, + flex = { + flex_direction = "row", + justify_content = "center", + align_items = "space-evenly", + align_content = "center", + }, + pad_top = 4, + pad_column = 4, + } + actions_container:add_style(styles.list_item) local update = actions_container:Button {} - update:Label { text = "Update" } + update:Label { text = "Update now" } update:onClicked(function() database.update() end) + + self.bindings = { + database.auto_update:bind(function(en) + if en then + auto_update_sw:add_state(lvgl.STATE.CHECKED) + else + auto_update_sw:clear_state(lvgl.STATE.CHECKED) + end + end), + } end } @@ -383,13 +411,13 @@ return screen:new { flex_grow = 1, } - local function section(name) - local elem = self.list:Label { - text = name, - pad_left = 4, - } - theme.set_style(elem, "settings_title") - end + local function section(name) + local elem = self.list:Label { + text = name, + pad_left = 4, + } + theme.set_style(elem, "settings_title") + end local function submenu(name, class) local item = self.list:add_btn(nil, name) diff --git a/src/drivers/include/nvs.hpp b/src/drivers/include/nvs.hpp index f288f8e2..25396622 100644 --- a/src/drivers/include/nvs.hpp +++ b/src/drivers/include/nvs.hpp @@ -114,6 +114,9 @@ class NvsStorage { auto PrimaryInput() -> InputModes; auto PrimaryInput(InputModes) -> void; + auto DbAutoIndex() -> bool; + auto DbAutoIndex(bool) -> void; + explicit NvsStorage(nvs_handle_t); ~NvsStorage(); @@ -136,6 +139,7 @@ class NvsStorage { Setting input_mode_; Setting output_mode_; Setting bt_preferred_; + Setting db_auto_index_; util::LruCache<10, bluetooth::mac_addr_t, uint8_t> bt_volumes_; bool bt_volumes_dirty_; diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 28cb542c..33d92a9f 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -39,6 +39,7 @@ static constexpr char kKeyScrollSensitivity[] = "scroll"; static constexpr char kKeyLockPolarity[] = "lockpol"; static constexpr char kKeyDisplayCols[] = "dispcols"; static constexpr char kKeyDisplayRows[] = "disprows"; +static constexpr char kKeyDbAutoIndex[] = "dbautoindex"; static auto nvs_get_string(nvs_handle_t nvs, const char* key) -> std::optional { @@ -173,6 +174,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle) input_mode_(kKeyPrimaryInput), output_mode_(kKeyOutput), bt_preferred_(kKeyBluetoothPreferred), + db_auto_index_(kKeyDbAutoIndex), bt_volumes_(), bt_volumes_dirty_(false) {} @@ -194,6 +196,7 @@ auto NvsStorage::Read() -> void { input_mode_.read(handle_); output_mode_.read(handle_); bt_preferred_.read(handle_); + db_auto_index_.read(handle_); readBtVolumes(); } @@ -210,6 +213,7 @@ auto NvsStorage::Write() -> bool { input_mode_.write(handle_); output_mode_.write(handle_); bt_preferred_.write(handle_); + db_auto_index_.write(handle_); writeBtVolumes(); return nvs_commit(handle_) == ESP_OK; } @@ -370,6 +374,16 @@ auto NvsStorage::PrimaryInput(InputModes mode) -> void { input_mode_.set(static_cast(mode)); } +auto NvsStorage::DbAutoIndex() -> bool { + std::lock_guard lock{mutex_}; + return db_auto_index_.get().value_or(true); +} + +auto NvsStorage::DbAutoIndex(bool en) -> void { + std::lock_guard lock{mutex_}; + db_auto_index_.set(static_cast(en)); +} + class VolumesParseClient : public cppbor::ParseClient { public: VolumesParseClient(util::LruCache<10, bluetooth::mac_addr_t, uint8_t>& out) diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp index a6ab5d47..8625ac66 100644 --- a/src/system_fsm/running.cpp +++ b/src/system_fsm/running.cpp @@ -166,13 +166,15 @@ auto Running::mountStorage() -> bool { // Tell the database to refresh so that we pick up any changes from the newly // mounted card. - sServices->bg_worker().Dispatch([&]() { - auto db = sServices->database().lock(); - if (!db) { - return; - } - db->updateIndexes(); - }); + if (sServices->nvs().DbAutoIndex()) { + sServices->bg_worker().Dispatch([&]() { + auto db = sServices->database().lock(); + if (!db) { + return; + } + db->updateIndexes(); + }); + } return true; } diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 5e1cc487..9f9aa9b9 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -130,6 +130,7 @@ class UiState : public tinyfsm::Fsm { static lua::Property sLockSwitch; static lua::Property sDatabaseUpdating; + static lua::Property sDatabaseAutoUpdate; static lua::Property sUsbMassStorageEnabled; }; diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 835da19e..bb1503b2 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -282,6 +282,14 @@ lua::Property UiState::sScrollSensitivity{ lua::Property UiState::sLockSwitch{false}; lua::Property UiState::sDatabaseUpdating{false}; +lua::Property UiState::sDatabaseAutoUpdate{ + false, [](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + sServices->nvs().DbAutoIndex(std::get(val)); + return true; + }}; lua::Property UiState::sUsbMassStorageEnabled{ false, [](const lua::LuaValue& val) { @@ -557,14 +565,18 @@ void Lua::entry() { "time", { {"ticks", [&](lua_State* s) { return Ticks(s); }}, }); - registry.AddPropertyModule("database", { - {"updating", &sDatabaseUpdating}, - }); + registry.AddPropertyModule("database", + { + {"updating", &sDatabaseUpdating}, + {"auto_update", &sDatabaseAutoUpdate}, + }); registry.AddPropertyModule("usb", { {"msc_enabled", &sUsbMassStorageEnabled}, }); + sDatabaseAutoUpdate.Update(sServices->nvs().DbAutoIndex()); + auto bt = sServices->bluetooth(); sBluetoothEnabled.Update(bt.IsEnabled()); sBluetoothConnected.Update(bt.IsConnected()); From 63bc8cf896bc2fbe7cda123cfe1479461f985e76 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 2 Apr 2024 21:19:54 +1100 Subject: [PATCH 06/10] Ensure we always clean up after ourselves when invoking bindings this fixes a very nasty lua stack leak --- src/lua/property.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lua/property.cpp b/src/lua/property.cpp index 200f4d5c..e136ad90 100644 --- a/src/lua/property.cpp +++ b/src/lua/property.cpp @@ -379,22 +379,22 @@ auto Property::Update(const LuaValue& v) -> void { for (int i = bindings_.size() - 1; i >= 0; i--) { auto& b = bindings_[i]; + int top = lua_gettop(b.first); + lua_pushstring(b.first, kBindingsTable); lua_gettable(b.first, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable] int type = lua_rawgeti(b.first, -1, b.second); // push bindings[i] // Has closure has been GCed? if (type == LUA_TNIL) { - // Clean up after ourselves. - lua_pop(b.first, 1); // Remove the binding. bindings_.erase(bindings_.begin() + i); - continue; + } else { + PushValue(*b.first); // push the argument + CallProtected(b.first, 1, 0); // invoke the closure } - PushValue(*b.first); // push the argument - CallProtected(b.first, 1, 0); // invoke the closure - lua_pop(b.first, 1); // pop the bindings table + lua_settop(b.first, top); // clean up after ourselves } } From 673b6a33ef5eea8e4fc0d416520670679a3985a8 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 2 Apr 2024 21:20:37 +1100 Subject: [PATCH 07/10] version bump --- tools/cmake/common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cmake/common.cmake b/tools/cmake/common.cmake index e375ffff..6dbafabd 100644 --- a/tools/cmake/common.cmake +++ b/tools/cmake/common.cmake @@ -5,7 +5,7 @@ # For more information about build system see # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html -set(PROJECT_VER "0.8.0") +set(PROJECT_VER "0.8.1") # esp-idf sets the C++ standard weird. Set cmake vars to match. set(CMAKE_CXX_STANDARD 23) From 251c0ba96dadf8d28406095ec2189466b631f7f8 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 4 Apr 2024 15:11:32 +1100 Subject: [PATCH 08/10] Fix null poiner dereference when turning off i2s output twice --- src/audio/i2s_audio_output.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index 2a251685..bf1c3e5e 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -63,16 +63,21 @@ auto I2SAudioOutput::changeMode(Modes mode) -> void { return; } if (mode == Modes::kOff) { - dac_->Stop(); - dac_.reset(); + if (dac_) { + dac_->Stop(); + dac_.reset(); + } return; - } else if (current_mode_ == Modes::kOff) { - auto instance = drivers::I2SDac::create(expander_); - if (!instance) { - return; + } + if (current_mode_ == Modes::kOff) { + if (!dac_) { + auto instance = drivers::I2SDac::create(expander_); + if (!instance) { + return; + } + dac_.reset(*instance); } SetVolume(GetVolume()); - dac_.reset(*instance); dac_->SetSource(stream()); dac_->Start(); } From 5f1a6507d8f7a0f9ecea179f94aeb182eac77cfe Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 8 Apr 2024 13:50:35 +1000 Subject: [PATCH 09/10] Warn and block MSC disable if the sd card is busy --- lua/settings.lua | 21 ++++++++++++++++++--- src/drivers/include/samd.hpp | 4 ++-- src/drivers/samd.cpp | 2 +- src/system_fsm/include/system_events.hpp | 4 ++++ src/system_fsm/system_fsm.cpp | 2 +- src/ui/include/ui_fsm.hpp | 2 ++ src/ui/ui_fsm.cpp | 9 +++++++++ 7 files changed, 37 insertions(+), 7 deletions(-) diff --git a/lua/settings.lua b/lua/settings.lua index 1e2debea..1a306dcf 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -282,10 +282,10 @@ local MassStorageSettings = screen:new { createUi = function(self) self.menu = SettingsScreen("USB Storage") local version = require("version").samd() - if tonumber(version) < 2 then + if tonumber(version) < 3 then self.menu.content:Label { w = lvgl.PCT(100), - text = "Usb Mass Storage requires a SAMD21 firmware version >=2." + text = "Usb Mass Storage requires a SAMD21 firmware version >=3." } return end @@ -304,6 +304,12 @@ local MassStorageSettings = screen:new { enable_container:Label { text = "Enable", flex_grow = 1 } local enable_sw = enable_container:Switch {} + local busy_text = self.menu.content:Label { + w = lvgl.PCT(100), + text = "USB is currently busy. Do not unplug or remove the SD card.", + long_mode = lvgl.LABEL.LONG_WRAP, + } + local bind_switch = function() if usb.msc_enabled:get() then enable_sw:add_state(lvgl.STATE.CHECKED) @@ -313,12 +319,21 @@ local MassStorageSettings = screen:new { end enable_sw:onevent(lvgl.EVENT.VALUE_CHANGED, function() - usb.msc_enabled:set(enable_sw:enabled()) + if not usb.msc_busy:get() then + usb.msc_enabled:set(enable_sw:enabled()) + end bind_switch() end) self.bindings = { usb.msc_enabled:bind(bind_switch), + usb.msc_busy:bind(function(busy) + if busy then + busy_text:clear_flag(lvgl.FLAG.HIDDEN) + else + busy_text:add_flag(lvgl.FLAG.HIDDEN) + end + end) } end, canPop = function() diff --git a/src/drivers/include/samd.hpp b/src/drivers/include/samd.hpp index ac265950..55ea513c 100644 --- a/src/drivers/include/samd.hpp +++ b/src/drivers/include/samd.hpp @@ -48,8 +48,8 @@ class Samd { // There is a compatible usb host attached, but USB MSC is not currently // in use by the SAMD. kAttachedIdle, - // The SAMD is currently exposing the SD card via USB MSC. - kAttachedMounted, + // The SAMD is currently writing to the SD card via USB MSC. + kAttachedBusy, }; auto GetUsbStatus() -> UsbStatus; diff --git a/src/drivers/samd.cpp b/src/drivers/samd.cpp index b631b4fb..e47d9cfe 100644 --- a/src/drivers/samd.cpp +++ b/src/drivers/samd.cpp @@ -113,7 +113,7 @@ auto Samd::UpdateUsbStatus() -> void { usb_status_ = UsbStatus::kDetached; } usb_status_ = - (raw_res & 0b10) ? UsbStatus::kAttachedMounted : UsbStatus::kAttachedIdle; + (raw_res & 0b10) ? UsbStatus::kAttachedBusy : UsbStatus::kAttachedIdle; } auto Samd::ResetToFlashSamd() -> void { diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp index 1be03f82..f9ab9e11 100644 --- a/src/system_fsm/include/system_events.hpp +++ b/src/system_fsm/include/system_events.hpp @@ -12,6 +12,7 @@ #include "bluetooth_types.hpp" #include "database.hpp" #include "haptics.hpp" +#include "samd.hpp" #include "service_locator.hpp" #include "tinyfsm.hpp" @@ -56,6 +57,9 @@ struct SdDetectChanged : tinyfsm::Event { struct SamdUsbMscChanged : tinyfsm::Event { bool en; }; +struct SamdUsbStatusChanged : tinyfsm::Event { + drivers::Samd::UsbStatus new_status; +}; struct BatteryStateChanged : tinyfsm::Event { battery::Battery::BatteryState new_state; diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp index 5a1ccf8c..f502b49a 100644 --- a/src/system_fsm/system_fsm.cpp +++ b/src/system_fsm/system_fsm.cpp @@ -88,7 +88,7 @@ void SystemState::react(const internal::SamdInterrupt&) { sServices->battery().Update(); } if (usb_status != prev_usb_status) { - ESP_LOGI(kTag, "usb status changed"); + events::Ui().Dispatch(SamdUsbStatusChanged{.new_status = usb_status}); } } diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 9f9aa9b9..2bab487d 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -65,6 +65,7 @@ class UiState : public tinyfsm::Fsm { void react(const audio::VolumeLimitChanged&); void react(const system_fsm::KeyLockChanged&); + void react(const system_fsm::SamdUsbStatusChanged&); void react(const internal::DismissAlerts&); void react(const internal::ControlSchemeChanged&); @@ -133,6 +134,7 @@ class UiState : public tinyfsm::Fsm { static lua::Property sDatabaseAutoUpdate; static lua::Property sUsbMassStorageEnabled; + static lua::Property sUsbMassStorageBusy; }; namespace states { diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index bb1503b2..28733123 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -43,6 +43,7 @@ #include "nvs.hpp" #include "property.hpp" #include "relative_wheel.hpp" +#include "samd.hpp" #include "screen.hpp" #include "screen_lua.hpp" #include "screen_splash.hpp" @@ -302,6 +303,8 @@ lua::Property UiState::sUsbMassStorageEnabled{ return true; }}; +lua::Property UiState::sUsbMassStorageBusy{false}; + auto UiState::InitBootSplash(drivers::IGpios& gpios, drivers::NvsStorage& nvs) -> bool { // Init LVGL first, since the display driver registers itself with LVGL. @@ -360,6 +363,11 @@ void UiState::react(const system_fsm::KeyLockChanged& ev) { sLockSwitch.Update(ev.locking); } +void UiState::react(const system_fsm::SamdUsbStatusChanged& ev) { + sUsbMassStorageBusy.Update(ev.new_status == + drivers::Samd::UsbStatus::kAttachedBusy); +} + void UiState::react(const internal::ControlSchemeChanged&) { if (!sInput) { return; @@ -573,6 +581,7 @@ void Lua::entry() { registry.AddPropertyModule("usb", { {"msc_enabled", &sUsbMassStorageEnabled}, + {"msc_busy", &sUsbMassStorageBusy}, }); sDatabaseAutoUpdate.Update(sServices->nvs().DbAutoIndex()); From 96b62321c33ff5e146d52416dc5da3f0c240b4b0 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 8 Apr 2024 15:29:57 +1000 Subject: [PATCH 10/10] fix some issues with settings screen styling --- lua/settings.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/settings.lua b/lua/settings.lua index 1a306dcf..282fc04a 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -24,7 +24,7 @@ local function SettingsScreen(title) align_items = "flex-start", align_content = "flex-start", }, - w = lvgl.PCT(90), + w = lvgl.PCT(100), flex_grow = 1, pad_left = 4, pad_right = 4, @@ -40,7 +40,7 @@ local BluetoothSettings = screen:new { flex = { flex_direction = "row", justify_content = "flex-start", - align_items = "content", + align_items = "center", align_content = "flex-start", }, w = lvgl.PCT(100), @@ -294,7 +294,7 @@ local MassStorageSettings = screen:new { flex = { flex_direction = "row", justify_content = "flex-start", - align_items = "content", + align_items = "center", align_content = "flex-start", }, w = lvgl.PCT(100),