Initial code import
This commit is contained in:
61
CMakeLists.txt
Normal file
61
CMakeLists.txt
Normal file
@@ -0,0 +1,61 @@
|
||||
set(headers
|
||||
src/configconstraints_base.h
|
||||
src/configconstraints_espchrono.h
|
||||
src/configmanager.h
|
||||
src/configmanager_priv.h
|
||||
src/configutils_base.h
|
||||
src/configutils_cpputils.h
|
||||
src/configutils_espchrono.h
|
||||
src/configutils_espcpputils.h
|
||||
src/configutils_espwifistack.h
|
||||
src/configutils_optional.h
|
||||
src/configutils_priv_enum.h
|
||||
src/configutils_sntp.h
|
||||
src/configwrapper.h
|
||||
src/configwrapperinterface.h
|
||||
src/configwrapper_priv.h
|
||||
)
|
||||
|
||||
set(sources
|
||||
src/configwrapper_base.cpp
|
||||
src/configwrapper_cpputils.cpp
|
||||
src/configwrapper_espchrono.cpp
|
||||
src/configwrapper_espcpputils.cpp
|
||||
src/configwrapper_espwifistack.cpp
|
||||
src/configwrapper_optional.cpp
|
||||
src/configwrapper_sntp.cpp
|
||||
src/configwrapperinterface.cpp
|
||||
)
|
||||
|
||||
set(dependencies
|
||||
ArduinoJson
|
||||
cpputils
|
||||
espchrono
|
||||
espcpputils
|
||||
esphttpdutils
|
||||
espwifistack
|
||||
date
|
||||
espchrono
|
||||
expected
|
||||
fmt
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
INCLUDE_DIRS
|
||||
src
|
||||
SRCS
|
||||
${headers}
|
||||
${sources}
|
||||
REQUIRES
|
||||
${dependencies}
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_TARGET}
|
||||
PRIVATE
|
||||
-fstack-reuse=all
|
||||
-fstack-protector-all
|
||||
-Wno-unused-function
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-parentheses
|
||||
)
|
44
Kconfig.projbuild
Normal file
44
Kconfig.projbuild
Normal file
@@ -0,0 +1,44 @@
|
||||
menu "ESP Config lib settings"
|
||||
|
||||
choice LOG_LOCAL_LEVEL_CONFIG
|
||||
bool "go-e CONFIG log verbosity"
|
||||
default LOG_LOCAL_LEVEL_CONFIG_INFO
|
||||
help
|
||||
Specify how much output to compile into the binary.
|
||||
You can set lower verbosity level at runtime using
|
||||
esp_log_level_set function.
|
||||
|
||||
Note that this setting limits which log statements
|
||||
are compiled into the program in go-e sources. So
|
||||
setting this to, say, "Warning" would mean that
|
||||
changing log level to "Debug" at runtime will not
|
||||
be possible.
|
||||
|
||||
config LOG_LOCAL_LEVEL_CONFIG_NONE
|
||||
bool "No output"
|
||||
config LOG_LOCAL_LEVEL_CONFIG_ERROR
|
||||
bool "Error"
|
||||
config LOG_LOCAL_LEVEL_CONFIG_WARN
|
||||
bool "Warning"
|
||||
config LOG_LOCAL_LEVEL_CONFIG_INFO
|
||||
bool "Info"
|
||||
config LOG_LOCAL_LEVEL_CONFIG_DEBUG
|
||||
bool "Debug"
|
||||
config LOG_LOCAL_LEVEL_CONFIG_VERBOSE
|
||||
bool "Verbose"
|
||||
endchoice
|
||||
|
||||
config LOG_LOCAL_LEVEL_CONFIG
|
||||
int
|
||||
default 0 if LOG_LOCAL_LEVEL_CONFIG_NONE
|
||||
default 1 if LOG_LOCAL_LEVEL_CONFIG_ERROR
|
||||
default 2 if LOG_LOCAL_LEVEL_CONFIG_WARN
|
||||
default 3 if LOG_LOCAL_LEVEL_CONFIG_INFO
|
||||
default 4 if LOG_LOCAL_LEVEL_CONFIG_DEBUG
|
||||
default 5 if LOG_LOCAL_LEVEL_CONFIG_VERBOSE
|
||||
|
||||
config SEPARATE_FACTORY_NVS_PARTITION
|
||||
bool "Separate factory nvs partition (for easy erasure of user settings)"
|
||||
default y
|
||||
|
||||
endmenu
|
104
src/configconstraints_base.h
Normal file
104
src/configconstraints_base.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <tl/expected.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
// local includes
|
||||
#include "configwrapper.h"
|
||||
#include "esphttpdutils.h"
|
||||
|
||||
namespace espconfig {
|
||||
template<int MAX_LENGTH>
|
||||
ConfigConstraintReturnType StringMaxSize(const std::string &str)
|
||||
{
|
||||
if (str.size() > MAX_LENGTH)
|
||||
return tl::make_unexpected(fmt::format("String length {} exceeds maximum {}", str.size(), MAX_LENGTH));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<int MIN_LENGTH, int MAX_LENGTH>
|
||||
ConfigConstraintReturnType StringMinMaxSize(const std::string &str)
|
||||
{
|
||||
if (str.size() < MIN_LENGTH || str.size() > MAX_LENGTH)
|
||||
return tl::make_unexpected(fmt::format("String length {} exceeds range {} to {}", str.size(), MIN_LENGTH, MAX_LENGTH));
|
||||
return {};
|
||||
}
|
||||
|
||||
inline ConfigConstraintReturnType StringEmpty(const std::string &str)
|
||||
{
|
||||
if (!str.empty())
|
||||
return tl::make_unexpected("String has to be empty");
|
||||
return {};
|
||||
}
|
||||
|
||||
inline ConfigConstraintReturnType StringValidUrl(const std::string &str)
|
||||
{
|
||||
return esphttpdutils::urlverify(str);
|
||||
}
|
||||
|
||||
template<ConfigWrapper<std::string>::ConstraintCallback callback0, ConfigWrapper<std::string>::ConstraintCallback callback1>
|
||||
ConfigConstraintReturnType StringOr(const std::string &str)
|
||||
{
|
||||
const auto result0 = callback0(str);
|
||||
if (result0)
|
||||
return {};
|
||||
const auto result1 = callback1(str);
|
||||
if (result1)
|
||||
return {};
|
||||
return tl::make_unexpected(fmt::format("None of the following 2 constraints succeded: {} | {}", result0.error(), result1.error()));
|
||||
}
|
||||
|
||||
template<ConfigWrapper<std::string>::ConstraintCallback callback0, ConfigWrapper<std::string>::ConstraintCallback callback1>
|
||||
ConfigConstraintReturnType StringAnd(const std::string &str)
|
||||
{
|
||||
if (const auto result = callback0(str); !result)
|
||||
return tl::make_unexpected(result.error());
|
||||
if (const auto result = callback1(str); !result)
|
||||
return tl::make_unexpected(result.error());
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, T ... ALLOWED_VALUES>
|
||||
ConfigConstraintReturnType OneOf(typename ConfigWrapper<T>::value_t val)
|
||||
{
|
||||
if (!((ALLOWED_VALUES == val) || ...))
|
||||
tl::make_unexpected("Value not one of the allowed ones");
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, T MIN_VALUE>
|
||||
ConfigConstraintReturnType MinValue(typename ConfigWrapper<T>::value_t val)
|
||||
{
|
||||
if (val < MIN_VALUE)
|
||||
return tl::make_unexpected(fmt::format("Value {} exceeds minimum {}", val, MIN_VALUE));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, T MAX_VALUE>
|
||||
ConfigConstraintReturnType MaxValue(typename ConfigWrapper<T>::value_t val)
|
||||
{
|
||||
if (val > MAX_VALUE)
|
||||
return tl::make_unexpected(fmt::format("Value {} exceeds maximum {}", val, MAX_VALUE));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, T MIN_VALUE, T MAX_VALUE>
|
||||
ConfigConstraintReturnType MinMaxValue(typename ConfigWrapper<T>::value_t val)
|
||||
{
|
||||
if (val < MIN_VALUE || val > MAX_VALUE)
|
||||
return tl::make_unexpected(fmt::format("Value {} exceeds range {} to {}", val, MIN_VALUE, MAX_VALUE));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, T MIN_VALUE, T MAX_VALUE>
|
||||
ConfigConstraintReturnType MinMaxOrZeroValue(typename ConfigWrapper<T>::value_t val)
|
||||
{
|
||||
if (val != 0 && (val < MIN_VALUE || val > MAX_VALUE))
|
||||
return tl::make_unexpected(fmt::format("Value {} exceeds constraint 0 or range {} to {}", val, MIN_VALUE, MAX_VALUE));
|
||||
return {};
|
||||
}
|
||||
} // namespace espconfig
|
22
src/configconstraints_espchrono.h
Normal file
22
src/configconstraints_espchrono.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <tl/expected.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
// local includes
|
||||
#include "configwrapper.h"
|
||||
#include "espchrono.h"
|
||||
|
||||
namespace espconfig {
|
||||
inline ConfigConstraintReturnType MinTimeSyncInterval(espchrono::milliseconds32 val)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
if (val < 15s)
|
||||
return tl::make_unexpected("SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds");
|
||||
return {};
|
||||
}
|
||||
} // namespace espconfig
|
56
src/configmanager.h
Normal file
56
src/configmanager.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// local includes
|
||||
#include "configwrapper.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
template<typename ConfigContainer>
|
||||
class ConfigManager : public ConfigContainer
|
||||
{
|
||||
public:
|
||||
using ConfigContainer::ConfigContainer;
|
||||
|
||||
nvs_handle_t nvs_handle_user{};
|
||||
#ifdef CONFIG_SEPARATE_FACTORY_NVS_PARTITION
|
||||
nvs_handle_t nvs_handle_factory{};
|
||||
#endif
|
||||
|
||||
esp_err_t init();
|
||||
//bool erase();
|
||||
ConfigStatusReturnType reset();
|
||||
|
||||
ConfigWrapperInterface *findConfigByKey(std::string_view key);
|
||||
|
||||
template<typename T>
|
||||
ConfigStatusReturnType write_config(ConfigWrapper<T> &config, typename ConfigWrapper<T>::value_t value);
|
||||
|
||||
inline ConfigStatusReturnType reset_config(ConfigWrapperInterface &config);
|
||||
|
||||
inline ConfigStatusReturnType force_reset_config(ConfigWrapperInterface &config);
|
||||
};
|
||||
|
||||
template<typename ConfigContainer> template<typename T>
|
||||
ConfigStatusReturnType ConfigManager<ConfigContainer>::write_config(ConfigWrapper<T> &config, typename ConfigWrapper<T>::value_t value)
|
||||
{
|
||||
return config.write(nvs_handle_user, value);
|
||||
}
|
||||
|
||||
template<typename ConfigContainer>
|
||||
inline ConfigStatusReturnType ConfigManager<ConfigContainer>::reset_config(ConfigWrapperInterface &config)
|
||||
{
|
||||
return config.reset(nvs_handle_user);
|
||||
}
|
||||
|
||||
template<typename ConfigContainer>
|
||||
inline ConfigStatusReturnType ConfigManager<ConfigContainer>::force_reset_config(ConfigWrapperInterface &config)
|
||||
{
|
||||
return config.forceReset(nvs_handle_user);
|
||||
}
|
||||
|
||||
} // namespace espconfig
|
173
src/configmanager_priv.h
Normal file
173
src/configmanager_priv.h
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include "configmanager.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#define LOG_LOCAL_LEVEL CONFIG_LOG_LOCAL_LEVEL_CONFIG
|
||||
|
||||
// esp-idf includes
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
// local includes
|
||||
#include "espchrono.h"
|
||||
|
||||
#define INSTANTIATE_CONFIGMANAGER_TEMPLATES(Type) \
|
||||
template esp_err_t ConfigManager<Type>::init(); \
|
||||
/* template bool ConfigManager<Type>::erase(); */ \
|
||||
template ConfigStatusReturnType ConfigManager<Type>::reset(); \
|
||||
template ConfigWrapperInterface *ConfigManager<Type>::findConfigByKey(std::string_view key);
|
||||
|
||||
namespace espconfig {
|
||||
namespace {
|
||||
constexpr const char * const TAG = "CONFIG";
|
||||
} // namespace
|
||||
|
||||
template<typename ConfigContainer>
|
||||
esp_err_t ConfigManager<ConfigContainer>::init()
|
||||
{
|
||||
ESP_LOGD(TAG, "called");
|
||||
|
||||
auto before = espchrono::millis_clock::now();
|
||||
#ifndef CONFIG_NVS_ENCRYPTION
|
||||
{
|
||||
const auto result = nvs_flash_init_partition("nvs");
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_init() for user returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEPARATE_FACTORY_NVS_PARTITION
|
||||
{
|
||||
const auto result = nvs_flash_init_partition("nvsFac");
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_init() for factory returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
const esp_partition_t *key_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
|
||||
if (!key_part)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_keys partition not found!");
|
||||
return ESP_ERR_NVS_PART_NOT_FOUND;
|
||||
}
|
||||
|
||||
nvs_sec_cfg_t cfg;
|
||||
|
||||
{
|
||||
const auto result = nvs_flash_read_security_cfg(key_part, &cfg);
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_read_security_cfg() returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
if (result == ESP_ERR_NVS_KEYS_NOT_INITIALIZED)
|
||||
{
|
||||
const auto result = nvs_flash_generate_keys(key_part, &cfg);
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_generate_keys() returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto result = nvs_flash_secure_init_partition("nvs", &cfg);
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_secure_init() for user returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEPARATE_FACTORY_NVS_PARTITION
|
||||
{
|
||||
const auto result = nvs_flash_secure_init_partition("nvsFac", &cfg);
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_secure_init() for user returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
{
|
||||
const auto result = nvs_open_from_partition("nvs", "recharge", NVS_READWRITE, &nvs_handle_user);
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_open() for user partition returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEPARATE_FACTORY_NVS_PARTITION
|
||||
{
|
||||
const auto result = nvs_open_from_partition("nvsFac", "recharge", NVS_READWRITE, &nvs_handle_factory);
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_open() for factory partition returned: %s", esp_err_to_name(result));
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto after = espchrono::millis_clock::now();
|
||||
|
||||
ESP_LOGI(TAG, "initializing NVS took %lldms", std::chrono::milliseconds{after-before}.count());
|
||||
|
||||
before = espchrono::millis_clock::now();
|
||||
|
||||
bool success = true;
|
||||
for (ConfigWrapperInterface &config : ConfigContainer::getAllConfigParams())
|
||||
if (const auto result = config.loadFromFlash(nvs_handle_user); !result)
|
||||
{
|
||||
ESP_LOGE(TAG, "config parameter %s failed to load: %.*s", config.nvsName(), result.error().size(), result.error().data());
|
||||
success = false;
|
||||
}
|
||||
|
||||
after = espchrono::millis_clock::now();
|
||||
|
||||
ESP_LOGI(TAG, "loading all config params took %lldms", std::chrono::milliseconds{after-before}.count());
|
||||
|
||||
return success ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
//template<typename ConfigContainer>
|
||||
//bool ConfigManager<ConfigContainer>::erase()
|
||||
//{
|
||||
// ESP_LOGW(TAG, "called");
|
||||
|
||||
// nvs_close(nvs_handle_user);
|
||||
// const auto err = nvs_flash_erase();
|
||||
// ESP_LOG_LEVEL_LOCAL((err == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "nvs_flash_erase() returned: %s", esp_err_to_name(err));
|
||||
// if (err != ESP_OK)
|
||||
// return false;
|
||||
|
||||
// return init();
|
||||
//}
|
||||
|
||||
template<typename ConfigContainer>
|
||||
ConfigStatusReturnType ConfigManager<ConfigContainer>::reset()
|
||||
{
|
||||
ESP_LOGW(TAG, "called");
|
||||
|
||||
std::string message;
|
||||
|
||||
for (ConfigWrapperInterface &config : ConfigContainer::getAllConfigParams())
|
||||
if (const auto result = reset_config(config); !result)
|
||||
{
|
||||
if (!message.empty())
|
||||
message.append(", ");
|
||||
message.append(fmt::format("reset of {} failed: {}", config.nvsName(), result.error()));
|
||||
}
|
||||
|
||||
if (!message.empty())
|
||||
return tl::make_unexpected(std::move(message));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ConfigContainer>
|
||||
ConfigWrapperInterface *ConfigManager<ConfigContainer>::findConfigByKey(std::string_view key)
|
||||
{
|
||||
const auto configParams = ConfigContainer::getAllConfigParams();
|
||||
const auto iter = std::find_if(std::cbegin(configParams), std::cend(configParams),
|
||||
[&key](const ConfigWrapperInterface &config){ return key == config.nvsName(); });
|
||||
return iter != std::cend(configParams) ? &iter->get() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace espconfig
|
77
src/configutils_base.h
Normal file
77
src/configutils_base.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// local includes
|
||||
#include "futurecpp.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, int8_t* out_value) { return nvs_get_i8 (handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint8_t* out_value) { return nvs_get_u8 (handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, int16_t* out_value) { return nvs_get_i16(handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint16_t* out_value){ return nvs_get_u16(handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, int32_t* out_value) { return nvs_get_i32(handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint32_t* out_value){ return nvs_get_u32(handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, int64_t* out_value) { return nvs_get_i64(handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, uint64_t* out_value){ return nvs_get_u64(handle, key, out_value); }
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value)
|
||||
{
|
||||
uint8_t temp;
|
||||
const auto result = nvs_get(handle, key, &temp);
|
||||
if (result == ESP_OK && out_value)
|
||||
*out_value = temp;
|
||||
return result;
|
||||
}
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, float* out_value)
|
||||
{
|
||||
uint32_t temp;
|
||||
const auto result = nvs_get(handle, key, &temp);
|
||||
if (result == ESP_OK)
|
||||
*out_value = std::bit_cast<float>(temp);
|
||||
return result;
|
||||
}
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, double* out_value)
|
||||
{
|
||||
uint64_t temp;
|
||||
const auto result = nvs_get(handle, key, &temp);
|
||||
if (result == ESP_OK)
|
||||
*out_value = std::bit_cast<double>(temp);
|
||||
return result;
|
||||
}
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, std::string* out_value)
|
||||
{
|
||||
size_t length;
|
||||
if (const esp_err_t result = nvs_get_str(handle, key, nullptr, &length); result != ESP_OK)
|
||||
return result;
|
||||
|
||||
char buf[length];
|
||||
if (const esp_err_t result = nvs_get_str(handle, key, buf, &length); result != ESP_OK)
|
||||
return result;
|
||||
|
||||
*out_value = buf;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, int8_t value) { return nvs_set_i8 (handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint8_t value) { return nvs_set_u8 (handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, int16_t value) { return nvs_set_i16(handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint16_t value) { return nvs_set_u16(handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, int32_t value) { return nvs_set_i32(handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint32_t value) { return nvs_set_u32(handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, int64_t value) { return nvs_set_i64(handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, uint64_t value) { return nvs_set_u64(handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, bool value) { return nvs_set_u8 (handle, key, value); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, float value) { return nvs_set(handle, key, std::bit_cast<uint32_t>(value)); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, double value) { return nvs_set(handle, key, std::bit_cast<uint64_t>(value)); }
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value) { return nvs_set_str(handle, key, value.c_str()); }
|
||||
|
||||
} // namespace espconfig
|
28
src/configutils_cpputils.h
Normal file
28
src/configutils_cpputils.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// local includes
|
||||
#include "configutils_base.h"
|
||||
#include "color_utils.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, cpputils::ColorHelper* out_value)
|
||||
{
|
||||
uint32_t temp;
|
||||
const auto result = nvs_get(handle, key, &temp);
|
||||
if (result == ESP_OK)
|
||||
*out_value = cpputils::numberToColor(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, cpputils::ColorHelper value)
|
||||
{
|
||||
return nvs_set(handle, key, cpputils::colorToNumber(value));
|
||||
}
|
||||
|
||||
} // namespace espconfig
|
32
src/configutils_espchrono.h
Normal file
32
src/configutils_espchrono.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
// local includes
|
||||
#include "configutils_priv_enum.h"
|
||||
#include "espchrono.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
IMPLEMENT_NVS_GET_SET_ENUM(espchrono::DayLightSavingMode)
|
||||
|
||||
#define IMPLEMENT_NVS_GET_SET_CHRONO(Name) \
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, Name* out_value) \
|
||||
{ \
|
||||
Name::rep temp; \
|
||||
const auto result = nvs_get(handle, key, &temp); \
|
||||
if (result == ESP_OK && out_value) \
|
||||
*out_value = Name{temp}; \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, Name value) \
|
||||
{ \
|
||||
return nvs_set(handle, key, Name::rep(value.count())); \
|
||||
}
|
||||
|
||||
IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::milliseconds32)
|
||||
IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::seconds32)
|
||||
IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::minutes32)
|
||||
IMPLEMENT_NVS_GET_SET_CHRONO(espchrono::hours32)
|
||||
#undef IMPLEMENT_NVS_GET_SET_CHRONO
|
||||
|
||||
} // namespace espconfig
|
11
src/configutils_espcpputils.h
Normal file
11
src/configutils_espcpputils.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
// local includes
|
||||
#include "configutils_priv_enum.h"
|
||||
#include "taskutils.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
IMPLEMENT_NVS_GET_SET_ENUM(espcpputils::CoreAffinity)
|
||||
|
||||
} // namespace espconfig
|
28
src/configutils_espwifistack.h
Normal file
28
src/configutils_espwifistack.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// local includes
|
||||
#include "configutils_base.h"
|
||||
#include "espwifiutils.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, wifi_stack::ip_address_t* out_value)
|
||||
{
|
||||
wifi_stack::ip_address_t::value_t temp;
|
||||
const auto result = nvs_get(handle, key, &temp);
|
||||
if (result == ESP_OK)
|
||||
*out_value = wifi_stack::ip_address_t{temp};
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, wifi_stack::ip_address_t value)
|
||||
{
|
||||
return nvs_set(handle, key, value.value());
|
||||
}
|
||||
|
||||
} // namespace espconfig
|
83
src/configutils_optional.h
Normal file
83
src/configutils_optional.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// local includes
|
||||
#include "configutils_base.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
template<typename T>
|
||||
esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<T>* out_value)
|
||||
{
|
||||
size_t length;
|
||||
if (const esp_err_t result = nvs_get_blob(handle, key, nullptr, &length); result != ESP_OK)
|
||||
return result;
|
||||
|
||||
struct Pair { T value; bool valid; };
|
||||
union { Pair pair; char buf[sizeof(Pair)]; };
|
||||
|
||||
if (length != sizeof(Pair))
|
||||
{
|
||||
//ESP_LOGW(TAG, "invalid size of optional, expected=%u actual=&zd", sizeof(Pair), length);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (const esp_err_t result = nvs_get_blob(handle, key, buf, &length); result != ESP_OK)
|
||||
return result;
|
||||
|
||||
if (pair.valid)
|
||||
*out_value = pair.value;
|
||||
else
|
||||
*out_value = std::nullopt;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<int8_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<uint8_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<int16_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<uint16_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<int32_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<uint32_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<int64_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<uint64_t>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<float>* out_value);
|
||||
template esp_err_t nvs_get(nvs_handle handle, const char* key, std::optional<double>* out_value);
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<T> value)
|
||||
{
|
||||
struct Pair { T value; bool valid; };
|
||||
union { Pair pair; char buf[sizeof(Pair)]; };
|
||||
|
||||
if (value)
|
||||
{
|
||||
pair.valid = true;
|
||||
pair.value = *value;
|
||||
}
|
||||
else
|
||||
pair.valid = false;
|
||||
|
||||
return nvs_set_blob(handle, key, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<int8_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<uint8_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<int16_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<uint16_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<int32_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<uint32_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<int64_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<uint64_t> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<float> value);
|
||||
template esp_err_t nvs_set(nvs_handle handle, const char* key, std::optional<double> value);
|
||||
|
||||
} // namespace espconfig
|
25
src/configutils_priv_enum.h
Normal file
25
src/configutils_priv_enum.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <type_traits>
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// local includes
|
||||
#include "configutils_base.h"
|
||||
|
||||
#define IMPLEMENT_NVS_GET_SET_ENUM(Name) \
|
||||
inline esp_err_t nvs_get(nvs_handle handle, const char* key, Name* out_value) \
|
||||
{ \
|
||||
std::underlying_type_t<Name> temp; \
|
||||
const auto result = nvs_get(handle, key, &temp); \
|
||||
if (result == ESP_OK) \
|
||||
*out_value = Name(temp); \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
inline esp_err_t nvs_set(nvs_handle handle, const char* key, Name value) \
|
||||
{ \
|
||||
return nvs_set(handle, key, std::underlying_type_t<Name>(value)); \
|
||||
}
|
13
src/configutils_sntp.h
Normal file
13
src/configutils_sntp.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// esp-idf includes
|
||||
#include <esp_sntp.h>
|
||||
|
||||
// local includes
|
||||
#include "configutils_priv_enum.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
IMPLEMENT_NVS_GET_SET_ENUM(sntp_sync_mode_t)
|
||||
|
||||
} // namespace espconfig
|
70
src/configwrapper.h
Normal file
70
src/configwrapper.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
// local includes
|
||||
#include "configwrapperinterface.h"
|
||||
#include "cppmacros.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
using ConfigConstraintReturnType = ConfigStatusReturnType;
|
||||
|
||||
template<typename T>
|
||||
class ConfigWrapper final : public ConfigWrapperInterface
|
||||
{
|
||||
CPP_DISABLE_COPY_MOVE(ConfigWrapper)
|
||||
|
||||
using DefaultValueCallbackRef = T(&)();
|
||||
using DefaultValueCallbackPtr = T(*)();
|
||||
|
||||
public:
|
||||
using value_t = typename std::conditional<std::is_same<T, std::string>::value, const T &, T>::type;
|
||||
using ConstraintCallback = ConfigConstraintReturnType(*)(value_t);
|
||||
|
||||
ConfigWrapper(const T &defaultValue, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName);
|
||||
ConfigWrapper(T &&defaultValue, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName);
|
||||
ConfigWrapper(const ConfigWrapper<T> &factoryConfig, ConstraintCallback constraintCallback, const char *nvsName);
|
||||
ConfigWrapper(const DefaultValueCallbackRef &defaultCallback, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName);
|
||||
~ConfigWrapper() override;
|
||||
|
||||
ConfigStatusReturnType write(nvs_handle_t nvsHandle, value_t value);
|
||||
|
||||
const char *type() const override final;
|
||||
|
||||
std::string valueAsString() const override final;
|
||||
std::string defaultAsString() const override final;
|
||||
|
||||
T defaultValue() const;
|
||||
|
||||
ConfigStatusReturnType loadFromFlash(nvs_handle_t nvsHandle) override final;
|
||||
ConfigStatusReturnType reset(nvs_handle_t nvsHandle) override final;
|
||||
ConfigStatusReturnType forceReset(nvs_handle_t nvsHandle) override final;
|
||||
|
||||
ConfigConstraintReturnType checkValue(value_t value) const;
|
||||
|
||||
const T &value{m_value};
|
||||
|
||||
private:
|
||||
ConfigStatusReturnType writeToFlash(nvs_handle_t nvsHandle, value_t value);
|
||||
|
||||
T m_value;
|
||||
|
||||
const enum : uint8_t { DefaultByValue, DefaultByFactoryConfig, DefaultByCallback } m_defaultType;
|
||||
|
||||
union
|
||||
{
|
||||
const T m_defaultValue;
|
||||
const ConfigWrapper<T> * const m_factoryConfig;
|
||||
const DefaultValueCallbackPtr m_defaultCallback;
|
||||
};
|
||||
|
||||
const ConstraintCallback m_constraintCallback;
|
||||
};
|
||||
|
||||
} // namespace espconfig
|
21
src/configwrapper_base.cpp
Normal file
21
src/configwrapper_base.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "configutils_base.h"
|
||||
#include "strutils.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::cpputils::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(bool)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(uint8_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(int8_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(uint16_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(int16_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(uint32_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(int32_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(uint64_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(int64_t)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(float)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(double)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::string)
|
||||
|
||||
} // namespace espconfig
|
8
src/configwrapper_cpputils.cpp
Normal file
8
src/configwrapper_cpputils.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "configutils_cpputils.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::cpputils::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
using ColorHelper = cpputils::ColorHelper;
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(ColorHelper)
|
||||
} // namespace espconfig
|
19
src/configwrapper_espchrono.cpp
Normal file
19
src/configwrapper_espchrono.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "configutils_espchrono.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::espchrono::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
using DayLightSavingMode = espchrono::DayLightSavingMode;
|
||||
using milliseconds32 = espchrono::milliseconds32;
|
||||
using seconds32 = espchrono::seconds32;
|
||||
using minutes32 = espchrono::minutes32;
|
||||
using hours32 = espchrono::hours32;
|
||||
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(espchrono::DayLightSavingMode)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(espchrono::milliseconds32)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(espchrono::seconds32)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(espchrono::minutes32)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(espchrono::hours32)
|
||||
|
||||
} // namespace espconfig
|
8
src/configwrapper_espcpputils.cpp
Normal file
8
src/configwrapper_espcpputils.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "configutils_espcpputils.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::espcpputils::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
using CoreAffinity = espcpputils::CoreAffinity;
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(CoreAffinity)
|
||||
} // namespace espconfig
|
8
src/configwrapper_espwifistack.cpp
Normal file
8
src/configwrapper_espwifistack.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "configutils_espwifistack.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::wifi_stack::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
using ip_address_t = wifi_stack::ip_address_t;
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(ip_address_t)
|
||||
} // namespace espconfig
|
19
src/configwrapper_optional.cpp
Normal file
19
src/configwrapper_optional.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "configutils_optional.h"
|
||||
#include "strutils.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::cpputils::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<int8_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<uint8_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<int16_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<uint16_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<int32_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<uint32_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<int64_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<uint64_t>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<float>)
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(std::optional<double>)
|
||||
|
||||
} // namespace espconfig
|
248
src/configwrapper_priv.h
Normal file
248
src/configwrapper_priv.h
Normal file
@@ -0,0 +1,248 @@
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#define LOG_LOCAL_LEVEL CONFIG_LOG_LOCAL_LEVEL_CONFIG
|
||||
|
||||
// system includes
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
// esp-idf includes
|
||||
#include <esp_log.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <fmt/core.h>
|
||||
|
||||
// local includes
|
||||
#include "configwrapper.h"
|
||||
#include "cpputils.h"
|
||||
|
||||
#define INSTANTIATE_CONFIGWRAPPER_TEMPLATES(TYPE) \
|
||||
template ConfigWrapper<TYPE>::ConfigWrapper(const TYPE &defaultValue, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName); \
|
||||
template ConfigWrapper<TYPE>::ConfigWrapper(TYPE &&defaultValue, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName); \
|
||||
template ConfigWrapper<TYPE>::ConfigWrapper(const ConfigWrapper<TYPE> &factoryConfig, ConstraintCallback constraintCallback, const char *nvsName); \
|
||||
template ConfigWrapper<TYPE>::ConfigWrapper(DefaultValueCallbackRef &defaultCallback, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName); \
|
||||
template ConfigStatusReturnType ConfigWrapper<TYPE>::write(nvs_handle_t nvsHandle, value_t value); \
|
||||
template<> const char *ConfigWrapper<TYPE>::type() const { return #TYPE; } \
|
||||
template std::string ConfigWrapper<TYPE>::valueAsString() const; \
|
||||
template std::string ConfigWrapper<TYPE>::defaultAsString() const; \
|
||||
template TYPE ConfigWrapper<TYPE>::defaultValue() const; \
|
||||
template ConfigStatusReturnType ConfigWrapper<TYPE>::loadFromFlash(nvs_handle_t nvsHandle); \
|
||||
template ConfigStatusReturnType ConfigWrapper<TYPE>::reset(nvs_handle_t nvsHandle); \
|
||||
template ConfigStatusReturnType ConfigWrapper<TYPE>::forceReset(nvs_handle_t nvsHandle); \
|
||||
template ConfigConstraintReturnType ConfigWrapper<TYPE>::checkValue(value_t value) const; \
|
||||
template ConfigStatusReturnType ConfigWrapper<TYPE>::writeToFlash(nvs_handle_t nvsHandle, value_t value);
|
||||
|
||||
namespace espconfig {
|
||||
namespace {
|
||||
constexpr const char * const TAG = "CONFIG";
|
||||
} // namespace
|
||||
|
||||
template<typename T>
|
||||
ConfigWrapper<T>::ConfigWrapper(const T &defaultValue, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName) :
|
||||
ConfigWrapperInterface{allowReset, nvsName},
|
||||
m_defaultType{DefaultByValue},
|
||||
m_defaultValue{defaultValue},
|
||||
m_constraintCallback{constraintCallback}
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigWrapper<T>::ConfigWrapper(T &&defaultValue, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName) :
|
||||
ConfigWrapperInterface{allowReset, nvsName},
|
||||
m_defaultType{DefaultByValue},
|
||||
m_defaultValue{std::move(defaultValue)},
|
||||
m_constraintCallback{constraintCallback}
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigWrapper<T>::ConfigWrapper(const ConfigWrapper<T> &factoryConfig, ConstraintCallback constraintCallback, const char *nvsName) :
|
||||
ConfigWrapperInterface{AllowReset::DoReset, nvsName},
|
||||
m_defaultType{DefaultByFactoryConfig},
|
||||
m_factoryConfig{&factoryConfig},
|
||||
m_constraintCallback{constraintCallback}
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigWrapper<T>::ConfigWrapper(const DefaultValueCallbackRef &defaultCallback, AllowReset allowReset, ConstraintCallback constraintCallback, const char *nvsName) :
|
||||
ConfigWrapperInterface{allowReset, nvsName},
|
||||
m_defaultType{DefaultByCallback},
|
||||
m_defaultCallback{&defaultCallback},
|
||||
m_constraintCallback{constraintCallback}
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigWrapper<T>::~ConfigWrapper()
|
||||
{
|
||||
switch (m_defaultType)
|
||||
{
|
||||
case DefaultByValue: m_defaultValue.~T(); break;
|
||||
case DefaultByFactoryConfig: /*m_factoryConfig.~typeof(m_factoryConfig)();*/ break;
|
||||
case DefaultByCallback: /*m_defaultCallback.~typeof(m_defaultCallback)();*/ break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigStatusReturnType ConfigWrapper<T>::write(nvs_handle_t nvsHandle, value_t value)
|
||||
{
|
||||
CONFIGWRAPPER_TOSTRING_USINGS
|
||||
|
||||
ESP_LOGD(TAG, "%s %s", m_nvsName, toString(value).c_str());
|
||||
|
||||
if (!m_loaded)
|
||||
ESP_LOGE(TAG, "%s has not been loaded yet!", m_nvsName);
|
||||
|
||||
if (const auto result = checkValue(value); !result)
|
||||
{
|
||||
ESP_LOGW(TAG, "%s cannot be set to %s: constraint not met: %s", m_nvsName, toString(value).c_str(), result.error().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
if (m_value == value)
|
||||
return {};
|
||||
|
||||
return writeToFlash(nvsHandle, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string ConfigWrapper<T>::valueAsString() const
|
||||
{
|
||||
CONFIGWRAPPER_TOSTRING_USINGS
|
||||
|
||||
if (m_touched)
|
||||
return toString(m_value);
|
||||
else
|
||||
return "--not-touched-- " + toString(m_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string ConfigWrapper<T>::defaultAsString() const
|
||||
{
|
||||
CONFIGWRAPPER_TOSTRING_USINGS
|
||||
|
||||
return toString(defaultValue());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ConfigWrapper<T>::defaultValue() const
|
||||
{
|
||||
switch (m_defaultType)
|
||||
{
|
||||
case DefaultByValue: return m_defaultValue;
|
||||
case DefaultByFactoryConfig: assert(m_factoryConfig->m_loaded); return m_factoryConfig->value;
|
||||
case DefaultByCallback: return m_defaultCallback();
|
||||
}
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigStatusReturnType ConfigWrapper<T>::loadFromFlash(nvs_handle_t nvsHandle)
|
||||
{
|
||||
//ESP_LOGD(TAG, "%s", m_nvsName);
|
||||
|
||||
const auto result = nvs_get(nvsHandle, m_nvsName, &m_value);
|
||||
|
||||
ESP_LOG_LEVEL_LOCAL((cpputils::is_in(result, ESP_OK, ESP_ERR_NVS_NOT_FOUND) ? ESP_LOG_DEBUG : ESP_LOG_ERROR), TAG, "%s nvs_get() returned: %s", m_nvsName, esp_err_to_name(result));
|
||||
|
||||
if (result == ESP_OK)
|
||||
{
|
||||
if (const auto result = checkValue(m_value); !result)
|
||||
{
|
||||
ESP_LOGE(TAG, "%s constraint not met for value in flash: %s", m_nvsName, result.error().c_str());
|
||||
return forceReset(nvsHandle);
|
||||
}
|
||||
|
||||
m_loaded = true;
|
||||
m_touched = true;
|
||||
return {};
|
||||
}
|
||||
else if (result == ESP_ERR_NVS_NOT_FOUND)
|
||||
{
|
||||
m_loaded = true;
|
||||
m_touched = false;
|
||||
m_value = defaultValue();
|
||||
if (const auto result = checkValue(m_value); !result)
|
||||
ESP_LOGE(TAG, "%s constraint not met for value from default: %s", m_nvsName, result.error().c_str());
|
||||
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return forceReset(nvsHandle);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigStatusReturnType ConfigWrapper<T>::reset(nvs_handle_t nvsHandle)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", m_nvsName);
|
||||
|
||||
if (!m_allowReset)
|
||||
return {};
|
||||
|
||||
return forceReset(nvsHandle);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigStatusReturnType ConfigWrapper<T>::forceReset(nvs_handle_t nvsHandle)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", m_nvsName);
|
||||
|
||||
auto result = nvs_erase_key(nvsHandle, m_nvsName);
|
||||
|
||||
ESP_LOG_LEVEL_LOCAL((cpputils::is_in(result, ESP_OK, ESP_ERR_NVS_NOT_FOUND) ? ESP_LOG_DEBUG : ESP_LOG_ERROR), TAG, "%s nvs_erase_key() returned: %s", m_nvsName, esp_err_to_name(result));
|
||||
|
||||
if (result == ESP_ERR_NVS_NOT_FOUND)
|
||||
{
|
||||
if (m_touched)
|
||||
ESP_LOGE(TAG, "%s for touched and not found?!", m_nvsName);
|
||||
result = ESP_OK;
|
||||
}
|
||||
|
||||
if (result == ESP_OK || !m_loaded)
|
||||
{
|
||||
if (!m_loaded /* && result == ESP_OK */)
|
||||
m_loaded = true;
|
||||
m_touched = false;
|
||||
m_value = defaultValue();
|
||||
}
|
||||
|
||||
if (result != ESP_OK)
|
||||
return tl::make_unexpected(std::string{"nvs_erase_key() failed with "} + esp_err_to_name(result));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigConstraintReturnType ConfigWrapper<T>::checkValue(value_t value) const
|
||||
{
|
||||
if (!m_constraintCallback)
|
||||
return {};
|
||||
|
||||
return m_constraintCallback(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConfigStatusReturnType ConfigWrapper<T>::writeToFlash(nvs_handle_t nvsHandle, value_t value)
|
||||
{
|
||||
CONFIGWRAPPER_TOSTRING_USINGS
|
||||
|
||||
ESP_LOGD(TAG, "%s %s", m_nvsName, toString(value).c_str());
|
||||
|
||||
const auto result = nvs_set(nvsHandle, m_nvsName, value);
|
||||
|
||||
ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "%s %s nvs_set() returned %s", m_nvsName, toString(value).c_str(), esp_err_to_name(result));
|
||||
|
||||
m_value = value;
|
||||
m_touched = true;
|
||||
|
||||
if (result != ESP_OK)
|
||||
return tl::make_unexpected(std::string{"nvs_set() failed with "} + esp_err_to_name(result));
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace espconfig
|
8
src/configwrapper_sntp.cpp
Normal file
8
src/configwrapper_sntp.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "configutils_sntp.h"
|
||||
#include "espstrutils.h"
|
||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::espcpputils::toString;
|
||||
#include "configwrapper_priv.h"
|
||||
|
||||
namespace espconfig {
|
||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(sntp_sync_mode_t)
|
||||
} // namespace espconfig
|
31
src/configwrapperinterface.cpp
Normal file
31
src/configwrapperinterface.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "configwrapperinterface.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#define LOG_LOCAL_LEVEL CONFIG_LOG_LOCAL_LEVEL_CONFIG
|
||||
|
||||
// system includes
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
// esp-idf includes
|
||||
#include <esp_log.h>
|
||||
#include <nvs.h>
|
||||
|
||||
namespace espconfig {
|
||||
namespace {
|
||||
constexpr const char * const TAG = "CONFIG";
|
||||
} // namespace
|
||||
|
||||
ConfigWrapperInterface::ConfigWrapperInterface(AllowReset allowReset, const char *nvsName) :
|
||||
m_allowReset{allowReset == AllowReset::DoReset},
|
||||
m_nvsName{nvsName}
|
||||
{
|
||||
{
|
||||
const auto length = std::strlen(nvsName);
|
||||
if (length >= NVS_KEY_NAME_MAX_SIZE)
|
||||
ESP_LOGE(TAG, "invalid nvs key %s (too long %zd)", nvsName, length);
|
||||
assert(length < NVS_KEY_NAME_MAX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace espconfig
|
51
src/configwrapperinterface.h
Normal file
51
src/configwrapperinterface.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string>
|
||||
|
||||
// esp-idf includes
|
||||
#include <nvs.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
// local includes
|
||||
#include "cppmacros.h"
|
||||
|
||||
namespace espconfig {
|
||||
enum class AllowReset { NoReset, DoReset };
|
||||
constexpr auto NoReset = AllowReset::NoReset;
|
||||
constexpr auto DoReset = AllowReset::DoReset;
|
||||
|
||||
using ConfigStatusReturnType = tl::expected<void, std::string>;
|
||||
|
||||
class ConfigWrapperInterface
|
||||
{
|
||||
CPP_DISABLE_COPY_MOVE(ConfigWrapperInterface)
|
||||
|
||||
public:
|
||||
ConfigWrapperInterface(AllowReset allowReset, const char *nvsName);
|
||||
virtual ~ConfigWrapperInterface() = default;
|
||||
|
||||
virtual const char *type() const = 0;
|
||||
|
||||
virtual std::string valueAsString() const = 0;
|
||||
virtual std::string defaultAsString() const = 0;
|
||||
|
||||
virtual ConfigStatusReturnType loadFromFlash(nvs_handle_t nvsHandle) = 0;
|
||||
virtual ConfigStatusReturnType reset(nvs_handle_t nvsHandle) = 0;
|
||||
virtual ConfigStatusReturnType forceReset(nvs_handle_t nvsHandle) = 0;
|
||||
|
||||
bool allowReset() const { return m_allowReset; }
|
||||
const char *nvsName() const { return m_nvsName; }
|
||||
bool touched() const { return m_touched; }
|
||||
bool loaded() const { return m_loaded; }
|
||||
|
||||
protected:
|
||||
const bool m_allowReset;
|
||||
const char * const m_nvsName;
|
||||
bool m_touched{};
|
||||
bool m_loaded{};
|
||||
};
|
||||
|
||||
} // namespace espconfig
|
Reference in New Issue
Block a user