16static const char *
const TAG =
"esp32.preferences";
20 std::unique_ptr<uint8_t[]> data;
23 void set_data(
const uint8_t *src,
size_t size) {
24 data = std::make_unique<uint8_t[]>(size);
25 memcpy(data.get(), src, size);
30static std::vector<NVSData> s_pending_save;
32class ESP32PreferenceBackend :
public ESPPreferenceBackend {
36 bool save(
const uint8_t *data,
size_t len)
override {
38 for (
auto &obj : s_pending_save) {
40 obj.set_data(data,
len);
46 save.set_data(data,
len);
47 s_pending_save.emplace_back(std::move(save));
48 ESP_LOGVV(TAG,
"s_pending_save: key: %s, len: %zu", key.c_str(),
len);
51 bool load(uint8_t *data,
size_t len)
override {
53 for (
auto &obj : s_pending_save) {
59 memcpy(data, obj.data.get(),
len);
65 esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(),
nullptr, &actual_len);
67 ESP_LOGV(TAG,
"nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err));
70 if (actual_len !=
len) {
71 ESP_LOGVV(TAG,
"NVS length does not match (%zu!=%zu)", actual_len,
len);
74 err = nvs_get_blob(nvs_handle, key.c_str(), data, &
len);
76 ESP_LOGV(TAG,
"nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err));
79 ESP_LOGVV(TAG,
"nvs_get_blob: key: %s, len: %zu", key.c_str(),
len);
85class ESP32Preferences :
public ESPPreferences {
91 esp_err_t err = nvs_open(
"esphome", NVS_READWRITE, &nvs_handle);
95 ESP_LOGW(TAG,
"nvs_open failed: %s - erasing NVS", esp_err_to_name(err));
100 err = nvs_open(
"esphome", NVS_READWRITE, &nvs_handle);
105 ESPPreferenceObject make_preference(
size_t length, uint32_t
type,
bool in_flash)
override {
108 ESPPreferenceObject make_preference(
size_t length, uint32_t
type)
override {
109 auto *pref =
new ESP32PreferenceBackend();
110 pref->nvs_handle = nvs_handle;
115 return ESPPreferenceObject(pref);
118 bool sync()
override {
119 if (s_pending_save.empty())
122 ESP_LOGV(TAG,
"Saving %zu items...", s_pending_save.size());
124 int cached = 0, written = 0, failed = 0;
125 esp_err_t last_err = ESP_OK;
126 std::string last_key{};
129 for (
ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
130 const auto &save = s_pending_save[i];
131 ESP_LOGVV(TAG,
"Checking if NVS data %s has changed", save.key.c_str());
132 if (is_changed(nvs_handle, save)) {
133 esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.get(), save.len);
134 ESP_LOGV(TAG,
"sync: key: %s, len: %zu", save.key.c_str(), save.len);
136 ESP_LOGV(TAG,
"nvs_set_blob('%s', len=%zu) failed: %s", save.key.c_str(), save.len, esp_err_to_name(err));
144 ESP_LOGV(TAG,
"NVS data not changed skipping %s len=%zu", save.key.c_str(), save.len);
147 s_pending_save.erase(s_pending_save.begin() + i);
149 ESP_LOGD(TAG,
"Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
152 ESP_LOGE(TAG,
"Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err),
157 esp_err_t err = nvs_commit(nvs_handle);
159 ESP_LOGV(TAG,
"nvs_commit() failed: %s", esp_err_to_name(err));
165 bool is_changed(
const uint32_t nvs_handle,
const NVSData &to_save) {
167 esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(),
nullptr, &actual_len);
169 ESP_LOGV(TAG,
"nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key.c_str(), esp_err_to_name(err));
173 if (actual_len != to_save.len) {
176 auto stored_data = std::make_unique<uint8_t[]>(actual_len);
177 err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.get(), &actual_len);
179 ESP_LOGV(TAG,
"nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err));
182 return memcmp(to_save.data.get(), stored_data.get(), to_save.len) != 0;
185 bool reset()
override {
186 ESP_LOGD(TAG,
"Erasing storage");
187 s_pending_save.clear();
198 auto *prefs =
new ESP32Preferences();
Providing packet encoding functions for exchanging data with a remote host.
ESPPreferences * global_preferences
std::string str_sprintf(const char *fmt,...)