共计 2883 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
在 ESP32 开发过程中,我们经常需要保存设备的运行参数,比如 Wi-Fi 配置、传感器校准值等。这些参数需要在设备重启后依然能够保持,传统的做法有以下几种:

- 使用全局变量:断电后数据丢失,无法持久化
- 使用 EEPROM:存储空间有限(通常只有几百字节),且擦写次数有限(约 10 万次)
- 使用 Preferences 库:比 EEPROM 更友好,但仍受限于键值对的简单结构
相比之下,文件系统方案(SPIFFS/LittleFS)具有明显优势:
- 存储空间大(通常可使用整个 Flash 的剩余空间)
- 支持结构化数据存储(如 JSON 格式)
- 可实现多配置文件管理
- 数据可读性强,便于调试
技术实现
1. 文件系统初始化
ESP32 支持两种文件系统:SPIFFS 和 LittleFS。后者是前者的改进版,具有更好的性能和可靠性。初始化流程如下:
- 安装 LittleFS 库(通过 Arduino 库管理器)
- 在 setup() 中初始化文件系统
#include "FS.h"
#include "LittleFS.h"
void setup() {Serial.begin(115200);
if(!LittleFS.begin()){Serial.println("LittleFS 挂载失败");
return;
}
Serial.println("LittleFS 挂载成功");
}
2. JSON 格式参数存储
使用 ArduinoJson 库处理 JSON 数据,以下是一个完整的配置保存和读取示例:
#include <ArduinoJson.h>
const char* configFile = "/config.json";
void saveConfig() {
StaticJsonDocument<512> doc;
doc["wifi_ssid"] = "my_wifi";
doc["wifi_pass"] = "password123";
doc["sensor_calib"] = 1.23;
File file = LittleFS.open(configFile, "w");
if(!file){Serial.println("打开文件失败");
return;
}
serializeJson(doc, file);
file.close();}
void loadConfig() {if(!LittleFS.exists(configFile)) {Serial.println("配置文件不存在");
return;
}
File file = LittleFS.open(configFile, "r");
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, file);
if(error) {Serial.print("JSON 解析错误:");
Serial.println(error.c_str());
return;
}
const char* ssid = doc["wifi_ssid"];
const char* pass = doc["wifi_pass"];
float calib = doc["sensor_calib"];
Serial.printf("SSID: %s, 密码: %s, 校准值: %.2f\n", ssid, pass, calib);
file.close();}
3. 封装为 ConfigManager 类
为提高代码复用性,我们可以将配置管理封装为一个类:
class ConfigManager {
public:
ConfigManager(const char* path) : filePath(path) {}
bool load(JsonDocument& doc) {if(!LittleFS.exists(filePath)) return false;
File file = LittleFS.open(filePath, "r");
DeserializationError error = deserializeJson(doc, file);
file.close();
return !error;
}
bool save(JsonDocument& doc) {File file = LittleFS.open(filePath, "w");
if(!file) return false;
serializeJson(doc, file);
file.close();
return true;
}
private:
const char* filePath;
};
进阶优化
1. 文件系统磨损均衡
Flash 存储器有擦写次数限制(通常 10 万次左右),为避免频繁写入同一区域:
- 避免高频写入(如每秒保存一次)
- 对大文件考虑循环写入不同区域
- 启用 LittleFS 的磨损均衡功能(默认已开启)
2. 数据完整性保障
为防止突发断电导致数据损坏:
- 先写入临时文件
- 写入完成后再重命名为目标文件
- 添加校验字段(如 CRC32)
bool safeSave(const char* path, JsonDocument& doc) {
const char* tempPath = "/config.tmp";
// 写入临时文件
if(!saveToFile(tempPath, doc)) return false;
// 重命名
if(!LittleFS.rename(tempPath, path)) {LittleFS.remove(tempPath);
return false;
}
return true;
}
3. 性能实测数据
在 ESP32-WROOM-32D 上进行测试:
- 写入 512B JSON 文件:约 15ms
- 读取 512B JSON 文件:约 8ms
- LittleFS 初始化时间:约 120ms(首次启动)
避坑指南
1. 文件系统挂载失败
常见原因及解决方案:
- Flash 分区表未配置文件系统:修改 partitions.csv
- 首次使用未格式化:调用 LittleFS.format()
- Flash 空间不足:检查分区大小
2. Flash 芯片兼容性
不同型号 ESP32 模块可能使用不同 Flash:
- WROOM 系列:通常兼容性好
- 某些国产模块:可能需要调整 SPI 模式
3. 并发访问控制
多任务环境下需要加锁:
#include "freertos/semphr.h"
SemaphoreHandle_t fsMutex = xSemaphoreCreateMutex();
void safeFileOperation() {if(xSemaphoreTake(fsMutex, pdMS_TO_TICKS(1000))) {
// 文件操作代码
xSemaphoreGive(fsMutex);
}
}
思考与扩展
- 参数加密 :敏感信息如 Wi-Fi 密码可考虑加密存储
- 单元测试 :使用 PlatformIO 的单元测试框架验证配置读写
- OTA 更新 :与 WebServer 结合实现远程配置更新
完整示例代码可参考 GitHub 仓库: 示例链接
总结
通过文件系统存储运行参数,我们实现了:
- 灵活的结构化数据存储
- 断电数据持久化
- 动态配置更新能力
相比传统方案,文件系统更适合存储复杂配置和大量数据。实际项目中,可根据需求选择 SPIFFS 或 LittleFS,并注意做好错误处理和性能优化。
正文完
