Files
ink-board/main/io/nvs_handler.cpp

211 lines
6.9 KiB
C++

#include "common/constants.h"
#include "io/nvs_handler.h"
#include "nvs_flash.h"
#include "string.h"
#include "esp_log.h"
#define TAG "NVSStorageHandler"
NVSStorageHandler::NVSStorageHandler(
const char* name_space
) : name_space(name_space) { }
NVSStorageHandler::~NVSStorageHandler() {
if (this->nvsHandle != 0) {
nvs_close(this->nvsHandle);
this->nvsHandle = 0;
}
}
void NVSStorageHandler::init(const EventGroupHandle_t& system_event_group) {
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_LOGW(TAG, "NVS Flash init failed with %s, erasing and retrying...", esp_err_to_name(err));
nvs_flash_erase();
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
err = nvs_open(this->name_space, NVS_READWRITE, &this->nvsHandle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
} else {
if (system_event_group != nullptr) {
xEventGroupSetBits(system_event_group, STORAGE_READY_BIT);
}
ESP_LOGI(TAG, "NVS Storage initialized.");
}
}
void NVSStorageHandler::put(const std::string& key, const std::string& value) {
if (this->nvsHandle == 0) {
ESP_LOGE(TAG, "NVS handle is not initialized.");
return;
}
esp_err_t err = nvs_set_str(this->nvsHandle, key.c_str(), value.c_str());
if (err == ESP_ERR_NVS_NOT_ENOUGH_SPACE) {
ESP_LOGE(TAG, "NVS storage full! Cannot store key '%s'. Consider clearing old data.", key.c_str());
ESP_LOGI(TAG, "Attempting to erase and retry...");
// Try to commit pending changes first
nvs_commit(this->nvsHandle);
// Retry once
err = nvs_set_str(this->nvsHandle, key.c_str(), value.c_str());
if (err != ESP_OK) {
ESP_LOGE(TAG, "Retry failed: %s", esp_err_to_name(err));
return;
}
} else if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) setting key-value pair in NVS!", esp_err_to_name(err));
return;
}
// Commit successful write
err = nvs_commit(this->nvsHandle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) committing to NVS!", esp_err_to_name(err));
}
}
std::string NVSStorageHandler::get(const std::string& key) const {
if (this->nvsHandle == 0) {
ESP_LOGE(TAG, "NVS handle is not initialized.");
return "";
}
size_t required_size = 0;
esp_err_t err = nvs_get_str(this->nvsHandle, key.c_str(), nullptr, &required_size);
if (err == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGW(TAG, "Key %s not found in NVS.", key.c_str());
return "";
} else if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) getting size for key %s from NVS!", esp_err_to_name(err), key.c_str());
return "";
}
// Allocate string buffer with correct size (includes null terminator)
std::string value(required_size - 1, '\0');
err = nvs_get_str(this->nvsHandle, key.c_str(), &value[0], &required_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) getting value for key %s from NVS!", esp_err_to_name(err), key.c_str());
return "";
}
return value;
}
NVSIteratorGuard NVSStorageHandler::create_iterator() const {
nvs_iterator_t it = nullptr;
esp_err_t err = nvs_entry_find(NVS_DEFAULT_PART_NAME, this->name_space, NVS_TYPE_ANY, &it);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) creating NVS iterator!", esp_err_to_name(err));
return NVSIteratorGuard(nullptr, err);
}
return NVSIteratorGuard(it, ESP_OK);
}
esp_err_t NVSStorageHandler::process_all(KeyValueProcessor processor, void* arg) const {
NVSIteratorGuard iterator_guard = this->create_iterator();
if (!iterator_guard.is_valid()) {
return iterator_guard.get_error();
}
const nvs_iterator_t& it = iterator_guard.get_iterator();
for (; it != NULL; iterator_guard.advance_iter()) {
nvs_entry_info_t info;
esp_err_t err = nvs_entry_info(it, &info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) getting NVS entry info!", esp_err_to_name(err));
return err;
}
nvs_handle_t temp_handle;
err = nvs_open(this->name_space, NVS_READONLY, &temp_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) opening NVS handle for reading!", esp_err_to_name(err));
return err;
}
// call the processor with the key and value
std::string key_str = info.key;
processor(arg, key_str, this->get(key_str));
nvs_close(temp_handle);
}
return ESP_OK;
}
esp_err_t NVSStorageHandler::process_filtered(const std::string& key_prefix, KeyValueProcessor processor, void* arg) const {
NVSIteratorGuard iterator_guard = this->create_iterator();
if (!iterator_guard.is_valid()) {
return iterator_guard.get_error();
}
const nvs_iterator_t& it = iterator_guard.get_iterator();
for (; it != NULL; iterator_guard.advance_iter()) {
nvs_entry_info_t info;
esp_err_t err = nvs_entry_info(it, &info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) getting NVS entry info!", esp_err_to_name(err));
return err;
}
// check if the key matches the prefix
if (strncmp(info.key, key_prefix.c_str(), key_prefix.length()) == 0) {
nvs_handle_t temp_handle;
err = nvs_open(this->name_space, NVS_READONLY, &temp_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) opening NVS handle for reading!", esp_err_to_name(err));
return err;
}
// call the processor with the key and value
processor(arg, std::string(info.key), this->get(std::string(info.key)));
nvs_close(temp_handle);
}
}
return ESP_OK;
}
esp_err_t NVSStorageHandler::process_filtered(FilterFunc filter_func, KeyValueProcessor processor, void* arg) const {
NVSIteratorGuard iterator_guard = this->create_iterator();
if (!iterator_guard.is_valid()) {
return iterator_guard.get_error();
}
const nvs_iterator_t& it = iterator_guard.get_iterator();
for (; it != NULL; iterator_guard.advance_iter()) {
nvs_entry_info_t info;
esp_err_t err = nvs_entry_info(it, &info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) getting NVS entry info!", esp_err_to_name(err));
return err;
}
// check if the key matches the filter function
std::string key_str(info.key);
if (filter_func(key_str)) {
nvs_handle_t temp_handle;
err = nvs_open(this->name_space, NVS_READONLY, &temp_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) opening NVS handle for reading!", esp_err_to_name(err));
return err;
}
// call the processor with the key and value
processor(arg, key_str, this->get(key_str));
nvs_close(temp_handle);
}
}
return ESP_OK;
}
void NVSStorageHandler::remove(const std::string& key) {
if (this->nvsHandle == 0) {
ESP_LOGE(TAG, "NVS handle is not initialized.");
return;
}
esp_err_t err = nvs_erase_key(this->nvsHandle, key.c_str());
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) deleting key %s from NVS!", esp_err_to_name(err), key.c_str());
} else {
nvs_commit(this->nvsHandle);
ESP_LOGI(TAG, "Key %s deleted from NVS.", key.c_str());
}
}