summaryrefslogtreecommitdiff
path: root/src/drivers/nvs.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-02-13 10:12:04 +1100
committerjacqueline <me@jacqueline.id.au>2024-02-13 10:12:04 +1100
commitcb379f4bc3c51eacf80b786566ab3c2675191164 (patch)
treee8930a80e1c39f57bfb939594d140309a743c0e2 /src/drivers/nvs.cpp
parent79879fbfa075007ba3db30086b0730016028daf5 (diff)
downloadtangara-fw-cb379f4bc3c51eacf80b786566ab3c2675191164.tar.gz
Cache pending nvs writes in memory
Includes refactoring nvs settings to be a bit less duplicated
Diffstat (limited to 'src/drivers/nvs.cpp')
-rw-r--r--src/drivers/nvs.cpp408
1 files changed, 253 insertions, 155 deletions
diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp
index ed85487f..a304c149 100644
--- a/src/drivers/nvs.cpp
+++ b/src/drivers/nvs.cpp
@@ -27,18 +27,105 @@ namespace drivers {
static constexpr uint8_t kSchemaVersion = 1;
static constexpr char kKeyVersion[] = "ver";
-static constexpr char kKeyBluetoothMac[] = "bt_mac";
-static constexpr char kKeyBluetoothName[] = "bt_name";
+static constexpr char kKeyBluetoothPreferred[] = "bt_dev";
static constexpr char kKeyBluetoothVolumes[] = "bt_vols";
static constexpr char kKeyOutput[] = "out";
static constexpr char kKeyBrightness[] = "bright";
static constexpr char kKeyAmpMaxVolume[] = "hp_vol_max";
static constexpr char kKeyAmpCurrentVolume[] = "hp_vol";
static constexpr char kKeyAmpLeftBias[] = "hp_bias";
-static constexpr char kKeyOnboarded[] = "intro";
static constexpr char kKeyPrimaryInput[] = "in_pri";
static constexpr char kKeyLockPolarity[] = "lockpol";
+static auto nvs_get_string(nvs_handle_t nvs, const char* key)
+ -> std::optional<std::string> {
+ size_t len = 0;
+ if (nvs_get_blob(nvs, key, NULL, &len) != ESP_OK) {
+ return {};
+ }
+ auto raw = std::unique_ptr<char[]>{new char[len]};
+ if (nvs_get_blob(nvs, key, raw.get(), &len) != ESP_OK) {
+ return {};
+ }
+ return {{raw.get(), len}};
+}
+
+template <>
+auto Setting<uint16_t>::load(nvs_handle_t nvs) -> std::optional<uint16_t> {
+ uint16_t out;
+ if (nvs_get_u16(nvs, name_, &out) != ESP_OK) {
+ return {};
+ }
+ return out;
+}
+
+template <>
+auto Setting<uint16_t>::store(nvs_handle_t nvs, uint16_t v) -> void {
+ nvs_set_u16(nvs, name_, v);
+}
+
+template <>
+auto Setting<uint8_t>::load(nvs_handle_t nvs) -> std::optional<uint8_t> {
+ uint8_t out;
+ if (nvs_get_u8(nvs, name_, &out) != ESP_OK) {
+ return {};
+ }
+ return out;
+}
+
+template <>
+auto Setting<uint8_t>::store(nvs_handle_t nvs, uint8_t v) -> void {
+ nvs_set_u8(nvs, name_, v);
+}
+
+template <>
+auto Setting<int8_t>::load(nvs_handle_t nvs) -> std::optional<int8_t> {
+ int8_t out;
+ if (nvs_get_i8(nvs, name_, &out) != ESP_OK) {
+ return {};
+ }
+ return out;
+}
+
+template <>
+auto Setting<int8_t>::store(nvs_handle_t nvs, int8_t v) -> void {
+ nvs_set_i8(nvs, name_, v);
+}
+
+template <>
+auto Setting<bluetooth::MacAndName>::load(nvs_handle_t nvs)
+ -> std::optional<bluetooth::MacAndName> {
+ auto raw = nvs_get_string(nvs, name_);
+ if (!raw) {
+ return {};
+ }
+ auto [parsed, unused, err] = cppbor::parseWithViews(
+ reinterpret_cast<const uint8_t*>(raw->data()), raw->size());
+ if (parsed->type() != cppbor::ARRAY) {
+ return {};
+ }
+ auto arr = parsed->asArray();
+ auto mac = arr->get(1)->asViewBstr()->view();
+ auto name = arr->get(0)->asViewTstr()->view();
+ bluetooth::MacAndName res{
+ .mac = {},
+ .name = {name.begin(), name.end()},
+ };
+ std::copy(mac.begin(), mac.end(), res.mac.begin());
+ return res;
+}
+
+template <>
+auto Setting<bluetooth::MacAndName>::store(nvs_handle_t nvs,
+ bluetooth::MacAndName v) -> void {
+ cppbor::Array cbor{
+ cppbor::Tstr{v.name},
+ cppbor::Bstr{{v.mac.data(), v.mac.size()}},
+ };
+ auto encoded = cbor.encode();
+ nvs_set_blob(nvs, name_, encoded.data(), encoded.size());
+}
+
auto NvsStorage::OpenSync() -> NvsStorage* {
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
@@ -64,17 +151,57 @@ auto NvsStorage::OpenSync() -> NvsStorage* {
return nullptr;
}
+ instance->Read();
+
ESP_LOGI(kTag, "nvm storage initialised okay");
return instance.release();
}
-NvsStorage::NvsStorage(nvs_handle_t handle) : handle_(handle) {}
+NvsStorage::NvsStorage(nvs_handle_t handle)
+ : handle_(handle),
+ lock_polarity_(kKeyLockPolarity),
+ brightness_(kKeyBrightness),
+ amp_max_vol_(kKeyAmpMaxVolume),
+ amp_cur_vol_(kKeyAmpCurrentVolume),
+ amp_left_bias_(kKeyAmpLeftBias),
+ input_mode_(kKeyPrimaryInput),
+ output_mode_(kKeyOutput),
+ bt_preferred_(kKeyBluetoothPreferred),
+ bt_volumes_(),
+ bt_volumes_dirty_(false) {}
NvsStorage::~NvsStorage() {
nvs_close(handle_);
nvs_flash_deinit();
}
+auto NvsStorage::Read() -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ lock_polarity_.read(handle_);
+ brightness_.read(handle_);
+ amp_max_vol_.read(handle_);
+ amp_cur_vol_.read(handle_);
+ amp_left_bias_.read(handle_);
+ input_mode_.read(handle_);
+ output_mode_.read(handle_);
+ bt_preferred_.read(handle_);
+ readBtVolumes();
+}
+
+auto NvsStorage::Write() -> bool {
+ std::lock_guard<std::mutex> lock{mutex_};
+ lock_polarity_.write(handle_);
+ brightness_.write(handle_);
+ amp_max_vol_.write(handle_);
+ amp_cur_vol_.write(handle_);
+ amp_left_bias_.write(handle_);
+ input_mode_.write(handle_);
+ output_mode_.write(handle_);
+ bt_preferred_.write(handle_);
+ writeBtVolumes();
+ return nvs_commit(handle_) == ESP_OK;
+}
+
auto NvsStorage::DowngradeSchemaSync() -> bool {
ESP_LOGW(kTag, "namespace needs downgrading");
nvs_erase_all(handle_);
@@ -91,56 +218,126 @@ auto NvsStorage::SchemaVersionSync() -> uint8_t {
}
auto NvsStorage::LockPolarity() -> bool {
- uint8_t res;
- if (nvs_get_u8(handle_, kKeyLockPolarity, &res) != ESP_OK) {
- return false;
- }
- return res > 0;
+ std::lock_guard<std::mutex> lock{mutex_};
+ return lock_polarity_.get().value_or(0) > 0;
}
-auto NvsStorage::LockPolarity(bool p) -> bool {
- nvs_set_u8(handle_, kKeyLockPolarity, p);
- return nvs_commit(handle_) == ESP_OK;
+auto NvsStorage::LockPolarity(bool p) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ lock_polarity_.set(p);
}
auto NvsStorage::PreferredBluetoothDevice()
-> std::optional<bluetooth::MacAndName> {
- bluetooth::mac_addr_t mac{0};
- size_t size = mac.size();
- if (nvs_get_blob(handle_, kKeyBluetoothMac, mac.data(), &size) != ESP_OK) {
- return {};
- }
- size_t name_len = 0;
- if (nvs_get_str(handle_, kKeyBluetoothName, NULL, &name_len) != ESP_OK) {
- }
- char* raw_name = new char[name_len];
- if (nvs_get_str(handle_, kKeyBluetoothName, raw_name, &name_len) != ESP_OK) {
- delete[] raw_name;
- return {};
- }
- bluetooth::MacAndName out{
- .mac = mac,
- .name = {raw_name, name_len},
- };
- delete[] raw_name;
- return out;
+ std::lock_guard<std::mutex> lock{mutex_};
+ return bt_preferred_.get();
}
auto NvsStorage::PreferredBluetoothDevice(
- std::optional<bluetooth::MacAndName> dev) -> bool {
- if (!dev) {
- nvs_erase_key(handle_, kKeyBluetoothMac);
- nvs_erase_key(handle_, kKeyBluetoothName);
- } else {
- nvs_set_blob(handle_, kKeyBluetoothMac, dev->mac.data(), dev->mac.size());
- nvs_set_str(handle_, kKeyBluetoothName, dev->name.c_str());
+ std::optional<bluetooth::MacAndName> dev) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ bt_preferred_.set(std::move(dev));
+}
+
+auto NvsStorage::BluetoothVolume(const bluetooth::mac_addr_t& mac) -> uint8_t {
+ std::lock_guard<std::mutex> lock{mutex_};
+ // Note we don't set the dirty flag here, even though it's an LRU cache, so
+ // that we can avoid constantly re-writing this setting to flash when the
+ // user hasn't actually been changing their volume.
+ return bt_volumes_.Get(mac).value_or(10);
+}
+
+auto NvsStorage::BluetoothVolume(const bluetooth::mac_addr_t& mac, uint8_t vol)
+ -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ bt_volumes_dirty_ = true;
+ bt_volumes_.Put(mac, vol);
+}
+
+auto NvsStorage::OutputMode() -> Output {
+ std::lock_guard<std::mutex> lock{mutex_};
+ switch (output_mode_.get().value_or(0xFF)) {
+ case static_cast<uint8_t>(Output::kBluetooth):
+ return Output::kBluetooth;
+ case static_cast<uint8_t>(Output::kHeadphones):
+ default:
+ return Output::kHeadphones;
}
- return nvs_commit(handle_) == ESP_OK;
+}
+
+auto NvsStorage::OutputMode(Output out) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ output_mode_.set(static_cast<uint8_t>(out));
+ // Always write this immediately, to guard against any crashes caused by
+ // toggling the output mode.
+ output_mode_.write(handle_);
+ nvs_commit(handle_);
+}
+
+auto NvsStorage::ScreenBrightness() -> uint_fast8_t {
+ std::lock_guard<std::mutex> lock{mutex_};
+ return std::clamp<uint8_t>(brightness_.get().value_or(50), 0, 100);
+}
+
+auto NvsStorage::ScreenBrightness(uint_fast8_t val) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ brightness_.set(val);
+}
+
+auto NvsStorage::AmpMaxVolume() -> uint16_t {
+ std::lock_guard<std::mutex> lock{mutex_};
+ return amp_max_vol_.get().value_or(wm8523::kDefaultMaxVolume);
+}
+
+auto NvsStorage::AmpMaxVolume(uint16_t val) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ amp_max_vol_.set(val);
+}
+
+auto NvsStorage::AmpCurrentVolume() -> uint16_t {
+ std::lock_guard<std::mutex> lock{mutex_};
+ return amp_cur_vol_.get().value_or(wm8523::kDefaultVolume);
+}
+
+auto NvsStorage::AmpCurrentVolume(uint16_t val) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ amp_cur_vol_.set(val);
+}
+
+auto NvsStorage::AmpLeftBias() -> int_fast8_t {
+ std::lock_guard<std::mutex> lock{mutex_};
+ return amp_left_bias_.get().value_or(0);
+}
+
+auto NvsStorage::AmpLeftBias(int_fast8_t val) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ amp_left_bias_.set(val);
+}
+
+auto NvsStorage::PrimaryInput() -> InputModes {
+ std::lock_guard<std::mutex> lock{mutex_};
+ switch (input_mode_.get().value_or(3)) {
+ case static_cast<uint8_t>(InputModes::kButtonsOnly):
+ return InputModes::kButtonsOnly;
+ case static_cast<uint8_t>(InputModes::kButtonsWithWheel):
+ return InputModes::kButtonsWithWheel;
+ case static_cast<uint8_t>(InputModes::kDirectionalWheel):
+ return InputModes::kDirectionalWheel;
+ case static_cast<uint8_t>(InputModes::kRotatingWheel):
+ return InputModes::kRotatingWheel;
+ default:
+ return InputModes::kRotatingWheel;
+ }
+}
+
+auto NvsStorage::PrimaryInput(InputModes mode) -> void {
+ std::lock_guard<std::mutex> lock{mutex_};
+ input_mode_.set(static_cast<uint8_t>(mode));
}
class VolumesParseClient : public cppbor::ParseClient {
public:
- VolumesParseClient(NvsStorage::BtVolumes& out)
+ VolumesParseClient(util::LruCache<10, bluetooth::mac_addr_t, uint8_t>& out)
: state_(State::kInit), mac_(), vol_(), out_(out) {}
ParseClient* item(std::unique_ptr<cppbor::Item>& item,
@@ -197,133 +394,34 @@ class VolumesParseClient : public cppbor::ParseClient {
State state_;
std::optional<bluetooth::mac_addr_t> mac_;
std::optional<uint8_t> vol_;
- NvsStorage::BtVolumes& out_;
+ util::LruCache<10, bluetooth::mac_addr_t, uint8_t>& out_;
};
-auto NvsStorage::BluetoothVolumes() -> BtVolumes {
- BtVolumes out;
- size_t encoded_len = 0;
- if (nvs_get_str(handle_, kKeyBluetoothVolumes, NULL, &encoded_len) !=
- ESP_OK) {
- return out;
- }
- auto encoded = std::unique_ptr<char[]>{new char[encoded_len]};
- if (nvs_get_str(handle_, kKeyBluetoothVolumes, encoded.get(), &encoded_len) !=
- ESP_OK) {
- return out;
+auto NvsStorage::readBtVolumes() -> void {
+ bt_volumes_.Clear();
+ auto raw = nvs_get_string(handle_, kKeyBluetoothVolumes);
+ if (!raw) {
+ return;
}
- VolumesParseClient client{out};
- auto data = reinterpret_cast<const uint8_t*>(encoded.get());
- cppbor::parse(data, data + encoded_len, &client);
- return out;
+ VolumesParseClient client{bt_volumes_};
+ auto data = reinterpret_cast<const uint8_t*>(raw->data());
+ cppbor::parse(data, data + raw->size(), &client);
}
-auto NvsStorage::BluetoothVolumes(const BtVolumes& vols) -> bool {
+auto NvsStorage::writeBtVolumes() -> void {
+ if (!bt_volumes_dirty_) {
+ return;
+ }
+ bt_volumes_dirty_ = false;
+
cppbor::Array enc;
- auto vols_list = vols.Get();
+ auto vols_list = bt_volumes_.Get();
for (auto vol = vols_list.rbegin(); vol < vols_list.rend(); vol++) {
enc.add(cppbor::Array{cppbor::Bstr{{vol->first.data(), vol->first.size()}},
cppbor::Uint{vol->second}});
}
std::string encoded = enc.toString();
nvs_set_str(handle_, kKeyBluetoothVolumes, encoded.c_str());
- 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<uint8_t>(Output::kBluetooth):
- return Output::kBluetooth;
- case static_cast<uint8_t>(Output::kHeadphones):
- default:
- return Output::kHeadphones;
- }
-}
-
-auto NvsStorage::OutputMode(Output out) -> bool {
- uint8_t as_int = static_cast<uint8_t>(out);
- nvs_set_u8(handle_, kKeyOutput, as_int);
- return nvs_commit(handle_) == ESP_OK;
-}
-
-auto NvsStorage::ScreenBrightness() -> uint_fast8_t {
- uint8_t out = 50;
- nvs_get_u8(handle_, kKeyBrightness, &out);
- return out;
-}
-
-auto NvsStorage::ScreenBrightness(uint_fast8_t val) -> bool {
- nvs_set_u8(handle_, kKeyBrightness, val);
- return nvs_commit(handle_) == ESP_OK;
-}
-
-auto NvsStorage::AmpMaxVolume() -> uint16_t {
- uint16_t out = wm8523::kDefaultMaxVolume;
- nvs_get_u16(handle_, kKeyAmpMaxVolume, &out);
- return out;
-}
-
-auto NvsStorage::AmpMaxVolume(uint16_t val) -> bool {
- nvs_set_u16(handle_, kKeyAmpMaxVolume, val);
- return nvs_commit(handle_) == ESP_OK;
-}
-
-auto NvsStorage::AmpCurrentVolume() -> uint16_t {
- uint16_t out = wm8523::kDefaultVolume;
- nvs_get_u16(handle_, kKeyAmpCurrentVolume, &out);
- return out;
-}
-
-auto NvsStorage::AmpCurrentVolume(uint16_t val) -> bool {
- nvs_set_u16(handle_, kKeyAmpCurrentVolume, val);
- return nvs_commit(handle_) == ESP_OK;
-}
-
-auto NvsStorage::AmpLeftBias() -> int_fast8_t {
- int8_t out = 0;
- nvs_get_i8(handle_, kKeyAmpLeftBias, &out);
- return out;
-}
-
-auto NvsStorage::AmpLeftBias(int_fast8_t val) -> bool {
- nvs_set_i8(handle_, kKeyAmpLeftBias, val);
- return nvs_commit(handle_) == ESP_OK;
-}
-
-auto NvsStorage::HasShownOnboarding() -> bool {
- uint8_t out = false;
- nvs_get_u8(handle_, kKeyOnboarded, &out);
- return out;
-}
-
-auto NvsStorage::HasShownOnboarding(bool val) -> bool {
- nvs_set_u8(handle_, kKeyOnboarded, val);
- return nvs_commit(handle_) == ESP_OK;
-}
-
-auto NvsStorage::PrimaryInput() -> InputModes {
- uint8_t out = 3;
- nvs_get_u8(handle_, kKeyPrimaryInput, &out);
- switch (out) {
- case static_cast<uint8_t>(InputModes::kButtonsOnly):
- return InputModes::kButtonsOnly;
- case static_cast<uint8_t>(InputModes::kButtonsWithWheel):
- return InputModes::kButtonsWithWheel;
- case static_cast<uint8_t>(InputModes::kDirectionalWheel):
- return InputModes::kDirectionalWheel;
- case static_cast<uint8_t>(InputModes::kRotatingWheel):
- return InputModes::kRotatingWheel;
- default:
- return InputModes::kRotatingWheel;
- }
-}
-
-auto NvsStorage::PrimaryInput(InputModes mode) -> bool {
- uint8_t as_int = static_cast<uint8_t>(mode);
- nvs_set_u8(handle_, kKeyPrimaryInput, as_int);
- return nvs_commit(handle_) == ESP_OK;
}
} // namespace drivers