NVS (Non-Volatile Storage) в ESP32 - это энергонезависимое хранилище данных, которое хранит пары ключ-значение во FLASH-памяти. Его удобно задействовать для сохранения конфигурации.
Заумь об организации памяти в ESP32
Флешка в ESP32 разделена на несколько разделов:
Минимальный размер каждого раздела - 4 Кб. Начальная область выделена под загрузчик, далее располагается NVS, данные для инициализации PHY, а так же сама прошивка. В более сложных варинтах под прошивку может быть выделено несколько разделов (например, если используется OTA).
NVS поддреживает различные типы данных:
- целочисленные значения (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t);
- ASCIIZ-строки (признак окончания строки - NUL-символ);
- blob (массивы двоичных данных).
Однако, для хранения больших массивов данных она не очень подходит. В таком случае лучше использовать отдельную файловую систему с выравниваем износа секторов.
Для избежания конфликтов в именах ключей, NVS использует пространсва имен (namespace). С помощью пространст имен удобно логически группировать данные. Максимальная длина имен пространств составляет 15 символов (кстати, как и для ключей). Для использования конкретного пространсва имен используются функции nvs_open()
и nvs_open_from_partition()
.
Рассмотрим работу с NVS на примере. В первую очередь необходимо инициализировать хранилище:
esp_err_t nvs_utils_init() {
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
return err;
}
После инициализации, мы можем выполнять чтение, запись и очистку хранилища:
esp_err_t nvs_utils_get_str(const char* namespace_name, const char* name, char* val) { nvs_handle_t nvs; esp_err_t err = nvs_open(namespace_name, NVS_READONLY, &nvs); if (err == ESP_OK) { size_t len; if ((err = nvs_get_str(nvs, name, val, &len)) != ESP_OK) { return err; } if (val == NULL) strcpy(val, ""); nvs_close(nvs); } else { return err; } return ESP_OK; } esp_err_t nvs_utils_set_str(const char* namespace_name, const char* name, const char* val) { nvs_handle_t nvs; esp_err_t err = nvs_open(namespace_name, NVS_READWRITE, &nvs); if (err != ESP_OK) { return err; } err = nvs_set_str(nvs, name, val); if (err != ESP_OK) { nvs_close(nvs); return err; } err = nvs_commit(nvs); nvs_close(nvs); return err; } esp_err_t nvs_utils_get_int(const char* namespace_name, const char* name, int32_t* val) { nvs_handle_t nvs; esp_err_t err = nvs_open(namespace_name, NVS_READONLY, &nvs); if (err == ESP_OK) { if ((err = nvs_get_i32(nvs, name, val)) != ESP_OK) { return err; } nvs_close(nvs); } else { return err; } return ESP_OK; } esp_err_t nvs_utils_set_int(const char* namespace_name, const char* name, const int32_t val) { nvs_handle_t nvs; esp_err_t err = nvs_open(namespace_name, NVS_READWRITE, &nvs); if (err != ESP_OK) { return err; } err = nvs_set_i32(nvs, name, val); if (err != ESP_OK) { nvs_close(nvs); return err; } err = nvs_commit(nvs); nvs_close(nvs); return err; } esp_err_t nvs_utils_erase_by_key(const char* namespace_name, const char *key) { nvs_handle_t nvs; esp_err_t err = nvs_open(namespace_name, NVS_READWRITE, &nvs); if (err == ESP_OK) { err = nvs_erase_key(nvs, key); if (err == ESP_OK) { err = nvs_commit(nvs); } nvs_close(nvs); } return err; } esp_err_t nvs_utils_erase_all(const char* namespace_name) { nvs_handle_t nvs; esp_err_t err = nvs_open(namespace_name, NVS_READWRITE, &nvs); if (err == ESP_OK) { err = nvs_erase_all(nvs); if (err == ESP_OK) { err = nvs_commit(nvs); } } nvs_close(nvs); return ESP_OK; }
Проверяем в работе:
extern "C" void app_main(void) {
char buffer[64];
int32_t counter;
auto err = nvs_utils_init();
ESP_ERROR_CHECK(err);
err = nvs_utils_get_str("storage", "label", buffer);
if (err != ESP_OK) printf("Error read value: %s. Error code: %d\n" , "label", err);
auto label = std::string(buffer);
if (label != "Restart counter: ") label = "Restart counter: ";
err = nvs_utils_get_int("storage", "counter", &counter);
if (err != ESP_OK) printf("Error read value: %s. Error code: %d\n" , "counter", err);
printf("%s %d\n", buffer, (int)counter);
if (counter > 1000) counter = 0;
else counter++;
err = nvs_utils_set_str("storage", "label", label.c_str());
if (err != ESP_OK) printf("Error write value: %s. Error code: %d\n" , "label", err);
err = nvs_utils_set_int("storage", "counter", counter);
if (err != ESP_OK) printf("Error write value: %s. Error code: %d\n" , "counter", err);
for (int i = 15; i >= 0; i--) {
printf("Перезагрузка через %d секунд...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}