1效果
网页输入数据,串口打印数据。掉电后数据还在
2源码
cpp
#include <WiFi.h> // 包含WiFi库,用于处理WiFi连接
#include <WebServer.h> // 包含WebServer库,用于创建Web服务器
#include <Preferences.h> // 包含Preferences库,用于在非易失性存储中保存键值对
// 定义接入点名称
const char* ssid = "ESP32C3";
WebServer server(80); // 创建Web服务器实例,监听80端口
Preferences preferences; // 创建Preferences实例,用于存储数据
// 处理根目录请求的函数
void handleRoot() {
String html = "<html><body>" // 构建HTML响应
"<form action=\"/save\" method=\"POST\">" // 表单提交到/save路由
"Enter data: <input type=\"text\" name=\"data\">" // 输入框,用户可以输入数据
"<input type=\"submit\" value=\"Save\">" // 提交按钮
"</form></body></html>";
server.send(200, "text/html", html); // 发送HTTP响应,状态码200表示成功,内容类型为text/html
}
// 处理保存数据的函数
void handleSave() {
if (server.hasArg("data")) { // 检查是否有名为"data"的参数
String data = server.arg("data"); // 获取名为"data"的参数值
// 将数据保存到Preferences
preferences.putString("myDataKey", data); // 使用"myDataKey"作为键保存数据
server.send(200, "text/plain", "Data saved!"); // 发送成功保存的响应
} else {
server.send(400, "text/plain", "Bad Request"); // 如果没有"data"参数,发送400错误响应
}
}
// 初始化函数
void setup() {
Serial.begin(115200); // 初始化串口通信,波特率为115200
WiFi.softAP(ssid); // 创建一个名为ssid的接入点
server.begin(); // 启动Web服务器
// 初始化Preferences,创建或打开名为"storage"的命名空间,不允许在文件系统中创建新文件
preferences.begin("storage", false);
// 设置Web服务器路由
server.on("/", handleRoot); // 当访问根目录时,调用handleRoot函数
server.on("/save", HTTP_POST, handleSave); // 当通过POST方法访问/save时,调用handleSave函数
}
// 主循环函数
void loop() {
server.handleClient(); // 处理客户端请求
// 读取Preferences中的数据并通过串口输出
String data = preferences.getString("myDataKey", "No data saved"); // 读取键为"myDataKey"的数据,如果不存在则返回"No data saved"
Serial.println("Data from Preferences: " + data); // 输出数据到串口
delay(1000); // 等待1000毫秒(1秒),每秒输出一次
}
3重点解释区
3-1
<Preferences.h>
是 Arduino ESP32 环境中的一个库,用于在 ESP32 的非易失性存储器(NVS)上保存数据。这个库是 Arduino EEPROM 库的替代品,特别适用于存储多个小值,而不是几个大值。如果需要存储大量数据,可以考虑使用文件系统库,如 LitteFS5。
基本功能和使用方法
-
数据存储方式 :
Preferences
库在 NVS 中以"命名空间(namespace)"的形式存储数据,每个命名空间包含一系列的"键值对(key-value pairs)"。键名(key)是数据项的名称,值(value)是该项数据的具体值。命名空间和键名都是字符串,且长度不超过15个字符。多个命名空间是允许的,且每个命名空间内的键名必须是唯一的5。 -
支持的数据类型 :
Preferences
直接支持多种数据类型,包括布尔型、字符型、整型、长整型、浮点型、双精度型和字符串等5。 -
基本操作 :使用
Preferences
库的基本步骤包括创建或打开命名空间、存储和检索数据、删除键值对、确定键值对的数据类型等。存储数据时,需要先打开命名空间,然后使用键名存储数据,最后关闭命名空间。检索数据时,同样需要先打开命名空间,然后使用键名获取数据,最后关闭命名空间5。
cpp
#include <Preferences.h>
void setup() {
Serial.begin(115200);
Serial.println();
delay(2000);
Preferences prefs; // 声明Preferences对象
prefs.begin("mynamespace"); // 打开命名空间mynamespace
uint32_t count = prefs.getUInt("count", 0); // 获取当前命名空间中的键名为"count"的值
count++; // 累加计数
Serial.printf("这是系统第 %u 次启动\n", count);
prefs.putUInt("count", count); // 将数据保存到当前命名空间的"count"键中
prefs.end(); // 关闭当前命名空间
}
void loop() {
// ...
}
布尔型(bool)
存储布尔值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putBool("myBool", true); // 存储布尔值 true
prefs.end();
获取布尔值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
bool myBool = prefs.getBool("myBool", false); // 获取布尔值,如果不存在则返回默认值 false
prefs.end();
字符型(int8_t)
存储字符型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putChar("myChar", 'A'); // 存储字符 'A'
prefs.end();
获取字符型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
char myChar = prefs.getChar("myChar", 'Z'); // 获取字符,如果不存在则返回默认值 'Z'
prefs.end();
整型(int32_t)
存储整型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putInt("myInt", 123); // 存储整型值 123
prefs.end();
获取整型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
int32_t myInt = prefs.getInt("myInt", -1); // 获取整型值,如果不存在则返回默认值 -1
prefs.end();
长整型(int32_t)
存储长整型值(在 Preferences 库中,长整型和整型使用相同的数据类型和存储方法):
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putLong("myLong", 1234567890); // 存储长整型值 1234567890
prefs.end();
获取长整型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
int32_t myLong = prefs.getLong("myLong", -1); // 获取长整型值,如果不存在则返回默认值 -1
prefs.end();
浮点型(float)
存储浮点型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putFloat("myFloat", 3.14f); // 存储浮点型值 3.14
prefs.end();
获取浮点型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
float myFloat = prefs.getFloat("myFloat", 0.0f); // 获取浮点型值,如果不存在则返回默认值 0.0
prefs.end();
双精度型(double)
存储双精度型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putDouble("myDouble", 3.14159265358979323846); // 存储双精度型值
prefs.end();
获取双精度型值:
cpp
Preferences prefs;
prefs.begin("mynamespace");
double myDouble = prefs.getDouble("myDouble", 0.0); // 获取双精度型值,如果不存在则返回默认值 0.0
prefs.end();
字符串(const char*)
存储字符串:
cpp
Preferences prefs;
prefs.begin("mynamespace");
prefs.putString("myString", "Hello World"); // 存储字符串 "Hello World"
prefs.end();
获取字符串:
cpp
Preferences prefs;
prefs.begin("mynamespace");
String myString = prefs.getString("myString", "Default"); // 获取字符串,如果不存在则返回默认值 "Default"
prefs.end();
请注意,字符串可以存储为 Arduino String 对象或 C-string(以空字符结尾的字符数组)。在存储和检索字符串时,确保使用正确的方法。如果使用 C-string,请确保字符串长度不超过 NVS 分区允许的最大长度。
何时调用 prefs.end();(运行后将清理此对象,数据还在就是不可用重新上电后可以看到数据但在运行到它是再次读取数据时读不到)
-
在完成所有数据操作后调用 :在完成对 NVS 的所有读取和写入操作后,应该调用
prefs.end();
。这通常是在一系列put...()
(用于写入数据)或get...()
(用于读取数据)方法调用之后。 -
在适当的作用域结束处调用 :通常在函数或作用域结束前调用
prefs.end();
,以确保资源得到及时释放。
prefs.end();
的作用
-
确保数据写入 :调用
prefs.end();
确保所有挂起的写入操作都已完成,并且数据已经安全地存储在 NVS 中。 -
释放资源:这个方法会关闭 Preferences 实例与 NVS 的连接,并释放与之相关的内存和其他资源。
-
防止数据损坏 :如果不调用
prefs.end();
,可能会导致数据未完全写入或损坏,特别是在断电或程序崩溃的情况下。
示例
cpp
Preferences prefs;
void setup() {
// 开始 Preferences 实例,指定命名空间
prefs.begin("mynamespace", false); // false 表示不覆盖现有数据
// 存储一些数据
prefs.putInt("myInt", 123);
prefs.putFloat("myFloat", 456.78);
// 结束 Preferences 实例
prefs.end();
}
void loop() {
// 其他代码...
}