/** * @file cmd_nvs.cpp * @author Gustice * @brief NVS-Diagnosis utility (derived from ESP-IDF Console-Example) * * @copyright Copyright (c) 2024 */ #include "nvsFunctions.hpp" #include "esp_err.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "nvs.h" #include "nvsIo.hpp" #include "nvs_flash.h" #include #include #include #include #include #include "nvsDefines.hpp" static void initializeNvs() { esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { // 1.OTA app partition table has a smaller NVS partition size than the non-OTA // partition table. This size mismatch may cause NVS initialization to fail. // 2.NVS partition contains data in new format and cannot be recognized by this version of code. // If this happens, we erase NVS partition and initialize NVS again. ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); } NvsApi *NvsApi::_instance = nullptr; NvsApi::NvsApi(std::string mount) : _mountingPoint(mount) { initializeNvs(); } NvsApi::~NvsApi() { } NvsApi &NvsApi::getInstance(std::string mount) { if (_instance == nullptr) { init(mount); } return *_instance; } void NvsApi::init(std::string mount) { if (_instance != nullptr) { return; } _instance = new NvsApi(mount); } const auto [TypeStrMap, StrTypeMap] = CreateNvsDictionaries(); static char activeNameSpace[16] = "storage"; static const char *TAG = "NVS"; static nvs_type_t confStr2Type(const char *type) { if (auto search = StrTypeMap.find(type); search != StrTypeMap.end()) { return search->second; } ESP_LOGW(TAG, "type '%s' not found", type); return NVS_TYPE_ANY; } static const char *confType2Str(nvs_type_t type) { if (auto search = TypeStrMap.find(type); search != TypeStrMap.end()) { return search->second; } ESP_LOGW(TAG, "type %d not found", type); return "Unknown"; } struct SetArgs { struct arg_str *key; struct arg_str *type; struct arg_str *value; struct arg_end *end; }; struct GetArgs { struct arg_str *key; struct arg_str *type; struct arg_end *end; }; struct EraseArgs { struct arg_str *key; struct arg_end *end; }; struct EraseNameSpaceArgs { struct arg_str *nameSpace; struct arg_end *end; }; struct NameSpaceArgs { struct arg_str *nameSpace; struct arg_end *end; }; struct ListArgs { struct arg_str *partition; struct arg_str *nameSpace; struct arg_str *type; struct arg_end *end; }; static esp_err_t setValueInNvs(const char *key, const char *strType, const char *strValue) { nvs_type_t type = confStr2Type(strType); if (type == NVS_TYPE_ANY) { ESP_LOGE(TAG, "Type '%s' is undefined", strType); return ESP_ERR_NVS_TYPE_MISMATCH; } try { NvsIo nvs(activeNameSpace, NVS_READWRITE); esp_err_t err = ESP_FAIL; if (type == NVS_TYPE_I8) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_U8) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_I16) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_U16) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_I32) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_U32) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_I64) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_U64) { err = nvs.setValueByStr(key, strValue); } else if (type == NVS_TYPE_STR) { err = nvs.setString(key, strValue); } else if (type == NVS_TYPE_BLOB) { err = nvs.setBlob(key, strValue); } if (err != ESP_OK) { return err; } err = nvs.commit(); if (err == ESP_OK) { ESP_LOGI(TAG, "Value stored under key '%s'", key); } return ESP_OK; } catch (const std::exception &e) { ESP_LOGE(TAG, "%s", e.what()); } return ESP_FAIL; } void NvsApi::setValue(const std::string &key, const std::string &value) { if (setValueInNvs(key.c_str(), "str", value.c_str()) != ESP_OK) { ESP_LOGE(TAG, "setValue failed"); } } std::string NvsApi::getValue(const std::string &key) { try { NvsIo nvs(activeNameSpace, NVS_READONLY); auto opt = nvs.getString(key.c_str()); auto value = opt.value(); return value; } catch (const std::bad_optional_access &e) { ESP_LOGE(TAG, "Value '%s' not defined", key.c_str()); } catch (const std::exception &e) { ESP_LOGE(TAG, "%s", e.what()); } return ""; } static esp_err_t showValueFromNvs(const char *key, const char *strType) { nvs_type_t type = confStr2Type(strType); if (type == NVS_TYPE_ANY) { ESP_LOGE(TAG, "Type '%s' is undefined", strType); return ESP_ERR_NVS_TYPE_MISMATCH; } try { NvsIo nvs(activeNameSpace, NVS_READONLY); if (type == NVS_TYPE_I8) { auto opt = nvs.getInt8(key); printf("%d\n", opt.value()); } else if (type == NVS_TYPE_U8) { auto opt = nvs.getUint8(key); printf("%u\n", opt.value()); } else if (type == NVS_TYPE_I16) { auto opt = nvs.getInt16(key); printf("%u\n", opt.value()); } else if (type == NVS_TYPE_U16) { auto opt = nvs.getUint16(key); printf("%u\n", opt.value()); } else if (type == NVS_TYPE_I32) { auto opt = nvs.getInt32(key); printf("%" PRIi32 "\n", opt.value()); } else if (type == NVS_TYPE_U32) { auto opt = nvs.getUint32(key); printf("%" PRIu32 "\n", opt.value()); } else if (type == NVS_TYPE_I64) { auto opt = nvs.getInt64(key); printf("%lld\n", opt.value()); } else if (type == NVS_TYPE_U64) { auto opt = nvs.getUint64(key); printf("%llu\n", opt.value()); } else if (type == NVS_TYPE_STR) { auto opt = nvs.getString(key); printf("%s\n", opt.value().c_str()); } else if (type == NVS_TYPE_BLOB) { auto opt = nvs.getBlob(key); auto value = opt.value(); for (int i = 0; i < value.size(); i++) { printf("%02x", value[i]); } printf("\n"); } return ESP_OK; } catch (const std::bad_optional_access &e) { ESP_LOGE(TAG, "%s", "Value not defined"); } catch (const std::exception &e) { ESP_LOGE(TAG, "%s", e.what()); } return ESP_FAIL; } static esp_err_t erase(const char *key) { try { NvsIo nvs(activeNameSpace, NVS_READWRITE); esp_err_t err = nvs.erase(key); if (err != ESP_OK) { return err; } err = nvs.commit(); if (err != ESP_OK) { return err; } ESP_LOGI(TAG, "Value with key '%s' erased", key); return ESP_OK; } catch (const std::exception &e) { ESP_LOGE(TAG, "%s", e.what()); } return ESP_FAIL; } static esp_err_t eraseAllIn(const char *name) { try { NvsIo nvs(name, NVS_READWRITE); esp_err_t err = nvs.clear(); if (err != ESP_OK) { return err; } err = nvs.commit(); if (err != ESP_OK) { return err; } ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not"); return ESP_OK; } catch (const std::exception &e) { ESP_LOGE(TAG, "%s", e.what()); } return ESP_FAIL; } static int list(const char *part, const char *name, const char *strType) { nvs_type_t type = confStr2Type(strType); nvs_iterator_t it = NULL; esp_err_t result = nvs_entry_find(part, NULL, type, &it); if (result == ESP_ERR_NVS_NOT_FOUND) { ESP_LOGE(TAG, "No such entry was found"); return 1; } if (result != ESP_OK) { ESP_LOGE(TAG, "NVS error: %s", esp_err_to_name(result)); return 1; } do { nvs_entry_info_t info; nvs_entry_info(it, &info); result = nvs_entry_next(&it); if ((strlen(name) > 0) && (strcmp(info.namespace_name, name) != 0)) { continue; } printf("namespace '%s', key '%s', type '%s' \n", info.namespace_name, info.key, confType2Str(info.type)); } while (result == ESP_OK); if (result != ESP_ERR_NVS_NOT_FOUND) { // the last iteration ran into an internal error ESP_LOGE(TAG, "NVS error %s at current iteration, stopping.", esp_err_to_name(result)); return 1; } return 0; }