From 4247c9fe7d25c921fbfc73fc50e849c8780e7ad6 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 29 Aug 2023 16:07:56 +1000 Subject: [PATCH] store the screen brightness in nvs --- src/drivers/bluetooth.cpp | 4 +- src/drivers/include/nvs.hpp | 25 ++++-- src/drivers/nvs.cpp | 135 +++++++++++++++++++---------- src/system_fsm/booting.cpp | 2 +- src/tasks/tasks.cpp | 21 +++++ src/tasks/tasks.hpp | 1 + src/ui/include/screen_settings.hpp | 2 + src/ui/include/themes.hpp | 6 +- src/ui/screen_settings.cpp | 28 +++++- src/ui/themes.cpp | 14 +-- src/ui/ui_fsm.cpp | 1 + 11 files changed, 168 insertions(+), 71 deletions(-) diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp index f6992f05..f3373849 100644 --- a/src/drivers/bluetooth.cpp +++ b/src/drivers/bluetooth.cpp @@ -120,7 +120,7 @@ std::atomic BluetoothState::sSource_; auto BluetoothState::Init(NvsStorage* storage) -> void { sStorage_ = storage; - sPreferredDevice_ = storage->PreferredBluetoothDevice(); + sPreferredDevice_ = storage->PreferredBluetoothDevice().get(); tinyfsm::FsmList::start(); } @@ -428,7 +428,7 @@ void Connecting::react(const events::internal::A2dp& ev) { void Connected::entry() { ESP_LOGI(kTag, "entering connected state"); - auto stored_pref = sStorage_->PreferredBluetoothDevice(); + auto stored_pref = sStorage_->PreferredBluetoothDevice().get(); if (stored_pref != sPreferredDevice_) { sStorage_->PreferredBluetoothDevice(sPreferredDevice_); } diff --git a/src/drivers/include/nvs.hpp b/src/drivers/include/nvs.hpp index 913ad51e..bc88f88d 100644 --- a/src/drivers/include/nvs.hpp +++ b/src/drivers/include/nvs.hpp @@ -7,35 +7,44 @@ #pragma once #include +#include #include #include "esp_err.h" #include "nvs.h" #include "bluetooth_types.hpp" +#include "tasks.hpp" namespace drivers { class NvsStorage { public: - static auto Open() -> NvsStorage*; + static auto OpenSync() -> NvsStorage*; - auto SchemaVersion() -> uint8_t; - - auto PreferredBluetoothDevice() -> std::optional; - auto PreferredBluetoothDevice(std::optional) -> void; + auto PreferredBluetoothDevice() + -> std::future>; + auto PreferredBluetoothDevice(std::optional) + -> std::future; enum class Output : uint8_t { kHeadphones = 0, kBluetooth = 1, }; - auto OutputMode() -> Output; - auto OutputMode(Output) -> void; + auto OutputMode() -> std::future; + auto OutputMode(Output) -> std::future; + + auto ScreenBrightness() -> std::future; + auto ScreenBrightness(uint_fast8_t) -> std::future; - explicit NvsStorage(nvs_handle_t); + explicit NvsStorage(std::unique_ptr, nvs_handle_t); ~NvsStorage(); private: + auto DowngradeSchemaSync() -> bool; + auto SchemaVersionSync() -> uint8_t; + + std::unique_ptr writer_; nvs_handle_t handle_; }; diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index d2110764..c2832bf4 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -6,14 +6,17 @@ #include "nvs.hpp" #include +#include #include #include #include "bluetooth.hpp" +#include "bluetooth_types.hpp" #include "esp_log.h" #include "nvs.h" #include "nvs_flash.h" +#include "tasks.hpp" namespace drivers { @@ -23,8 +26,9 @@ static constexpr uint8_t kSchemaVersion = 1; static constexpr char kKeyVersion[] = "ver"; static constexpr char kKeyBluetooth[] = "bt"; static constexpr char kKeyOutput[] = "out"; +static constexpr char kKeyBrightness[] = "bright"; -auto NvsStorage::Open() -> NvsStorage* { +auto NvsStorage::OpenSync() -> NvsStorage* { esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_LOGW(kTag, "partition needs initialisation"); @@ -42,73 +46,112 @@ auto NvsStorage::Open() -> NvsStorage* { return nullptr; } - std::unique_ptr instance = std::make_unique(handle); - if (instance->SchemaVersion() < kSchemaVersion) { - ESP_LOGW(kTag, "namespace needs downgrading"); - nvs_erase_all(handle); - nvs_set_u8(handle, kKeyVersion, kSchemaVersion); - err = nvs_commit(handle); - if (err != ESP_OK) { - ESP_LOGW(kTag, "failed to init namespace"); - return nullptr; - } + std::unique_ptr instance = std::make_unique( + std::unique_ptr( + tasks::Worker::Start()), + handle); + if (instance->SchemaVersionSync() < kSchemaVersion && + !instance->DowngradeSchemaSync()) { + ESP_LOGW(kTag, "failed to init namespace"); + return nullptr; } ESP_LOGI(kTag, "nvm storage initialised okay"); return instance.release(); } -NvsStorage::NvsStorage(nvs_handle_t handle) : handle_(handle) {} +NvsStorage::NvsStorage(std::unique_ptr worker, + nvs_handle_t handle) + : writer_(std::move(worker)), handle_(handle) {} NvsStorage::~NvsStorage() { nvs_close(handle_); nvs_flash_deinit(); } -auto NvsStorage::SchemaVersion() -> uint8_t { - uint8_t ret; - if (nvs_get_u8(handle_, kKeyVersion, &ret) != ESP_OK) { - return UINT8_MAX; - } - return ret; +auto NvsStorage::DowngradeSchemaSync() -> bool { + ESP_LOGW(kTag, "namespace needs downgrading"); + return writer_ + ->Dispatch([&]() -> bool { + nvs_erase_all(handle_); + nvs_set_u8(handle_, kKeyVersion, kSchemaVersion); + return nvs_commit(handle_); + }) + .get() == ESP_OK; +} + +auto NvsStorage::SchemaVersionSync() -> uint8_t { + return writer_ + ->Dispatch([&]() -> uint8_t { + uint8_t ret; + if (nvs_get_u8(handle_, kKeyVersion, &ret) != ESP_OK) { + return UINT8_MAX; + } + return ret; + }) + .get(); } auto NvsStorage::PreferredBluetoothDevice() - -> std::optional { - bluetooth::mac_addr_t out{0}; - size_t size = out.size(); - if (nvs_get_blob(handle_, kKeyBluetooth, out.data(), &size) != ESP_OK) { - return {}; - } - return out; + -> std::future> { + return writer_->Dispatch>( + [&]() -> std::optional { + bluetooth::mac_addr_t out{0}; + size_t size = out.size(); + if (nvs_get_blob(handle_, kKeyBluetooth, out.data(), &size) != ESP_OK) { + return {}; + } + return out; + }); } auto NvsStorage::PreferredBluetoothDevice( - std::optional addr) -> void { - if (!addr) { - nvs_erase_key(handle_, kKeyBluetooth); - } else { - nvs_set_blob(handle_, kKeyBluetooth, addr.value().data(), - addr.value().size()); - } - nvs_commit(handle_); + std::optional addr) -> std::future { + return writer_->Dispatch([&]() { + if (!addr) { + nvs_erase_key(handle_, kKeyBluetooth); + } else { + nvs_set_blob(handle_, kKeyBluetooth, addr.value().data(), + addr.value().size()); + } + return nvs_commit(handle_) == ESP_OK; + }); } -auto NvsStorage::OutputMode() -> Output { - uint8_t out = 0; - nvs_get_u8(handle_, kKeyOutput, &out); - switch (out) { - case static_cast(Output::kBluetooth): - return Output::kHeadphones; - case static_cast(Output::kHeadphones): - default: - return Output::kHeadphones; - } +auto NvsStorage::OutputMode() -> std::future { + return writer_->Dispatch([&]() -> Output { + uint8_t out = 0; + nvs_get_u8(handle_, kKeyOutput, &out); + switch (out) { + case static_cast(Output::kBluetooth): + return Output::kHeadphones; + case static_cast(Output::kHeadphones): + default: + return Output::kHeadphones; + } + }); +} + +auto NvsStorage::OutputMode(Output out) -> std::future { + return writer_->Dispatch([&]() { + nvs_set_u8(handle_, kKeyOutput, static_cast(out)); + return nvs_commit(handle_) == ESP_OK; + }); +} + +auto NvsStorage::ScreenBrightness() -> std::future { + return writer_->Dispatch([&]() -> uint_fast8_t { + uint8_t out = 50; + nvs_get_u8(handle_, kKeyBrightness, &out); + return out; + }); } -auto NvsStorage::OutputMode(Output out) -> void { - nvs_set_u8(handle_, kKeyOutput, static_cast(out)); - nvs_commit(handle_); +auto NvsStorage::ScreenBrightness(uint_fast8_t val) -> std::future { + return writer_->Dispatch([&]() { + nvs_set_u8(handle_, kKeyBrightness, val); + return nvs_commit(handle_) == ESP_OK; + }); } } // namespace drivers diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index cdbe4d55..a988c622 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -49,7 +49,7 @@ auto Booting::entry() -> void { sSamd.reset(drivers::Samd::Create()); sAdc.reset(drivers::AdcBattery::Create()); - sNvs.reset(drivers::NvsStorage::Open()); + sNvs.reset(drivers::NvsStorage::OpenSync()); assert(sSamd.get() && sAdc.get() && sNvs.get()); sBattery.reset(new battery::Battery(sSamd.get(), sAdc.get())); diff --git a/src/tasks/tasks.cpp b/src/tasks/tasks.cpp index 493d6ba9..f7dc0279 100644 --- a/src/tasks/tasks.cpp +++ b/src/tasks/tasks.cpp @@ -45,6 +45,10 @@ template <> auto Name() -> std::string { return "DB_BG"; } +template <> +auto Name() -> std::string { + return "NVS"; +} template auto AllocateStack() -> cpp::span; @@ -102,6 +106,13 @@ auto AllocateStack() -> cpp::span { return {static_cast(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), size}; } +template <> +auto AllocateStack() -> cpp::span { + std::size_t size = 2 * 1024; + return {static_cast( + heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)), + size}; +} // 2048 bytes in internal ram // 302 KiB in external ram. @@ -113,6 +124,12 @@ auto AllocateStack() -> cpp::span { template auto Priority() -> UBaseType_t; +// NVS writing requires suspending one of our cores, and disabling tasks with +// their stacks in PSRAM. Get it over and done with as soon as possible. +template <> +auto Priority() -> UBaseType_t { + return 13; +} // Realtime audio is the entire point of this device, so give this task the // highest priority. template <> @@ -171,6 +188,10 @@ template <> auto WorkerQueueSize() -> std::size_t { return 2; } +template <> +auto WorkerQueueSize() -> std::size_t { + return 2; +} auto PersistentMain(void* fn) -> void { auto* function = reinterpret_cast*>(fn); diff --git a/src/tasks/tasks.hpp b/src/tasks/tasks.hpp index a0c201d5..17836795 100644 --- a/src/tasks/tasks.hpp +++ b/src/tasks/tasks.hpp @@ -42,6 +42,7 @@ enum class Type { kDatabase, // Task for internal database operations kDatabaseBackground, + kNvsWriter, }; template diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp index 53d9277b..61375fa9 100644 --- a/src/ui/include/screen_settings.hpp +++ b/src/ui/include/screen_settings.hpp @@ -40,12 +40,14 @@ class Appearance : public MenuScreen { Appearance(drivers::NvsStorage* nvs, drivers::Display* display); auto ChangeBrightness(uint_fast8_t) -> void; + auto CommitBrightness() -> void; private: drivers::NvsStorage* nvs_; drivers::Display* display_; lv_obj_t* current_brightness_label_; + uint_fast8_t current_brightness_; }; class InputMethod : public MenuScreen { diff --git a/src/ui/include/themes.hpp b/src/ui/include/themes.hpp index ef0e719c..ee4bb05d 100644 --- a/src/ui/include/themes.hpp +++ b/src/ui/include/themes.hpp @@ -5,13 +5,9 @@ namespace ui { namespace themes { - enum class Style { - kMenuItem, - kTopBar - }; +enum class Style { kMenuItem, kTopBar }; class Theme { public: - void Apply(void); void Callback(lv_obj_t* obj); void ApplyStyle(lv_obj_t* obj, Style style); diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index c496ce00..6bafb9a8 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -5,6 +5,7 @@ */ #include "screen_settings.hpp" +#include #include "core/lv_event.h" #include "core/lv_obj.h" @@ -151,6 +152,15 @@ static void change_brightness_cb(lv_event_t* ev) { instance->ChangeBrightness(lv_slider_get_value(ev->target)); } +static void release_brightness_cb(lv_event_t* ev) { + Appearance* instance = reinterpret_cast(ev->user_data); + instance->CommitBrightness(); +} + +static auto brightness_str(uint_fast8_t percent) -> std::string { + return std::to_string(percent) + "%"; +} + Appearance::Appearance(drivers::NvsStorage* nvs, drivers::Display* display) : MenuScreen("Appearance"), nvs_(nvs), display_(display) { lv_obj_t* toggle_container = settings_container(content_); @@ -160,25 +170,35 @@ Appearance::Appearance(drivers::NvsStorage* nvs, drivers::Display* display) lv_obj_t* toggle = lv_switch_create(toggle_container); lv_group_add_obj(group_, toggle); + uint_fast8_t initial_brightness = nvs_->ScreenBrightness().get(); + lv_obj_t* brightness_label = lv_label_create(content_); lv_label_set_text(brightness_label, "Brightness"); lv_obj_t* brightness = lv_slider_create(content_); lv_obj_set_width(brightness, lv_pct(100)); lv_slider_set_range(brightness, 10, 100); - lv_slider_set_value(brightness, 50, LV_ANIM_OFF); + lv_slider_set_value(brightness, initial_brightness, LV_ANIM_OFF); lv_group_add_obj(group_, brightness); current_brightness_label_ = lv_label_create(content_); - lv_label_set_text(current_brightness_label_, "50%"); + lv_label_set_text(current_brightness_label_, + brightness_str(initial_brightness).c_str()); lv_obj_set_size(current_brightness_label_, lv_pct(100), LV_SIZE_CONTENT); lv_obj_add_event_cb(brightness, change_brightness_cb, LV_EVENT_VALUE_CHANGED, this); + lv_obj_add_event_cb(brightness, release_brightness_cb, LV_EVENT_RELEASED, + this); } auto Appearance::ChangeBrightness(uint_fast8_t new_level) -> void { + current_brightness_ = new_level; display_->SetBrightness(new_level); - std::string new_str = std::to_string(new_level) + "%"; - lv_label_set_text(current_brightness_label_, new_str.c_str()); + lv_label_set_text(current_brightness_label_, + brightness_str(new_level).c_str()); +} + +auto Appearance::CommitBrightness() -> void { + nvs_->ScreenBrightness(current_brightness_); } InputMethod::InputMethod() : MenuScreen("Input Method") { diff --git a/src/ui/themes.cpp b/src/ui/themes.cpp index dd9ef423..38bcd9fd 100644 --- a/src/ui/themes.cpp +++ b/src/ui/themes.cpp @@ -1,7 +1,7 @@ #include "themes.hpp" #include "core/lv_obj.h" -#include "misc/lv_color.h" #include "esp_log.h" +#include "misc/lv_color.h" LV_FONT_DECLARE(font_fusion); @@ -19,7 +19,8 @@ Theme::Theme() { lv_style_set_bg_color(&button_style_, lv_color_white()); lv_style_init(&button_style_focused_); - lv_style_set_bg_color(&button_style_focused_, lv_palette_lighten(LV_PALETTE_BLUE_GREY, 2)); + lv_style_set_bg_color(&button_style_focused_, + lv_palette_lighten(LV_PALETTE_BLUE_GREY, 2)); lv_theme_t* parent_theme = lv_disp_get_theme(NULL); theme_ = *parent_theme; @@ -41,15 +42,18 @@ void Theme::Callback(lv_obj_t* obj) { if (lv_obj_check_type(obj, &lv_btn_class) || lv_obj_check_type(obj, &lv_list_btn_class)) { lv_obj_add_style(obj, &button_style_, LV_PART_MAIN); - lv_obj_add_style(obj, &button_style_focused_, LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_add_style(obj, &button_style_focused_, + LV_PART_MAIN | LV_STATE_FOCUSED); } } void Theme::ApplyStyle(lv_obj_t* obj, Style style) { if (style == Style::kTopBar) { - lv_obj_set_style_border_color(obj, lv_palette_darken(LV_PALETTE_BLUE_GREY, 2), LV_PART_MAIN); + lv_obj_set_style_border_color( + obj, lv_palette_darken(LV_PALETTE_BLUE_GREY, 2), LV_PART_MAIN); lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN); - lv_obj_set_style_border_side(obj, LV_BORDER_SIDE_BOTTOM|LV_BORDER_SIDE_TOP, LV_PART_MAIN); + lv_obj_set_style_border_side( + obj, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_TOP, LV_PART_MAIN); lv_obj_set_style_pad_top(obj, 2, LV_PART_MAIN); lv_obj_set_style_pad_bottom(obj, 2, LV_PART_MAIN); } diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 733b6bee..1febd1c7 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -69,6 +69,7 @@ auto UiState::Init(drivers::IGpios* gpio_expander, if (sDisplay == nullptr) { return false; } + sDisplay->SetBrightness(nvs->ScreenBrightness().get()); sTouchWheel.reset(drivers::TouchWheel::Create()); if (sTouchWheel != nullptr) {