store the screen brightness in nvs

custom
jacqueline 2 years ago
parent 773f285767
commit 4247c9fe7d
  1. 4
      src/drivers/bluetooth.cpp
  2. 25
      src/drivers/include/nvs.hpp
  3. 77
      src/drivers/nvs.cpp
  4. 2
      src/system_fsm/booting.cpp
  5. 21
      src/tasks/tasks.cpp
  6. 1
      src/tasks/tasks.hpp
  7. 2
      src/ui/include/screen_settings.hpp
  8. 6
      src/ui/include/themes.hpp
  9. 28
      src/ui/screen_settings.cpp
  10. 14
      src/ui/themes.cpp
  11. 1
      src/ui/ui_fsm.cpp

@ -120,7 +120,7 @@ std::atomic<StreamBufferHandle_t> BluetoothState::sSource_;
auto BluetoothState::Init(NvsStorage* storage) -> void { auto BluetoothState::Init(NvsStorage* storage) -> void {
sStorage_ = storage; sStorage_ = storage;
sPreferredDevice_ = storage->PreferredBluetoothDevice(); sPreferredDevice_ = storage->PreferredBluetoothDevice().get();
tinyfsm::FsmList<bluetooth::BluetoothState>::start(); tinyfsm::FsmList<bluetooth::BluetoothState>::start();
} }
@ -428,7 +428,7 @@ void Connecting::react(const events::internal::A2dp& ev) {
void Connected::entry() { void Connected::entry() {
ESP_LOGI(kTag, "entering connected state"); ESP_LOGI(kTag, "entering connected state");
auto stored_pref = sStorage_->PreferredBluetoothDevice(); auto stored_pref = sStorage_->PreferredBluetoothDevice().get();
if (stored_pref != sPreferredDevice_) { if (stored_pref != sPreferredDevice_) {
sStorage_->PreferredBluetoothDevice(sPreferredDevice_); sStorage_->PreferredBluetoothDevice(sPreferredDevice_);
} }

@ -7,35 +7,44 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <memory>
#include <optional> #include <optional>
#include "esp_err.h" #include "esp_err.h"
#include "nvs.h" #include "nvs.h"
#include "bluetooth_types.hpp" #include "bluetooth_types.hpp"
#include "tasks.hpp"
namespace drivers { namespace drivers {
class NvsStorage { class NvsStorage {
public: public:
static auto Open() -> NvsStorage*; static auto OpenSync() -> NvsStorage*;
auto SchemaVersion() -> uint8_t; auto PreferredBluetoothDevice()
-> std::future<std::optional<bluetooth::mac_addr_t>>;
auto PreferredBluetoothDevice() -> std::optional<bluetooth::mac_addr_t>; auto PreferredBluetoothDevice(std::optional<bluetooth::mac_addr_t>)
auto PreferredBluetoothDevice(std::optional<bluetooth::mac_addr_t>) -> void; -> std::future<bool>;
enum class Output : uint8_t { enum class Output : uint8_t {
kHeadphones = 0, kHeadphones = 0,
kBluetooth = 1, kBluetooth = 1,
}; };
auto OutputMode() -> Output; auto OutputMode() -> std::future<Output>;
auto OutputMode(Output) -> void; auto OutputMode(Output) -> std::future<bool>;
auto ScreenBrightness() -> std::future<uint_fast8_t>;
auto ScreenBrightness(uint_fast8_t) -> std::future<bool>;
explicit NvsStorage(nvs_handle_t); explicit NvsStorage(std::unique_ptr<tasks::Worker>, nvs_handle_t);
~NvsStorage(); ~NvsStorage();
private: private:
auto DowngradeSchemaSync() -> bool;
auto SchemaVersionSync() -> uint8_t;
std::unique_ptr<tasks::Worker> writer_;
nvs_handle_t handle_; nvs_handle_t handle_;
}; };

@ -6,14 +6,17 @@
#include "nvs.hpp" #include "nvs.hpp"
#include <stdint.h> #include <stdint.h>
#include <sys/_stdint.h>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "bluetooth.hpp" #include "bluetooth.hpp"
#include "bluetooth_types.hpp"
#include "esp_log.h" #include "esp_log.h"
#include "nvs.h" #include "nvs.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "tasks.hpp"
namespace drivers { namespace drivers {
@ -23,8 +26,9 @@ static constexpr uint8_t kSchemaVersion = 1;
static constexpr char kKeyVersion[] = "ver"; static constexpr char kKeyVersion[] = "ver";
static constexpr char kKeyBluetooth[] = "bt"; static constexpr char kKeyBluetooth[] = "bt";
static constexpr char kKeyOutput[] = "out"; 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(); esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) { if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_LOGW(kTag, "partition needs initialisation"); ESP_LOGW(kTag, "partition needs initialisation");
@ -42,59 +46,80 @@ auto NvsStorage::Open() -> NvsStorage* {
return nullptr; return nullptr;
} }
std::unique_ptr<NvsStorage> instance = std::make_unique<NvsStorage>(handle); std::unique_ptr<NvsStorage> instance = std::make_unique<NvsStorage>(
if (instance->SchemaVersion() < kSchemaVersion) { std::unique_ptr<tasks::Worker>(
ESP_LOGW(kTag, "namespace needs downgrading"); tasks::Worker::Start<tasks::Type::kNvsWriter>()),
nvs_erase_all(handle); handle);
nvs_set_u8(handle, kKeyVersion, kSchemaVersion); if (instance->SchemaVersionSync() < kSchemaVersion &&
err = nvs_commit(handle); !instance->DowngradeSchemaSync()) {
if (err != ESP_OK) {
ESP_LOGW(kTag, "failed to init namespace"); ESP_LOGW(kTag, "failed to init namespace");
return nullptr; return nullptr;
} }
}
ESP_LOGI(kTag, "nvm storage initialised okay"); ESP_LOGI(kTag, "nvm storage initialised okay");
return instance.release(); return instance.release();
} }
NvsStorage::NvsStorage(nvs_handle_t handle) : handle_(handle) {} NvsStorage::NvsStorage(std::unique_ptr<tasks::Worker> worker,
nvs_handle_t handle)
: writer_(std::move(worker)), handle_(handle) {}
NvsStorage::~NvsStorage() { NvsStorage::~NvsStorage() {
nvs_close(handle_); nvs_close(handle_);
nvs_flash_deinit(); nvs_flash_deinit();
} }
auto NvsStorage::SchemaVersion() -> uint8_t { auto NvsStorage::DowngradeSchemaSync() -> bool {
ESP_LOGW(kTag, "namespace needs downgrading");
return writer_
->Dispatch<bool>([&]() -> 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 {
uint8_t ret; uint8_t ret;
if (nvs_get_u8(handle_, kKeyVersion, &ret) != ESP_OK) { if (nvs_get_u8(handle_, kKeyVersion, &ret) != ESP_OK) {
return UINT8_MAX; return UINT8_MAX;
} }
return ret; return ret;
})
.get();
} }
auto NvsStorage::PreferredBluetoothDevice() auto NvsStorage::PreferredBluetoothDevice()
-> std::optional<bluetooth::mac_addr_t> { -> std::future<std::optional<bluetooth::mac_addr_t>> {
return writer_->Dispatch<std::optional<bluetooth::mac_addr_t>>(
[&]() -> std::optional<bluetooth::mac_addr_t> {
bluetooth::mac_addr_t out{0}; bluetooth::mac_addr_t out{0};
size_t size = out.size(); size_t size = out.size();
if (nvs_get_blob(handle_, kKeyBluetooth, out.data(), &size) != ESP_OK) { if (nvs_get_blob(handle_, kKeyBluetooth, out.data(), &size) != ESP_OK) {
return {}; return {};
} }
return out; return out;
});
} }
auto NvsStorage::PreferredBluetoothDevice( auto NvsStorage::PreferredBluetoothDevice(
std::optional<bluetooth::mac_addr_t> addr) -> void { std::optional<bluetooth::mac_addr_t> addr) -> std::future<bool> {
return writer_->Dispatch<bool>([&]() {
if (!addr) { if (!addr) {
nvs_erase_key(handle_, kKeyBluetooth); nvs_erase_key(handle_, kKeyBluetooth);
} else { } else {
nvs_set_blob(handle_, kKeyBluetooth, addr.value().data(), nvs_set_blob(handle_, kKeyBluetooth, addr.value().data(),
addr.value().size()); addr.value().size());
} }
nvs_commit(handle_); return nvs_commit(handle_) == ESP_OK;
});
} }
auto NvsStorage::OutputMode() -> Output { auto NvsStorage::OutputMode() -> std::future<Output> {
return writer_->Dispatch<Output>([&]() -> Output {
uint8_t out = 0; uint8_t out = 0;
nvs_get_u8(handle_, kKeyOutput, &out); nvs_get_u8(handle_, kKeyOutput, &out);
switch (out) { switch (out) {
@ -104,11 +129,29 @@ auto NvsStorage::OutputMode() -> Output {
default: default:
return Output::kHeadphones; return Output::kHeadphones;
} }
});
} }
auto NvsStorage::OutputMode(Output out) -> void { auto NvsStorage::OutputMode(Output out) -> std::future<bool> {
return writer_->Dispatch<bool>([&]() {
nvs_set_u8(handle_, kKeyOutput, static_cast<uint8_t>(out)); nvs_set_u8(handle_, kKeyOutput, static_cast<uint8_t>(out));
nvs_commit(handle_); return nvs_commit(handle_) == ESP_OK;
});
}
auto NvsStorage::ScreenBrightness() -> std::future<uint_fast8_t> {
return writer_->Dispatch<uint_fast8_t>([&]() -> uint_fast8_t {
uint8_t out = 50;
nvs_get_u8(handle_, kKeyBrightness, &out);
return out;
});
}
auto NvsStorage::ScreenBrightness(uint_fast8_t val) -> std::future<bool> {
return writer_->Dispatch<bool>([&]() {
nvs_set_u8(handle_, kKeyBrightness, val);
return nvs_commit(handle_) == ESP_OK;
});
} }
} // namespace drivers } // namespace drivers

@ -49,7 +49,7 @@ auto Booting::entry() -> void {
sSamd.reset(drivers::Samd::Create()); sSamd.reset(drivers::Samd::Create());
sAdc.reset(drivers::AdcBattery::Create()); sAdc.reset(drivers::AdcBattery::Create());
sNvs.reset(drivers::NvsStorage::Open()); sNvs.reset(drivers::NvsStorage::OpenSync());
assert(sSamd.get() && sAdc.get() && sNvs.get()); assert(sSamd.get() && sAdc.get() && sNvs.get());
sBattery.reset(new battery::Battery(sSamd.get(), sAdc.get())); sBattery.reset(new battery::Battery(sSamd.get(), sAdc.get()));

@ -45,6 +45,10 @@ template <>
auto Name<Type::kDatabaseBackground>() -> std::string { auto Name<Type::kDatabaseBackground>() -> std::string {
return "DB_BG"; return "DB_BG";
} }
template <>
auto Name<Type::kNvsWriter>() -> std::string {
return "NVS";
}
template <Type t> template <Type t>
auto AllocateStack() -> cpp::span<StackType_t>; auto AllocateStack() -> cpp::span<StackType_t>;
@ -102,6 +106,13 @@ auto AllocateStack<Type::kDatabaseBackground>() -> cpp::span<StackType_t> {
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
size}; size};
} }
template <>
auto AllocateStack<Type::kNvsWriter>() -> cpp::span<StackType_t> {
std::size_t size = 2 * 1024;
return {static_cast<StackType_t*>(
heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)),
size};
}
// 2048 bytes in internal ram // 2048 bytes in internal ram
// 302 KiB in external ram. // 302 KiB in external ram.
@ -113,6 +124,12 @@ auto AllocateStack<Type::kDatabaseBackground>() -> cpp::span<StackType_t> {
template <Type t> template <Type t>
auto Priority() -> UBaseType_t; 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<Type::kNvsWriter>() -> UBaseType_t {
return 13;
}
// Realtime audio is the entire point of this device, so give this task the // Realtime audio is the entire point of this device, so give this task the
// highest priority. // highest priority.
template <> template <>
@ -171,6 +188,10 @@ template <>
auto WorkerQueueSize<Type::kUiFlush>() -> std::size_t { auto WorkerQueueSize<Type::kUiFlush>() -> std::size_t {
return 2; return 2;
} }
template <>
auto WorkerQueueSize<Type::kNvsWriter>() -> std::size_t {
return 2;
}
auto PersistentMain(void* fn) -> void { auto PersistentMain(void* fn) -> void {
auto* function = reinterpret_cast<std::function<void(void)>*>(fn); auto* function = reinterpret_cast<std::function<void(void)>*>(fn);

@ -42,6 +42,7 @@ enum class Type {
kDatabase, kDatabase,
// Task for internal database operations // Task for internal database operations
kDatabaseBackground, kDatabaseBackground,
kNvsWriter,
}; };
template <Type t> template <Type t>

@ -40,12 +40,14 @@ class Appearance : public MenuScreen {
Appearance(drivers::NvsStorage* nvs, drivers::Display* display); Appearance(drivers::NvsStorage* nvs, drivers::Display* display);
auto ChangeBrightness(uint_fast8_t) -> void; auto ChangeBrightness(uint_fast8_t) -> void;
auto CommitBrightness() -> void;
private: private:
drivers::NvsStorage* nvs_; drivers::NvsStorage* nvs_;
drivers::Display* display_; drivers::Display* display_;
lv_obj_t* current_brightness_label_; lv_obj_t* current_brightness_label_;
uint_fast8_t current_brightness_;
}; };
class InputMethod : public MenuScreen { class InputMethod : public MenuScreen {

@ -5,13 +5,9 @@
namespace ui { namespace ui {
namespace themes { namespace themes {
enum class Style { enum class Style { kMenuItem, kTopBar };
kMenuItem,
kTopBar
};
class Theme { class Theme {
public: public:
void Apply(void); void Apply(void);
void Callback(lv_obj_t* obj); void Callback(lv_obj_t* obj);
void ApplyStyle(lv_obj_t* obj, Style style); void ApplyStyle(lv_obj_t* obj, Style style);

@ -5,6 +5,7 @@
*/ */
#include "screen_settings.hpp" #include "screen_settings.hpp"
#include <string>
#include "core/lv_event.h" #include "core/lv_event.h"
#include "core/lv_obj.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)); instance->ChangeBrightness(lv_slider_get_value(ev->target));
} }
static void release_brightness_cb(lv_event_t* ev) {
Appearance* instance = reinterpret_cast<Appearance*>(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) Appearance::Appearance(drivers::NvsStorage* nvs, drivers::Display* display)
: MenuScreen("Appearance"), nvs_(nvs), display_(display) { : MenuScreen("Appearance"), nvs_(nvs), display_(display) {
lv_obj_t* toggle_container = settings_container(content_); 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_obj_t* toggle = lv_switch_create(toggle_container);
lv_group_add_obj(group_, toggle); lv_group_add_obj(group_, toggle);
uint_fast8_t initial_brightness = nvs_->ScreenBrightness().get();
lv_obj_t* brightness_label = lv_label_create(content_); lv_obj_t* brightness_label = lv_label_create(content_);
lv_label_set_text(brightness_label, "Brightness"); lv_label_set_text(brightness_label, "Brightness");
lv_obj_t* brightness = lv_slider_create(content_); lv_obj_t* brightness = lv_slider_create(content_);
lv_obj_set_width(brightness, lv_pct(100)); lv_obj_set_width(brightness, lv_pct(100));
lv_slider_set_range(brightness, 10, 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); lv_group_add_obj(group_, brightness);
current_brightness_label_ = lv_label_create(content_); 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_set_size(current_brightness_label_, lv_pct(100), LV_SIZE_CONTENT);
lv_obj_add_event_cb(brightness, change_brightness_cb, LV_EVENT_VALUE_CHANGED, lv_obj_add_event_cb(brightness, change_brightness_cb, LV_EVENT_VALUE_CHANGED,
this); this);
lv_obj_add_event_cb(brightness, release_brightness_cb, LV_EVENT_RELEASED,
this);
} }
auto Appearance::ChangeBrightness(uint_fast8_t new_level) -> void { auto Appearance::ChangeBrightness(uint_fast8_t new_level) -> void {
current_brightness_ = new_level;
display_->SetBrightness(new_level); display_->SetBrightness(new_level);
std::string new_str = std::to_string(new_level) + "%"; lv_label_set_text(current_brightness_label_,
lv_label_set_text(current_brightness_label_, new_str.c_str()); brightness_str(new_level).c_str());
}
auto Appearance::CommitBrightness() -> void {
nvs_->ScreenBrightness(current_brightness_);
} }
InputMethod::InputMethod() : MenuScreen("Input Method") { InputMethod::InputMethod() : MenuScreen("Input Method") {

@ -1,7 +1,7 @@
#include "themes.hpp" #include "themes.hpp"
#include "core/lv_obj.h" #include "core/lv_obj.h"
#include "misc/lv_color.h"
#include "esp_log.h" #include "esp_log.h"
#include "misc/lv_color.h"
LV_FONT_DECLARE(font_fusion); LV_FONT_DECLARE(font_fusion);
@ -19,7 +19,8 @@ Theme::Theme() {
lv_style_set_bg_color(&button_style_, lv_color_white()); lv_style_set_bg_color(&button_style_, lv_color_white());
lv_style_init(&button_style_focused_); 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); lv_theme_t* parent_theme = lv_disp_get_theme(NULL);
theme_ = *parent_theme; theme_ = *parent_theme;
@ -41,15 +42,18 @@ void Theme::Callback(lv_obj_t* obj) {
if (lv_obj_check_type(obj, &lv_btn_class) || if (lv_obj_check_type(obj, &lv_btn_class) ||
lv_obj_check_type(obj, &lv_list_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_, 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) { void Theme::ApplyStyle(lv_obj_t* obj, Style style) {
if (style == Style::kTopBar) { 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_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_top(obj, 2, LV_PART_MAIN);
lv_obj_set_style_pad_bottom(obj, 2, LV_PART_MAIN); lv_obj_set_style_pad_bottom(obj, 2, LV_PART_MAIN);
} }

@ -69,6 +69,7 @@ auto UiState::Init(drivers::IGpios* gpio_expander,
if (sDisplay == nullptr) { if (sDisplay == nullptr) {
return false; return false;
} }
sDisplay->SetBrightness(nvs->ScreenBrightness().get());
sTouchWheel.reset(drivers::TouchWheel::Create()); sTouchWheel.reset(drivers::TouchWheel::Create());
if (sTouchWheel != nullptr) { if (sTouchWheel != nullptr) {

Loading…
Cancel
Save