ESP32: Сохранение данных в постоянную память с помощью библиотеки Preferences
В этом руководстве показано, как сохранять данные в постоянную флеш-память ESP32 с помощью библиотеки Preferences.h. Данные, хранящиеся во флеш-памяти, сохраняются при перезагрузке или отключении питания. Использование библиотеки Preferences.h удобно для сохранения таких данных, как сетевые учетные данные, API-ключи, пороговые значения или даже последнее состояние GPIO. Вы узнаете, как сохранять и считывать данные из флеш-памяти.
В этом руководстве мы рассмотрим следующие темы:
Пример 2: ESP32 – Сохранение/Чтение сетевых учетных данных с помощью библиотеки Preferences.h;
Пример 3: ESP32 – Запоминание последнего состояния GPIO после сброса;
Библиотека Preferences.h
В предыдущем руководстве мы рекомендовали использовать библиотеку EEPROM для сохранения данных во флеш-памяти. Однако библиотека EEPROM устарела в пользу библиотеки Preferences.h. Эта библиотека устанавливается автоматически при установке плат ESP32 в Arduino IDE.
Библиотека Preferences.h предпочтительно используется для хранения значений переменных через пары ключ:значение. Постоянное сохранение данных может быть важно для:
запоминания последнего состояния переменной;
сохранения настроек;
подсчета количества включений устройства;
или любого другого типа данных, которые нужно сохранять постоянно.
Если вместо переменных вам нужно сохранять файлы на ESP32, мы рекомендуем использовать файловую систему (SPIFFS). Чтобы узнать, как сохранять файлы в файловой системе ESP32, прочитайте одно из следующих руководств:
Сохранение данных с помощью библиотеки Preferences.h
Данные, сохраненные с помощью Preferences, структурированы следующим образом:
namespace {
key:value
}
Вы можете сохранить несколько ключей в одном пространстве имен, например:
namespace {
key1: value1
key2: value2
}
В практическом примере эта конфигурация может использоваться для сохранения ваших сетевых учетных данных:
credentials {
ssid: "your_ssid"
pass: "your_pass"
}
В приведенном выше примере credentials – это пространство имен (namespace), а ssid и pass – ключи (keys).
Вы также можете иметь несколько пространств имен с одинаковым ключом (но каждый ключ со своим значением):
namespace1{
key:value1
}
namespace2{
key:value2
}
При использовании библиотеки Preferences.h вы должны указать тип данных, который хотите сохранить. Позже, если вы захотите прочитать эти данные, вам нужно знать сохраненный тип данных. Другими словами, тип данных при записи и чтении должен совпадать.
Вы можете сохранять следующие типы данных с помощью Preferences.h: char, Uchar, short, Ushort, int, Uint, long, Ulong, long64, Ulong64, float, double, bool, string и bytes.
Для получения дополнительной информации вы можете ознакомиться с файлом Preferences.cpp здесь.
Полезные функции библиотеки Preferences.h
Для использования библиотеки Preferences.h для хранения данных сначала нужно подключить её в вашем скетче:
#include <Preferences.h>
Затем необходимо создать экземпляр библиотеки Preferences. Вы можете назвать его, например, preferences:
Preferences preferences;
После этого вы можете использовать следующие методы для работы с данными с помощью библиотеки Preferences.h.
Инициализация Preferences
Метод begin() открывает «пространство хранения» с определенным пространством имен. Аргумент false означает, что мы будем использовать его в режиме чтения/записи. Используйте true, чтобы открыть или создать пространство имен в режиме только для чтения.
preferences.begin("my-app", false);
В данном случае имя пространства имен – my-app. Имя пространства имен ограничено 15 символами.
Очистка Preferences
Используйте clear() для очистки всех настроек в открытом пространстве имен (пространство имен не удаляется):
preferences.clear();
Удаление ключа
Удаление ключа из открытого пространства имен:
preferences.remove(key);
Закрытие Preferences
Используйте метод end() для закрытия настроек в открытом пространстве имен:
preferences.end();
Запись значения по ключу (Сохранение значения)
Вы должны использовать различные методы в зависимости от типа переменной, которую хотите сохранить.
Char |
putChar(const char* key, int8_t value) |
Unsigned Char |
putUChar(const char* key, int8_t value) |
Short |
putShort(const char* key, int16_t value) |
Unsigned Short |
putUShort(const char* key, uint16_t value) |
Int |
putInt(const char* key, int32_t value) |
Unsigned Int |
putUInt(const char* key, uint32_t value) |
Long |
putLong(const char* key, int32_t value) |
Unsigned Long |
putULong(const char* key, uint32_t value) |
Long64 |
putLong64(const char* key, int64_t value) |
Unsigned Long64 |
putULong64(const char* key, uint64_t value) |
Float |
putFloat(const char* key, const float_t value) |
Double |
putDouble(const char* key, const double_t value) |
Bool |
putBool(const char* key, const bool value) |
String |
putString(const char* key, const String value) |
Bytes |
putBytes(const char* key, const void* value, size_t len) |
Чтение значения по ключу (Чтение значения)
Аналогично, вы должны использовать различные методы в зависимости от типа переменной, которую хотите получить.
Char |
getChar(const char* key, const int8_t defaultValue) |
Unsigned Char |
getUChar(const char* key, const uint8_t defaultValue) |
Short |
getShort(const char* key, const int16_t defaultValue) |
Unsigned Short |
getUShort(const char* key, const uint16_t defaultValue) |
Int |
getInt(const char* key, const int32_t defaultValue) |
Unsigned Int |
getUInt(const char* key, const uint32_t defaultValue) |
Long |
getLong(const char* key, const int32_t defaultValue) |
Unsigned Long |
getULong(const char* key, const uint32_t defaultValue) |
Long64 |
getLong64(const char* key, const int64_t defaultValue) |
Unsigned Long64 |
getULong64(const char* key, const uint64_t defaultValue) |
Float |
getFloat(const char* key, const float_t defaultValue) |
Double |
getDouble(const char* key, const double_t defaultValue) |
Bool |
getBool(const char* key, const bool defaultValue) |
String |
getString(const char* key, const String defaultValue) |
String |
getString(const char* key, char* value, const size_t maxLen) |
Bytes |
getBytes(const char* key, void * buf, size_t maxLen) |
Удаление пространства имен
В реализации Preferences для Arduino нет метода для полного удаления пространства имен. В результате, в ходе нескольких проектов раздел энергонезависимого хранилища (NVS) Preferences на ESP32 может заполниться. Чтобы полностью стереть и переформатировать память NVS, используемую Preferences, создайте скетч, содержащий:
#include <nvs_flash.h>
void setup() {
nvs_flash_erase(); // erase the NVS partition and...
nvs_flash_init(); // initialize the NVS partition.
while(true);
}
void loop() {
}
Вы должны загрузить новый скетч на плату сразу после выполнения приведенного выше, иначе он будет переформатировать раздел NVS при каждом включении.
Preferences.h – Сохранение пар ключ:значение
Для простого примера сохранения и получения данных с помощью Preferences.h, в Arduino IDE перейдите в File > Examples > Preferences > StartCounter.
/*
ESP32 startup counter example with Preferences library.
This simple example demonstrates using the Preferences library to store how many times the ESP32 module has booted.
The Preferences library is a wrapper around the Non-volatile storage on ESP32 processor.
created for arduino-esp32 09 Feb 2017 by Martin Sloup (Arcao)
Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
*/
#include <Preferences.h>
Preferences preferences;
void setup() {
Serial.begin(115200);
Serial.println();
// Open Preferences with my-app namespace. Each application module, library, etc
// has to use a namespace name to prevent key name collisions. We will open storage in
// RW-mode (second parameter has to be false).
// Note: Namespace name is limited to 15 chars.
preferences.begin("my-app", false);
// Remove all preferences under the opened namespace
//preferences.clear();
// Or remove the counter key only
//preferences.remove("counter");
// Get the counter value, if the key does not exist, return a default value of 0
// Note: Key name is limited to 15 chars.
unsigned int counter = preferences.getUInt("counter", 0);
// Increase counter by 1
counter++;
// Print the counter to Serial Monitor
Serial.printf("Current counter value: %u\n", counter);
// Store the counter to the Preferences
preferences.putUInt("counter", counter);
// Close the Preferences
preferences.end();
// Wait 10 seconds
Serial.println("Restarting in 10 seconds...");
delay(10000);
// Restart ESP
ESP.restart();
}
void loop() {
}
Этот пример увеличивает переменную counter между перезагрузками. Это демонстрирует, что ESP32 «запоминает» значение даже после сброса.
Загрузите предыдущий скетч на плату ESP32. Откройте Serial Monitor на скорости 115200 бод и нажмите встроенную кнопку RST. Вы должны увидеть, как переменная counter увеличивается между перезагрузками.
Как работает код
В этом примере используются функции, которые мы рассмотрели в предыдущих разделах.
Сначала подключите библиотеку Preferences.h.
#include <Preferences.h>
Затем создайте экземпляр библиотеки с именем preferences.
Preferences preferences;
В setup() инициализируйте Serial Monitor на скорости 115200 бод.
Serial.begin(115200);
Создайте «пространство хранения» во флеш-памяти с именем my-app в режиме чтения/записи. Вы можете дать ему любое другое имя.
preferences.begin("my-app", false);
Получите значение ключа counter, сохраненного в Preferences. Если значение не найдено, по умолчанию возвращается 0 (что происходит при первом запуске этого кода).
unsigned int counter = preferences.getUInt("counter", 0);
Переменная counter увеличивается на единицу при каждом запуске ESP:
counter++;
Выведите значение переменной counter:
Serial.printf("Current counter value: %u\n", counter);
Сохраните новое значение в ключе «counter»:
preferences.putUInt("counter", counter);
Закройте Preferences.
preferences.end();
Наконец, перезагрузите плату ESP32:
ESP.restart();
ESP32 – Сохранение/Чтение сетевых учетных данных с помощью библиотеки Preferences.h
Библиотека Preferences.h часто используется для постоянного сохранения сетевых учетных данных во флеш-памяти. Таким образом, вам не нужно жестко прописывать учетные данные в каждом скетче, который подключает ESP32 к интернету.
В этом разделе мы покажем два простых скетча, которые могут быть полезны в ваших проектах:
Чтобы узнать больше о функциях Wi-Fi для ESP32, прочитайте следующую статью:
Сохранение сетевых учетных данных с помощью Preferences.h
Следующий скетч сохраняет ваши сетевые учетные данные в постоянную флеш-память ESP32 с помощью Preferences.h.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Preferences.h>
Preferences preferences;
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
void setup() {
Serial.begin(115200);
Serial.println();
preferences.begin("credentials", false);
preferences.putString("ssid", ssid);
preferences.putString("password", password);
Serial.println("Network Credentials Saved using Preferences");
preferences.end();
}
void loop() {
}
Не забудьте указать ваши сетевые учетные данные в следующих переменных:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Как работает код
Рассмотрим основные части кода этого примера.
В setup() создайте новое пространство хранения во флеш-памяти с пространством имен credentials.
preferences.begin("credentials", false);
Затем создайте ключ ssid, который сохраняет значение вашего SSID (переменная ssid) – используйте метод putString().
preferences.putString("ssid", ssid);
Добавьте еще один ключ password для сохранения пароля (переменная password):
preferences.putString("password", password);
Таким образом, ваши данные структурированы следующим образом:
credentials{
ssid: your_ssid
password: your_password
}
Загрузите код на плату, и вот что вы должны увидеть в Serial Monitor:
В следующем примере мы покажем, как считать сетевые учетные данные из Preferences и использовать их для подключения ESP32 к вашей сети.
Подключение к Wi-Fi с учетными данными, сохраненными в Preferences
Следующий скетч получает значения сетевых учетных данных и подключается к вашей сети, используя эти учетные данные.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Preferences.h>
#include "WiFi.h"
Preferences preferences;
String ssid;
String password;
void setup() {
Serial.begin(115200);
Serial.println();
preferences.begin("credentials", false);
ssid = preferences.getString("ssid", "");
password = preferences.getString("password", "");
if (ssid == "" || password == ""){
Serial.println("No values saved for ssid or password");
}
else {
// Connect to Wi-Fi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
}
}
void loop() {
// put your main code here, to run repeatedly:
}
Как работает код
Рассмотрим основные части кода этого примера.
Откройте пространство имен credentials:
preferences.begin("credentials", false);
Получите значения SSID и пароля с помощью метода getString(). Вам нужно использовать имя ключа, которое вы использовали для сохранения переменных, в данном случае ключи ssid и password:
ssid = preferences.getString("ssid", "");
password = preferences.getString("password", "");
В качестве второго аргумента функции getString() мы передали пустую строку (String). Это значение, возвращаемое по умолчанию, если ключи ssid или password не сохранены в Preferences.
Если это так, мы выводим сообщение о том, что сохраненных значений нет:
if (ssid == "" || password == ""){
Serial.println("No values saved for ssid or password");
}
В противном случае мы подключаемся к Wi-Fi, используя SSID и пароль, сохраненные в Preferences.
else {
// Connect to Wi-Fi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
}
Загрузите этот код на плату после предыдущего (чтобы убедиться, что учетные данные сохранены). Если все пройдет как ожидалось, вот что вы должны увидеть в Serial Monitor.
Запоминание последнего состояния GPIO после сброса
Еще одно применение библиотеки Preferences.h – сохранение последнего состояния выхода. Например, представьте следующий сценарий:
Вы управляете выходом с помощью ESP32;
Вы включаете выход;
ESP32 внезапно теряет питание;
Когда питание восстанавливается, выход остается выключенным – потому что последнее состояние не было сохранено.
Вы не хотите, чтобы это происходило. Вы хотите, чтобы ESP32 запоминал, что происходило до потери питания, и возвращался к последнему состоянию.
Чтобы решить эту проблему, вы можете сохранить состояние выхода во флеш-памяти. Затем вам нужно добавить условие в начале скетча для проверки последнего состояния выхода и соответствующего включения или выключения.
На следующем рисунке показано, что мы собираемся сделать:
Мы покажем пример с использованием светодиода и кнопки. Кнопка управляет состоянием светодиода. Светодиод сохраняет свое состояние между перезагрузками. Это означает, что если светодиод горел, когда вы отключили питание, он будет гореть при повторном включении.
Схема подключения
Подключите кнопку и светодиод к ESP32, как показано на следующей схеме.
Рекомендуемое чтение: ESP32 Pinout Reference: Какие GPIO пины следует использовать?
Код
Это код с подавлением дребезга контактов, который меняет состояние светодиода при каждом нажатии кнопки. Но в этом коде есть кое-что особенное – он запоминает последнее состояние светодиода даже после сброса или отключения питания от ESP32. Это возможно, потому что мы сохраняем состояние светодиода в Preferences при каждом его изменении.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Preferences.h>
Preferences preferences;
const int buttonPin = 4;
const int ledPin = 5;
bool ledState;
bool buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
void setup() {
Serial.begin(115200);
//Create a namespace called "gpio"
preferences.begin("gpio", false);
pinMode(buttonPin, INPUT);
pinMode(ledPin, OUTPUT);
// read the last LED state from flash memory
ledState = preferences.getBool("state", false);
Serial.printf("LED state before reset: %d \n", ledState);
// set the LED to the last stored state
digitalWrite(ledPin, ledState);
}
void loop() {
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) {
ledState = !ledState;
}
}
}
lastButtonState = reading;
if (digitalRead(ledPin)!= ledState) {
Serial.println("State changed");
// change the LED state
digitalWrite(ledPin, ledState);
// save the LED state in flash memory
preferences.putBool("state", ledState);
Serial.printf("State saved: %d \n", ledState);
}
}
Как работает код
Рассмотрим основные части кода этого примера.
В setup() начните с создания раздела во флеш-памяти для сохранения состояния GPIO. В этом примере мы назвали его gpio.
preferences.begin("gpio", false);
Получите состояние GPIO, сохраненное в Preferences по ключу state. Это булева переменная, поэтому используйте функцию getBool(). Если ключа state еще не существует (что происходит при первом запуске ESP32), возвращается false (светодиод будет выключен).
ledState = preferences.getBool("state", false);
Выведите состояние и установите светодиод в правильное состояние:
Serial.printf("LED state before reset: %d \n", ledState);
// set the LED to the last stored state
digitalWrite(ledPin, ledState);
Наконец, в loop() обновляйте ключ state в Preferences при каждом изменении.
// save the LED state in flash memory
preferences.putBool("state", ledState);
Serial.printf("State saved: %d \n", ledState);
Демонстрация
Загрузите код на плату и соберите схему. Откройте Serial Monitor на скорости 115200 бод и нажмите встроенную кнопку RST.
Нажмите кнопку, чтобы изменить состояние светодиода, а затем отключите питание или нажмите кнопку RST.
Когда ESP32 перезагрузится, он прочитает последнее сохраненное состояние из Preferences и установит светодиод в это состояние. Он также выводит сообщение в Serial Monitor при каждом изменении состояния GPIO.
Заключение
В этом руководстве вы узнали, как сохранять данные в постоянную флеш-память ESP32 с помощью библиотеки Preferences.h. Эта библиотека удобна для сохранения пар ключ:значение. Данные, хранящиеся во флеш-памяти, сохраняются даже после перезагрузки ESP32 или отключения питания.
Если вам нужно сохранять большие объемы данных или файлы, используйте файловую систему ESP32 (SPIFFS) или microSD-карту:
Надеемся, что это руководство было вам полезно.
Узнайте больше об ESP32 с нашими ресурсами:
Источник: ESP32 Save Data Permanently using Preferences Library – Random Nerd Tutorials