From be685107c69d6944fe5434ceb68b57ebb07969e4 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Tue, 18 Jun 2024 11:14:28 -0500 Subject: [PATCH] feat(nvs): refactor espp::Nvs to use espp::NvsHandle (#260) --- components/nvs/example/main/nvs_example.cpp | 2 +- components/nvs/include/nvs.hpp | 410 ++------------------ components/nvs/include/nvs_errc.hpp | 77 ++++ components/nvs/include/nvs_handle_espp.hpp | 193 +++++++-- 4 files changed, 253 insertions(+), 429 deletions(-) create mode 100644 components/nvs/include/nvs_errc.hpp diff --git a/components/nvs/example/main/nvs_example.cpp b/components/nvs/example/main/nvs_example.cpp index 8fe04095f..0d4a0889f 100644 --- a/components/nvs/example/main/nvs_example.cpp +++ b/components/nvs/example/main/nvs_example.cpp @@ -101,7 +101,7 @@ extern "C" void app_main(void) { // Open fmt::print("\nOpening Non-Volatile Storage (NVS) handle... "); // Handle will automatically close when going out of scope or when it's reset. - espp::NVSHandle storage("storage", ec); + espp::NvsHandle storage("storage", ec); fmt::print("Done\n"); ec.clear(); diff --git a/components/nvs/include/nvs.hpp b/components/nvs/include/nvs.hpp index 2a366336c..5baf9432c 100644 --- a/components/nvs/include/nvs.hpp +++ b/components/nvs/include/nvs.hpp @@ -2,76 +2,13 @@ #include -#include "nvs.h" -#include "nvs_flash.h" -#include "nvs_handle.hpp" +#include +#include +#include #include "base_component.hpp" - -namespace { -/** - * @brief Custom C++ error codes used for Auth - */ -enum class NvsErrc { - // no 0 - Namespace_Length_Too_Long = 10, - Key_Length_Too_Long, - Open_NVS_Handle_Failed, - Write_NVS_Failed, - Commit_NVS_Failed, - Read_NVS_Failed, - Key_Not_Found, - Init_NVS_Failed, - Erase_NVS_Failed, - Handle_Uninitialized, -}; - -struct NvsErrCategory : std::error_category { - const char *name() const noexcept override; - std::string message(int ev) const override; -}; - -const char *NvsErrCategory::name() const noexcept { return "nvs"; } - -std::string NvsErrCategory::message(int ev) const { - switch (static_cast(ev)) { - case NvsErrc::Namespace_Length_Too_Long: - return "Namespace too long, must be <= 15 characters"; - - case NvsErrc::Key_Length_Too_Long: - return "Key too long, must be <= 15 characters"; - - case NvsErrc::Open_NVS_Handle_Failed: - return "Failed to open NVS handle"; - - case NvsErrc::Write_NVS_Failed: - return "Failed to write to NVS"; - - case NvsErrc::Commit_NVS_Failed: - return "Failed to commit to NVS"; - - case NvsErrc::Read_NVS_Failed: - return "Failed to read from NVS"; - - case NvsErrc::Key_Not_Found: - return "Key not found in NVS"; - - case NvsErrc::Init_NVS_Failed: - return "Failed to initialize NVS"; - - case NvsErrc::Erase_NVS_Failed: - return "Failed to erase NVS"; - - case NvsErrc::Handle_Uninitialized: - return "Handle not initialized"; - - default: - return "(unrecognized error)"; - } -} - -const NvsErrCategory theNvsErrCategory{}; -} // namespace +#include "nvs_errc.hpp" +#include "nvs_handle_espp.hpp" namespace espp { /** @@ -103,7 +40,7 @@ class Nvs : public BaseComponent { } if (err != ESP_OK) { logger_.error("NVS INIT FAILED"); - ec = make_error_code(NvsErrc::Init_NVS_Failed); + ec = make_nvs_error_code(NvsErrc::Init_NVS_Failed); } } @@ -113,7 +50,7 @@ class Nvs : public BaseComponent { esp_err_t err = nvs_flash_erase(); if (err != ESP_OK) { logger_.error("Failed to erase NVS partition: {}", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Erase_NVS_Failed); + ec = make_nvs_error_code(NvsErrc::Erase_NVS_Failed); return; } } @@ -131,7 +68,7 @@ class Nvs : public BaseComponent { /// @brief Reads a variable from the NVS /// @param[in] ns_name Namespace of the variable to read /// @param[in] key NVS Key of the variable to read - /// @param[in] value Variable to read + /// @param[out] value Variable to read /// @param[out] ec Saves a std::error_code representing success or failure template void get_var(std::string_view ns_name, std::string_view key, T &value, std::error_code &ec) { @@ -141,190 +78,15 @@ class Nvs : public BaseComponent { /// @brief Reads a variable from the NVS /// @param[in] ns_name Namespace of the variable to read /// @param[in] key NVS Key of the variable to read - /// @param[in] value Variable to read + /// @param[out] value Variable to read /// @param[in] default_value If the key isn't found in the NVS, this value is saved to NVS /// @param[out] ec Saves a std::error_code representing success or failure template void get_or_set_var(std::string_view ns_name, std::string_view key, T &value, T default_value, std::error_code &ec) { - get_or_set_var(ns_name.data(), key.data(), value, default_value, ec); - } - - /// @brief Set a bool in the NVS - /// @param[in] ns_name Namespace of the bool to set - /// @param[in] key NVS Key of the bool to set - /// @param[in] value bool to set - /// @param[out] ec Saves a std::error_code representing success or failure - void set_var(std::string_view ns_name, std::string_view key, bool value, std::error_code &ec) { - set_var(ns_name.data(), key.data(), value, ec); - } - - /// @brief Set a string in the NVS - /// @param[in] ns_name Namespace of the string to set - /// @param[in] key NVS Key of the string to set - /// @param[in] value string to set - /// @param[out] ec Saves a std::error_code representing success or failure - void set_var(std::string_view ns_name, std::string_view key, const std::string &value, - std::error_code &ec) { - set_var(ns_name.data(), key.data(), value, ec); - } - - /// @brief Set a string in the NVS - /// @param[in] ns_name Namespace of the string to set - /// @param[in] key NVS Key of the string to set - /// @param[in] value string to set - /// @param[out] ec Saves a std::error_code representing success or failure - void set_var(const char *ns_name, const char *key, const std::string &value, - std::error_code &ec) { - check_lengths(ns_name, key, ec); - if (ec) - return; - esp_err_t err; - std::unique_ptr handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); - if (err != ESP_OK) { - logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), - ns_name); - ec = make_error_code(NvsErrc::Open_NVS_Handle_Failed); - return; - } - err = handle->set_string(key, value.data()); - if (err != ESP_OK) { - ec = make_error_code(NvsErrc::Write_NVS_Failed); - logger_.error("Error {} writing to NVS!", esp_err_to_name(err)); - return; - } - err = handle->commit(); - if (err != ESP_OK) { - ec = make_error_code(NvsErrc::Commit_NVS_Failed); - logger_.error("Error {} committing to NVS!", esp_err_to_name(err)); - } - return; - } - - /// @brief Reads a bool from the NVS - /// @param[in] ns_name Namespace of the bool to read - /// @param[in] key NVS Key of the bool to read - /// @param[in] value bool to read - /// @param[out] ec Saves a std::error_code representing success or failure - void get_var(std::string_view ns_name, std::string_view key, bool &value, std::error_code &ec) { get_var(ns_name.data(), key.data(), value, ec); } - /// @brief Reads a string from the NVS - /// @param[in] ns_name Namespace of the string to read - /// @param[in] key NVS Key of the string to read - /// @param[in] value string to read - /// @param[out] ec Saves a std::error_code representing success or failure - void get_var(std::string_view ns_name, std::string_view key, std::string &value, - std::error_code &ec) { - get_var(ns_name.data(), key.data(), value, ec); - } - - /// @brief Reads a string from the NVS - /// @param[in] ns_name Namespace of the string to read - /// @param[in] key NVS Key of the string to read - /// @param[in] value string to read - /// @param[out] ec Saves a std::error_code representing success or failure - void get_var(const char *ns_name, const char *key, std::string &value, std::error_code &ec) { - check_lengths(ns_name, key, ec); - if (ec) - return; - esp_err_t err; - std::unique_ptr handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); - if (err != ESP_OK) { - logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), - ns_name); - ec = make_error_code(NvsErrc::Open_NVS_Handle_Failed); - return; - } - std::size_t len = 0; - err = handle->get_item_size(nvs::ItemType::SZ, key, len); - if (err != ESP_OK) { - logger_.error("Error {} reading!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Read_NVS_Failed); - return; - } - value.resize(len); - err = handle->get_string(key, value.data(), len); - if (err != ESP_OK) { - ec = make_error_code(NvsErrc::Read_NVS_Failed); - logger_.error("Error {} reading from NVS!", esp_err_to_name(err)); - return; - } - return; - } - - /// @brief Reads a bool from the NVS. Writes the default value if the key is not found - /// @param[in] ns_name Namespace of the bool to read - /// @param[in] key NVS Key of the bool to read - /// @param[in] value bool to read - /// @param[in] default_value If the key isn't found in the NVS, this bool is saved to NVS - /// @param[out] ec Saves a std::error_code representing success or failure - void get_or_set_var(std::string_view ns_name, std::string_view key, bool &value, - bool default_value, std::error_code &ec) { - get_or_set_var(ns_name.data(), key.data(), value, default_value, ec); - } - - /// @brief Reads a string from the NVS. Writes the default value if the key is not found - /// @param[in] ns_name Namespace of the string to read - /// @param[in] key NVS Key of the string to read - /// @param[in] value string to read - /// @param[in] default_value If the key isn't found in the NVS, this string is saved to NVS - /// @param[out] ec Saves a std::error_code representing success or failure - void get_or_set_var(std::string_view ns_name, std::string_view key, std::string &value, - const std::string &default_value, std::error_code &ec) { - get_or_set_var(ns_name.data(), key.data(), value, default_value, ec); - } - - /// @brief Reads a string from the NVS. Writes the default value if the key is not found - /// @param[in] ns_name Namespace of the string to read - /// @param[in] key NVS Key of the string to read - /// @param[in] value string to read - /// @param[in] default_value If the key isn't found in the NVS, this string is saved to NVS - /// @param[out] ec Saves a std::error_code representing success or failure - void get_or_set_var(const char *ns_name, const char *key, std::string &value, - const std::string &default_value, std::error_code &ec) { - check_lengths(ns_name, key, ec); - if (ec) - return; - // don't set default value unless the key is valid - value = default_value; - esp_err_t err; - std::unique_ptr handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); - if (err != ESP_OK) { - logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), - ns_name); - ec = make_error_code(NvsErrc::Open_NVS_Handle_Failed); - return; - } - std::size_t len = 0; - err = handle->get_item_size(nvs::ItemType::SZ, key, len); - if (err != ESP_OK || len == 0) { - logger_.warn("The value is not initialized yet! ns::key '{}::{}' set to: {}", ns_name, key, - default_value); - err = handle->set_string(key, default_value.data()); - if (err != ESP_OK) { - logger_.error("Error {} writing to NVS!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Write_NVS_Failed); - return; - } - err = handle->commit(); - if (err != ESP_OK) { - logger_.error("Error {} committing to NVS!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Commit_NVS_Failed); - } - } else { - value.resize(len); - err = handle->get_string(key, value.data(), len); - if (err != ESP_OK) { - ec = make_error_code(NvsErrc::Read_NVS_Failed); - logger_.error("Error {} reading from NVS!", esp_err_to_name(err)); - return; - } - } - return; - } - /// @brief Save a variable in the NVS and commit /// @param[in] ns_name Namespace of the variable to save /// @param[in] key NVS Key of the variable to save @@ -333,71 +95,36 @@ class Nvs : public BaseComponent { /// @details Saves the key/variable pair, and commits the NVS. template void set_var(const char *ns_name, const char *key, T value, std::error_code &ec) { - check_lengths(ns_name, key, ec); + NvsHandle handle(ns_name, ec); if (ec) return; - esp_err_t err; - std::unique_ptr handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); - if (err != ESP_OK) { - logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), - ns_name); - ec = make_error_code(NvsErrc::Open_NVS_Handle_Failed); - return; - } - err = handle->set_item(key, value); - if (err != ESP_OK) { - ec = make_error_code(NvsErrc::Write_NVS_Failed); - logger_.error("Error {} writing to NVS!", esp_err_to_name(err)); + + handle.set(key, value, ec); + if (ec) return; - } - err = handle->commit(); - if (err != ESP_OK) { - ec = make_error_code(NvsErrc::Commit_NVS_Failed); - logger_.error("Error {} committing to NVS!", esp_err_to_name(err)); - } - return; + + handle.commit(ec); } /// @brief Reads a variable from the NVS /// @param[in] ns_name Namespace of the variable to read /// @param[in] key NVS Key of the variable to read - /// @param[in] value Variable to read + /// @param[out] value Variable to read /// @param[out] ec Saves a std::error_code representing success or failure /// @details Read the key/variable pair template void get_var(const char *ns_name, const char *key, T &value, std::error_code &ec) { - check_lengths(ns_name, key, ec); + NvsHandle handle(ns_name, ec); if (ec) return; - esp_err_t err; - std::unique_ptr handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); - if (err != ESP_OK) { - logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), - ns_name); - ec = make_error_code(NvsErrc::Open_NVS_Handle_Failed); - return; - } - T readvalue; - err = handle->get_item(key, readvalue); - switch (err) { - case ESP_OK: - value = readvalue; - break; - case ESP_ERR_NVS_NOT_FOUND: - logger_.error("The value is not initialized in NVS, ns::key = '{}::{}'", ns_name, key); - ec = make_error_code(NvsErrc::Key_Not_Found); - break; - default: - logger_.error("Error {} reading!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Read_NVS_Failed); - } - return; + + handle.get(key, value, ec); } /// @brief Reads a variable from the NVS /// @param[in] ns_name Namespace of the variable to read /// @param[in] key NVS Key of the variable to read - /// @param[in] value Variable to read + /// @param[out] value Variable to read /// @param[in] default_value If the key isn't found in the NVS, this value is saved to NVS /// @param[out] ec Saves a std::error_code representing success or failure /// @details Read the key/variable pair, If the key isn't found in the NVS, default_value is saved @@ -405,104 +132,11 @@ class Nvs : public BaseComponent { template void get_or_set_var(const char *ns_name, const char *key, T &value, T default_value, std::error_code &ec) { - check_lengths(ns_name, key, ec); + NvsHandle handle(ns_name, ec); if (ec) return; - // don't set default value unless the key is valid - value = default_value; - esp_err_t err; - std::unique_ptr handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); - if (err != ESP_OK) { - logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), - ns_name); - ec = make_error_code(NvsErrc::Open_NVS_Handle_Failed); - return; - } - T readvalue; - err = handle->get_item(key, readvalue); - switch (err) { - case ESP_OK: - value = readvalue; - break; - case ESP_ERR_NVS_NOT_FOUND: - logger_.warn("The value is not initialized yet! ns::key '{}::{}' set to: {}", ns_name, key, - default_value); - err = handle->set_item(key, default_value); - if (err != ESP_OK) { - logger_.error("Error {} writing to NVS!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Write_NVS_Failed); - return; - } - err = handle->commit(); - if (err != ESP_OK) { - logger_.error("Error {} committing to NVS!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Commit_NVS_Failed); - } - break; - default: - logger_.error("Error {} reading!", esp_err_to_name(err)); - ec = make_error_code(NvsErrc::Read_NVS_Failed); - } - return; - } - /// @brief Set a bool in the NVS - /// @param[in] ns_name Namespace of the bool to set - /// @param[in] key NVS Key of the bool to set - /// @param[in] value bool to set - /// @param[out] ec Saves a std::error_code representing success or failure - void set_var(const char *ns_name, const char *key, bool value, std::error_code &ec) { - uint8_t u8 = static_cast(value); - set_var(ns_name, key, u8, ec); + handle.get(key, value, default_value, ec); } - - /// @brief Reads a bool from the NVS - /// @param[in] ns_name Namespace of the bool to read - /// @param[in] key NVS Key of the bool to read - /// @param[in] value bool to read - /// @param[out] ec Saves a std::error_code representing success or failure - /// @details Read the key/variable pair - void get_var(const char *ns_name, const char *key, bool &value, std::error_code &ec) { - uint8_t u8 = static_cast(value); - get_var(ns_name, key, u8, ec); - if (!ec) - value = static_cast(u8); - } - - /// @brief Reads a bool from the NVS - /// @param[in] ns_name Namespace of the bool to read - /// @param[in] key NVS Key of the bool to read - /// @param[in] value bool to read - /// @param[in] default_value If the key isn't found in the NVS, this bool is saved to NVS - /// @param[out] ec Saves a std::error_code representing success or failure - /// @details Read the key/variable pair, If the key isn't found in the NVS, default_value is saved - /// to NVS - void get_or_set_var(const char *ns_name, const char *key, bool &value, bool default_value, - std::error_code &ec) { - uint8_t u8 = static_cast(value); - get_or_set_var(ns_name, key, u8, static_cast(default_value), ec); - if (!ec) - value = static_cast(u8); - } - -protected: - void check_lengths(const char *ns_name, const char *key, std::error_code &ec) { - if (strlen(ns_name) > 15) { - logger_.error("Namespace too long, must be <= 15 characters: {}", ns_name); - ec = make_error_code(NvsErrc::Namespace_Length_Too_Long); - return; - } - if (strlen(key) > 15) { - logger_.error("Key too long, must be <= 15 characters: {}", key); - ec = make_error_code(NvsErrc::Key_Length_Too_Long); - return; - } - } - - /** - * @brief overload of std::make_error_code used by custom error codes. - */ - std::error_code make_error_code(NvsErrc e) { return {static_cast(e), theNvsErrCategory}; } - }; // Class Nvs } // namespace espp diff --git a/components/nvs/include/nvs_errc.hpp b/components/nvs/include/nvs_errc.hpp new file mode 100644 index 000000000..23932a133 --- /dev/null +++ b/components/nvs/include/nvs_errc.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +namespace { +/** + * @brief Custom C++ error codes used for NVS operations. + */ +enum class NvsErrc { + // no 0 + Namespace_Length_Too_Long = 10, + Key_Length_Too_Long, + Open_NVS_Handle_Failed, + Write_NVS_Failed, + Commit_NVS_Failed, + Read_NVS_Failed, + Key_Not_Found, + Init_NVS_Failed, + Erase_NVS_Failed, + Handle_Uninitialized, +}; + +struct NvsErrCategory : std::error_category { + const char *name() const noexcept override; + std::string message(int ev) const override; +}; + +const char *NvsErrCategory::name() const noexcept { return "nvs"; } + +std::string NvsErrCategory::message(int ev) const { + switch (static_cast(ev)) { + case NvsErrc::Namespace_Length_Too_Long: + return "Namespace too long, must be <= 15 characters"; + + case NvsErrc::Key_Length_Too_Long: + return "Key too long, must be <= 15 characters"; + + case NvsErrc::Open_NVS_Handle_Failed: + return "Failed to open NVS handle"; + + case NvsErrc::Write_NVS_Failed: + return "Failed to write to NVS"; + + case NvsErrc::Commit_NVS_Failed: + return "Failed to commit to NVS"; + + case NvsErrc::Read_NVS_Failed: + return "Failed to read from NVS"; + + case NvsErrc::Key_Not_Found: + return "Key not found in NVS"; + + case NvsErrc::Init_NVS_Failed: + return "Failed to initialize NVS"; + + case NvsErrc::Erase_NVS_Failed: + return "Failed to erase NVS"; + + case NvsErrc::Handle_Uninitialized: + return "Handle not initialized"; + + default: + return "(unrecognized error)"; + } +} + +const NvsErrCategory theNvsErrCategory{}; + +/** + * @brief overload of std::make_error_code used by custom error codes. + */ +[[maybe_unused]] std::error_code make_nvs_error_code(NvsErrc e) { + return {static_cast(e), theNvsErrCategory}; +} + +} // namespace diff --git a/components/nvs/include/nvs_handle_espp.hpp b/components/nvs/include/nvs_handle_espp.hpp index 1e2014347..04d92cb30 100644 --- a/components/nvs/include/nvs_handle_espp.hpp +++ b/components/nvs/include/nvs_handle_espp.hpp @@ -2,12 +2,14 @@ #include -#include "nvs.h" -#include "nvs.hpp" -#include "nvs_flash.h" -#include "nvs_handle.hpp" +#include +#include +#include +#include #include "base_component.hpp" +#include "nvs_errc.hpp" +#include "nvs_handle_espp.hpp" namespace espp { /** @@ -17,17 +19,17 @@ namespace espp { * encapsulates all direct interactions with the NVS to ensure proper error handling * and namespace management. * - * @section nvshandle_ex1 NVSHandle Example + * @section nvshandle_ex1 NvsHandle Example * @snippet nvs_example.cpp nvshandle example */ -class NVSHandle : public BaseComponent { +class NvsHandle : public BaseComponent { public: - /// @brief Construct a new NVSHandle object + /// @brief Construct a new NvsHandle object /// @param[in] ns_name Namespace for NVS /// @param[out] ec Saves a std::error_code representing success or failure - /// @details Create an NVSHandle object for the key-value pairs in the ns_name namespace - explicit NVSHandle(const char *ns_name, std::error_code &ec) - : BaseComponent("NVSHandle", espp::Logger::Verbosity::WARN) { + /// @details Create an NvsHandle object for the key-value pairs in the ns_name namespace + explicit NvsHandle(const char *ns_name, std::error_code &ec) + : BaseComponent("NvsHandle", espp::Logger::Verbosity::WARN) { if (strlen(ns_name) > 15) { logger_.error("Namespace too long, must be <= 15 characters: {}", ns_name); ec = make_error_code(NvsErrc::Namespace_Length_Too_Long); @@ -35,7 +37,7 @@ class NVSHandle : public BaseComponent { } esp_err_t err; - handle = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); + handle_ = nvs::open_nvs_handle(ns_name, NVS_READWRITE, &err); if (err != ESP_OK) { logger_.error("Error {} opening NVS handle for namespace '{}'!", esp_err_to_name(err), ns_name); @@ -45,20 +47,19 @@ class NVSHandle : public BaseComponent { /// @brief Reads a variable from the NVS /// @param[in] key NVS Key of the variable to read - /// @param[in] value Variable to read + /// @param[out] value Variable to read /// @param[out] ec Saves a std::error_code representing success or failure /// @details Reads the value of key into value, if key exists template void get(const char *key, T &value, std::error_code &ec) { - check_key_length(key, ec); - if (ec) + if (!check_handle_initialized(ec)) return; - if (!check_handle_initialized(ec)) + if (!check_key(key, ec)) return; esp_err_t err; T readvalue; - err = handle->get_item(key, readvalue); + err = handle_->get_item(key, readvalue); switch (err) { case ESP_OK: value = readvalue; @@ -76,7 +77,7 @@ class NVSHandle : public BaseComponent { /// @brief Reads a variable from the NVS /// @param[in] key NVS Key of the variable to read - /// @param[in] value Variable to read + /// @param[out] value Variable to read /// @param[out] ec Saves a std::error_code representing success or failure template void get(std::string_view key, T &value, std::error_code &ec) { get(key.data(), value, ec); @@ -84,7 +85,7 @@ class NVSHandle : public BaseComponent { /// @brief Reads a bool from the NVS /// @param[in] key NVS Key of the bool to read - /// @param[in] value bool to read + /// @param[out] value bool to read /// @param[out] ec Saves a std::error_code representing success or failure /// @details Read the key/variable pair void get(const char *key, bool &value, std::error_code &ec) { @@ -96,13 +97,13 @@ class NVSHandle : public BaseComponent { /// @brief Reads a bool from the NVS /// @param[in] key NVS Key of the bool to read - /// @param[in] value bool to read + /// @param[out] value bool to read /// @param[out] ec Saves a std::error_code representing success or failure void get(std::string_view key, bool &value, std::error_code &ec) { get(key.data(), value, ec); } /// @brief Reads a string from the NVS /// @param[in] key NVS Key of the string to read - /// @param[in] value string to read + /// @param[inout] value string to read /// @param[out] ec Saves a std::error_code representing success or failure void get(std::string_view key, std::string &value, std::error_code &ec) { get(key.data(), value, ec); @@ -110,26 +111,25 @@ class NVSHandle : public BaseComponent { /// @brief Reads a string from the NVS /// @param[in] key NVS Key of the string to read - /// @param[in] value string to read + /// @param[out] value string to read /// @param[out] ec Saves a std::error_code representing success or failure void get(const char *key, std::string &value, std::error_code &ec) { - check_key_length(key, ec); - if (ec) + if (!check_handle_initialized(ec)) return; - if (!check_handle_initialized(ec)) + if (!check_key(key, ec)) return; esp_err_t err; std::size_t len = 0; - err = handle->get_item_size(nvs::ItemType::SZ, key, len); + err = handle_->get_item_size(nvs::ItemType::SZ, key, len); if (err != ESP_OK) { logger_.error("Error {} reading!", esp_err_to_name(err)); ec = make_error_code(NvsErrc::Read_NVS_Failed); return; } value.resize(len); - err = handle->get_string(key, value.data(), len); + err = handle_->get_string(key, value.data(), len); if (err != ESP_OK) { ec = make_error_code(NvsErrc::Read_NVS_Failed); logger_.error("Error {} reading from NVS!", esp_err_to_name(err)); @@ -138,21 +138,131 @@ class NVSHandle : public BaseComponent { return; } + /// @brief Reads a variable from the NVS + /// @param[in] key NVS Key of the variable to read + /// @param[out] value Variable to read + /// @param[in] default_value Default value to return if key is not found + /// @param[out] ec Saves a std::error_code representing success or failure + /// @details Reads the value of key into value, if key exists + template + void get(const char *key, T &value, const T default_value, std::error_code &ec) { + if (!check_handle_initialized(ec)) + return; + + if (!check_key(key, ec)) + return; + + esp_err_t err; + T readvalue; + err = handle_->get_item(key, readvalue); + switch (err) { + case ESP_OK: + value = readvalue; + break; + case ESP_ERR_NVS_NOT_FOUND: + logger_.warn("The value is not initialized yet! key '{}' set to: {}", key, default_value); + set(key, default_value, ec); + if (ec) + return; + value = default_value; + break; + default: + logger_.error("Error {} reading!", esp_err_to_name(err)); + ec = make_error_code(NvsErrc::Read_NVS_Failed); + } + return; + } + + /// @brief Reads a variable from the NVS + /// @param[in] key NVS Key of the variable to read + /// @param[out] value Variable to read + /// @param[in] default_value Default value to return if key is not found + /// @param[out] ec Saves a std::error_code representing success or failure + template + void get(std::string_view key, T &value, const T default_value, std::error_code &ec) { + get(key.data(), value, default_value, ec); + } + + /// @brief Reads a bool from the NVS + /// @param[in] key NVS Key of the bool to read + /// @param[out] value bool to read + /// @param[in] default_value Default value to return if key is not found + /// @param[out] ec Saves a std::error_code representing success or failure + /// @details Read the key/variable pair + void get(const char *key, bool &value, const bool default_value, std::error_code &ec) { + uint8_t u8 = static_cast(value); + uint8_t u8_default = static_cast(default_value); + get(key, u8, u8_default, ec); + if (!ec) + value = static_cast(u8); + } + + /// @brief Reads a bool from the NVS + /// @param[in] key NVS Key of the bool to read + /// @param[out] value bool to read + /// @param[in] default_value Default value to return if key is not found + /// @param[out] ec Saves a std::error_code representing success or failure + void get(std::string_view key, bool &value, const bool default_value, std::error_code &ec) { + get(key.data(), value, default_value, ec); + } + + /// @brief Reads a string from the NVS + /// @param[in] key NVS Key of the string to read + /// @param[out] value string to read + /// @param[in] default_value Default value to return if key is not found + /// @param[out] ec Saves a std::error_code representing success or failure + void get(std::string_view key, std::string &value, const std::string &default_value, + std::error_code &ec) { + get(key.data(), value, default_value, ec); + } + + /// @brief Reads a string from the NVS + /// @param[in] key NVS Key of the string to read + /// @param[out] value string to read + /// @param[in] default_value Default value to return if key is not found + /// @param[out] ec Saves a std::error_code representing success or failure + void get(const char *key, std::string &value, const std::string &default_value, + std::error_code &ec) { + if (!check_handle_initialized(ec)) + return; + + if (!check_key(key, ec)) + return; + + esp_err_t err; + std::size_t len = 0; + err = handle_->get_item_size(nvs::ItemType::SZ, key, len); + if (err != ESP_OK || len == 0) { + logger_.warn("The value is not initialized yet! key '{}' set to: {}", key, default_value); + set(key, default_value, ec); + if (ec) + return; + value = default_value; + } else { + value.resize(len); + err = handle_->get_string(key, value.data(), len); + if (err != ESP_OK) { + ec = make_error_code(NvsErrc::Read_NVS_Failed); + logger_.error("Error {} reading from NVS!", esp_err_to_name(err)); + return; + } + } + } + /// @brief Save a variable in the NVS /// @param[in] key NVS Key of the variable to read /// @param[in] value Variable to read /// @param[out] ec Saves a std::error_code representing success or failure /// @details Saves the key/variable pair without committing the NVS. template void set(const char *key, T value, std::error_code &ec) { - check_key_length(key, ec); - if (ec) + if (!check_handle_initialized(ec)) return; - if (!check_handle_initialized(ec)) + if (!check_key(key, ec)) return; esp_err_t err; - err = handle->set_item(key, value); + err = handle_->set_item(key, value); if (err != ESP_OK) { ec = make_error_code(NvsErrc::Write_NVS_Failed); logger_.error("Error {} writing to NVS!", esp_err_to_name(err)); @@ -196,15 +306,14 @@ class NVSHandle : public BaseComponent { /// @param[in] value string to set /// @param[out] ec Saves a std::error_code representing success or failure void set(const char *key, const std::string &value, std::error_code &ec) { - check_key_length(key, ec); - if (ec) + if (!check_handle_initialized(ec)) return; - if (!check_handle_initialized(ec)) + if (!check_key(key, ec)) return; esp_err_t err; - err = handle->set_string(key, value.data()); + err = handle_->set_string(key, value.data()); if (err != ESP_OK) { ec = make_error_code(NvsErrc::Write_NVS_Failed); logger_.error("Error {} writing to NVS!", esp_err_to_name(err)); @@ -217,7 +326,10 @@ class NVSHandle : public BaseComponent { /// @param[out] ec Saves a std::error_code representing success or failure /// @details Commits changes to the NVS void commit(std::error_code &ec) { - esp_err_t err = handle->commit(); + if (!check_handle_initialized(ec)) + return; + + esp_err_t err = handle_->commit(); if (err != ESP_OK) { logger_.error("Error {} committing to NVS!", esp_err_to_name(err)); ec = make_error_code(NvsErrc::Commit_NVS_Failed); @@ -226,18 +338,19 @@ class NVSHandle : public BaseComponent { } protected: - std::unique_ptr handle; + std::unique_ptr handle_; - void check_key_length(const char *key, std::error_code &ec) { + bool check_key(const char *key, std::error_code &ec) { if (strlen(key) > 15) { logger_.error("Key too long, must be <= 15 characters: {}", key); ec = make_error_code(NvsErrc::Key_Length_Too_Long); - return; + return false; } + return true; } bool check_handle_initialized(std::error_code &ec) { - if (!handle) { + if (!handle_) { ec = make_error_code(NvsErrc::Handle_Uninitialized); logger_.error("NVS Handle not initialized!"); return false; @@ -250,5 +363,5 @@ class NVSHandle : public BaseComponent { */ std::error_code make_error_code(NvsErrc e) { return {static_cast(e), theNvsErrCategory}; } -}; // Class NVSHandle +}; // Class NvsHandle } // namespace espp