#include "settingspersister.h" // system includes #include // esp-idf includes #include #include #include #include // 3rdparty lib includes #include #include #include // local includes #include "profilesettings.h" #ifdef FEATURE_BLUETOOTH #include "bluetoothmode.h" #endif #include "unifiedmodelmode.h" #include "globals.h" bool SettingsPersister::init() { if (esp_err_t result = nvs_flash_init(); cpputils::is_in(result, ESP_ERR_NVS_NO_FREE_PAGES, ESP_ERR_NVS_NEW_VERSION_FOUND)) { ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s, trying to erase...", esp_err_to_name(result)); return erase(); } else if (result != ESP_OK) { ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result)); return false; } return true; } bool SettingsPersister::erase() { closeProfile(); bool result{true}; if (esp_err_t result = nvs_flash_erase(); result != ESP_OK) { ESP_LOGE("BOBBY", "nvs_flash_erase() failed with %s", esp_err_to_name(result)); result = false; } if (esp_err_t result = nvs_flash_init(); result != ESP_OK) { ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result)); result = false; } return result; } bool SettingsPersister::openProfile(uint8_t index) { closeProfile(); nvs_handle handle; const auto name = fmt::format("bobbycar{}", index); if (esp_err_t result = nvs_open(name.c_str(), NVS_READWRITE, &handle); result != ESP_OK) { ESP_LOGE("BOBBY", "nvs_open() PROFILE %s failed with %s", name.c_str(), esp_err_to_name(result)); return false; } m_profile = {handle, index}; return true; } void SettingsPersister::closeProfile() { if (!m_profile) return; nvs_close(m_profile->handle); m_profile = std::nullopt; } template struct nvsGetterHelper; template<> struct nvsGetterHelper { static constexpr auto nvs_get = &nvs_get_i8; }; template<> struct nvsGetterHelper { static constexpr auto nvs_get = &nvs_get_u8; }; template<> struct nvsGetterHelper { static constexpr auto nvs_get = &nvs_get_i16; }; template<> struct nvsGetterHelper { static constexpr auto nvs_get = &nvs_get_u16; }; template<> struct nvsGetterHelper { static constexpr auto nvs_get = &nvs_get_i32; }; template<> struct nvsGetterHelper { static constexpr auto nvs_get = &nvs_get_u32; }; template<> struct nvsGetterHelper { static 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; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = tempValue; return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlType* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = bobbycar::protocol::ControlType(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = bobbycar::protocol::ControlMode(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, LarsmModeMode* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = LarsmModeMode(tempValue); return err; }}; #ifdef FEATURE_BLUETOOTH template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, BluetoothMode* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = BluetoothMode(tempValue); return err; }}; #endif template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, UnifiedModelMode* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = UnifiedModelMode(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, wifi_mode_t* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = wifi_mode_t(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::DayLightSavingMode* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = espchrono::DayLightSavingMode(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::milliseconds32* out_value) { int32_t tempValue; esp_err_t err = nvs_get_i32(handle, key, &tempValue); if (err == ESP_OK) *out_value = espchrono::milliseconds32(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::seconds32* out_value) { int32_t tempValue; esp_err_t err = nvs_get_i32(handle, key, &tempValue); if (err == ESP_OK) *out_value = espchrono::seconds32(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::minutes32* out_value) { int32_t tempValue; esp_err_t err = nvs_get_i32(handle, key, &tempValue); if (err == ESP_OK) *out_value = espchrono::minutes32(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::hours32* out_value) { int32_t tempValue; esp_err_t err = nvs_get_i32(handle, key, &tempValue); if (err == ESP_OK) *out_value = espchrono::hours32(tempValue); return err; }}; template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, sntp_sync_mode_t* out_value) { uint8_t tempValue; esp_err_t err = nvs_get_u8(handle, key, &tempValue); if (err == ESP_OK) *out_value = sntp_sync_mode_t(tempValue); return err; }}; template<> struct nvsGetterHelper> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array* out_value) { uint32_t tempValue; esp_err_t err = nvs_get_u32(handle, key, &tempValue); if (err == ESP_OK) *out_value = std::bit_cast>(tempValue); return err; }}; template<> struct nvsGetterHelper> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array* out_value) { uint32_t tempValue; esp_err_t err = nvs_get_u32(handle, key, &tempValue); if (err == ESP_OK) *out_value = std::bit_cast>(tempValue); return err; }}; template bool SettingsPersister::load(T &settings) { bool result{true}; if (m_profile) { profileSettings.executeForEveryProfileSetting([&](const char *key, auto &value) { if (esp_err_t result = nvsGetterHelper>::nvs_get(m_profile->handle, key, &value); result != ESP_OK) { if (result != ESP_ERR_NVS_NOT_FOUND) ESP_LOGE("BOBBY", "nvs_get() PROFILE %s failed with %s", key, esp_err_to_name(result)); result = false; } }); } else { ESP_LOGW("BOBBY", "no profile open currently!"); result = false; } return result; } template bool SettingsPersister::load(ProfileSettings &profileSettings); template struct nvsSetterHelper; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_i8; }; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_u8; }; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_i16; }; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_u16; }; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_i32; }; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_u32; }; template<> struct nvsSetterHelper { static constexpr auto nvs_set = &nvs_set_u8; }; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value) { return nvs_set_str(handle, key, value.c_str()); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlType value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, LarsmModeMode value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; #ifdef FEATURE_BLUETOOTH template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, BluetoothMode value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; #endif template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, UnifiedModelMode value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, wifi_mode_t value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::DayLightSavingMode value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::milliseconds32 value) { return nvs_set_i32(handle, key, value.count()); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::seconds32 value) { return nvs_set_i32(handle, key, value.count()); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::minutes32 value) { return nvs_set_i32(handle, key, value.count()); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::hours32 value) { return nvs_set_i32(handle, key, value.count()); }}; template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, sntp_sync_mode_t value) { return nvs_set_u8(handle, key, uint8_t(value)); }}; template<> struct nvsSetterHelper> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array value) { return nvs_set_u32(handle, key, std::bit_cast(value)); }}; template<> struct nvsSetterHelper> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array value) { return nvs_set_u32(handle, key, std::bit_cast(value)); }}; template bool SettingsPersister::save(T &settings) { bool result{true}; if (m_profile) { profileSettings.executeForEveryProfileSetting([&](const char *key, const auto &value) { if (esp_err_t result = nvsSetterHelper>::nvs_set(m_profile->handle, key, value); result != ESP_OK) { ESP_LOGE("BOBBY", "nvs_set() PROFILE %s failed with %s", key, esp_err_to_name(result)); result = false; } }); } else { ESP_LOGW("BOBBY", "no profile open currently!"); result = false; } return result; } template bool SettingsPersister::save(ProfileSettings &settings); std::optional SettingsPersister::currentlyOpenProfileIndex() const { if (m_profile) return m_profile->profileIndex; return std::nullopt; }