9#include <soc/soc_caps.h>
15static const char *
const TAG =
"preferences";
19 SmallInlineBuffer<8> data;
22static std::vector<NVSData> s_pending_save;
39#ifdef USE_ESP32_RTC_PREFERENCES_STORAGE
40static constexpr size_t RTC_PREF_SIZE_WORDS = 64;
41static constexpr size_t RTC_PREF_MAX_WORDS = 255;
44static RTC_NOINIT_ATTR
uint32_t s_rtc_storage[RTC_PREF_SIZE_WORDS];
46static bool save_to_rtc(uint16_t offset,
uint32_t key, uint8_t length_words,
const uint8_t *data,
size_t len) {
49 const size_t buffer_size =
static_cast<size_t>(length_words) + 1;
50 if (
static_cast<size_t>(offset) + buffer_size > RTC_PREF_SIZE_WORDS)
56static bool load_from_rtc(uint16_t offset,
uint32_t key, uint8_t length_words, uint8_t *data,
size_t len) {
59 const size_t buffer_size =
static_cast<size_t>(length_words) + 1;
60 if (
static_cast<size_t>(offset) + buffer_size > RTC_PREF_SIZE_WORDS)
70static esp_err_t s_open_err = ESP_OK;
73#ifdef USE_ESP32_RTC_PREFERENCES_STORAGE
75 return save_to_rtc(this->
rtc_offset, this->key, this->length_words, data,
len);
78 for (
auto &obj : s_pending_save) {
79 if (obj.key == this->key) {
80 obj.data.set(data,
len);
87 s_pending_save.push_back(std::move(
save));
88 ESP_LOGVV(TAG,
"s_pending_save: key: %" PRIu32
", len: %zu", this->key,
len);
93#ifdef USE_ESP32_RTC_PREFERENCES_STORAGE
95 return load_from_rtc(this->
rtc_offset, this->key, this->length_words, data,
len);
98 for (
auto &obj : s_pending_save) {
99 if (obj.key == this->key) {
100 if (obj.data.size() !=
len) {
104 memcpy(data, obj.data.data(),
len);
109 char key_str[UINT32_MAX_STR_SIZE];
112 esp_err_t err = nvs_get_blob(this->
nvs_handle, key_str,
nullptr, &actual_len);
114 ESP_LOGV(TAG,
"nvs_get_blob('%s'): %s - the key might not be set yet", key_str, esp_err_to_name(err));
117 if (actual_len !=
len) {
118 ESP_LOGVV(TAG,
"NVS length does not match (%zu!=%zu)", actual_len,
len);
123 ESP_LOGV(TAG,
"nvs_get_blob('%s') failed: %s", key_str, esp_err_to_name(err));
126 ESP_LOGVV(TAG,
"nvs_get_blob: key: %s, len: %zu", key_str,
len);
135 esp_err_t err = nvs_open(
"esphome", NVS_READWRITE, &this->
nvs_handle);
144 err = nvs_open(
"esphome", NVS_READWRITE, &this->
nvs_handle);
151#ifdef USE_ESP32_RTC_PREFERENCES_STORAGE
159 static bool warned =
false;
161 ESP_LOGW(TAG,
"RTC preference storage not compiled in; using NVS (enable with 'preferences: rtc_storage: true')");
171 if (s_open_err != ESP_OK) {
173 ESP_LOGW(TAG,
"nvs_open failed: %s - NVS unavailable", esp_err_to_name(s_open_err));
175 ESP_LOGW(TAG,
"nvs_open failed: %s - erased NVS", esp_err_to_name(s_open_err));
182 pref->in_flash =
true;
187#ifdef USE_ESP32_RTC_PREFERENCES_STORAGE
190 if (length_words > RTC_PREF_MAX_WORDS) {
191 ESP_LOGE(TAG,
"RTC preference too large: %" PRIu32
" words", length_words);
194 const uint32_t total_words = length_words + 1;
196 ESP_LOGE(TAG,
"RTC preference storage full, cannot allocate %" PRIu32
" words", total_words);
201 pref->in_flash =
false;
203 pref->length_words =
static_cast<uint8_t
>(length_words);
211 if (s_pending_save.empty())
214 ESP_LOGV(TAG,
"Saving %zu items...", s_pending_save.size());
215 int cached = 0,
written = 0, failed = 0;
216 esp_err_t last_err = ESP_OK;
219 for (
const auto &save : s_pending_save) {
220 char key_str[UINT32_MAX_STR_SIZE];
222 ESP_LOGVV(TAG,
"Checking if NVS data %s has changed", key_str);
224 esp_err_t err = nvs_set_blob(this->
nvs_handle, key_str, save.data.data(), save.data.size());
225 ESP_LOGV(TAG,
"sync: key: %s, len: %zu", key_str, save.data.size());
227 ESP_LOGV(TAG,
"nvs_set_blob('%s', len=%zu) failed: %s", key_str, save.data.size(), esp_err_to_name(err));
235 ESP_LOGV(TAG,
"NVS data not changed skipping %" PRIu32
" len=%zu", save.key, save.data.size());
239 s_pending_save.clear();
242 ESP_LOGE(TAG,
"Writing %d items: %d cached, %d written, %d failed. Last error=%s for key=%" PRIu32,
243 cached +
written + failed, cached,
written, failed, esp_err_to_name(last_err), last_key);
245 ESP_LOGD(TAG,
"Writing %d items: %d cached, %d written, %d failed", cached +
written + failed, cached,
written,
248 ESP_LOGV(TAG,
"Writing %d items: %d cached, %d written, %d failed", cached +
written + failed, cached,
written,
255 ESP_LOGV(TAG,
"nvs_commit() failed: %s", esp_err_to_name(err));
264 esp_err_t err = nvs_get_blob(
nvs_handle, key_str,
nullptr, &actual_len);
266 ESP_LOGV(TAG,
"nvs_get_blob('%s'): %s - the key might not be set yet", key_str, esp_err_to_name(err));
270 if (actual_len != to_save.data.size()) {
275 err = nvs_get_blob(
nvs_handle, key_str, stored_data.
get(), &actual_len);
277 ESP_LOGV(TAG,
"nvs_get_blob('%s') failed: %s", key_str, esp_err_to_name(err));
280 return memcmp(to_save.data.data(), stored_data.
get(), to_save.data.size()) != 0;
284 ESP_LOGD(TAG,
"Erasing storage");
285 s_pending_save.clear();
286#ifdef USE_ESP32_RTC_PREFERENCES_STORAGE
290 memset(s_rtc_storage, 0,
sizeof(s_rtc_storage));
305 s_preferences.
open();
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
bool load(uint8_t *data, size_t len)
bool save(const uint8_t *data, size_t len)
bool is_changed_(uint32_t nvs_handle, const NVSData &to_save, const char *key_str)
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)
ESPPreferenceObject make_rtc_preference_(size_t length, uint32_t type)
uint16_t current_rtc_offset_
ESP32Preferences * get_preferences()
size_t rtc_pref_bytes_to_words(size_t bytes)
Round a byte count up to whole 32-bit words.
bool rtc_pref_decode(const uint32_t *buffer, uint32_t type, uint8_t length_words, uint8_t *data, size_t len)
Verify the checksum of a record held in buffer (length_words data words + 1 checksum word) and,...
size_t uint32_to_str(std::span< char, UINT32_MAX_STR_SIZE > buf, uint32_t val)
Write unsigned 32-bit integer to buffer with compile-time size check.
ESPPreferences * global_preferences
void rtc_pref_encode(uint32_t *buffer, uint32_t type, uint8_t length_words, const uint8_t *data, size_t len)
Encode len data bytes into buffer (length_words data words + 1 trailing checksum word).