去年为一个工业传感器项目调试时,我需要让ESP32设备在断网情况下仍能保存校准参数和日志文件。尝试过EEPROM后发现容量太小,而外接SD卡又增加成本和故障点。最终采用LittleFS文件系统,成功将配置文件、HTML页面甚至小图标都塞进了ESP32的4MB闪存中。这种方案特别适合需要持久化存储中小型数据集的物联网设备。
LittleFS是ARM推出的轻量级文件系统,相比SPIFFS具有更快的挂载速度、更好的断电保护机制。实测在ESP32上,其写入速度可达80KB/s,且支持目录结构。以下是典型应用场景:
推荐使用ESP32-WROOM-32D模组(4MB Flash),其Flash分区方案更灵活。若选用ESP8266,需注意其Flash通常只有1MB,需手动调整分区表。关键硬件参数:
使用PlatformIO进行开发时,需在platformio.ini中添加:
ini复制board_build.filesystem = littlefs
monitor_speed = 115200
lib_deps =
arduino-libraries/Arduino_JSON @ 0.1.0
Arduino IDE用户需要:
partition.csv)重要提示:首次使用前务必执行擦除Flash操作(esptool.py erase_flash),避免旧分区表冲突
推荐使用异步挂载方式避免阻塞:
cpp复制#include "LITTLEFS.h"
void initFS() {
if(!LITTLEFS.begin(false)) { // 非格式化模式
Serial.println("首次使用,需要格式化...");
if(LITTLEFS.begin(true)) {
Serial.println("格式化成功");
} else {
Serial.println("格式化失败");
return;
}
}
size_t total = LITTLEFS.totalBytes();
size_t used = LITTLEFS.usedBytes();
Serial.printf("文件系统挂载成功,总空间: %uB,已用: %uB\n", total, used);
}
写入配置文件示例:
cpp复制void saveConfig() {
File configFile = LITTLEFS.open("/config.json", "w");
if(!configFile) {
Serial.println("打开文件失败");
return;
}
DynamicJsonDocument doc(1024);
doc["ssid"] = "MyWiFi";
doc["password"] = "12345678";
if(serializeJson(doc, configFile) == 0) {
Serial.println("写入失败");
}
configFile.close(); // 必须显式关闭!
}
读取文件优化技巧:
cpp复制String readFile(const char* path) {
File file = LITTLEFS.open(path, "r");
if(!file || file.isDirectory()) {
return String();
}
String content;
while(file.available()) {
content += char(file.read());
}
file.close();
return content;
}
关键经验:文件操作后必须调用close(),否则可能造成文件系统损坏。实测发现未关闭的文件在断电后会有50%概率丢失数据。
使用ESP32 LittleFS Data Upload插件时需注意:
data目录存放待上传文件上传流程:
通过Web接口实现动态文件管理:
cpp复制#include <WebServer.h>
WebServer server(80);
void handleUpload() {
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START) {
fs::File file = LITTLEFS.open(upload.filename, "w");
} else if(upload.status == UPLOAD_FILE_WRITE) {
file.write(upload.buf, upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END) {
file.close();
}
}
void setup() {
server.on("/upload", HTTP_POST, [](){
server.send(200, "text/plain", "Upload OK");
}, handleUpload);
}
/config.json和/config.bak| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| Mount failed: -1 | 分区表不匹配 | 重新烧录分区表 |
| File write error -100 | 空间不足 | 使用LITTLEFS.info()检查 |
| Corrupted file | 未正常关闭 | 添加异常处理机制 |
开发诊断函数:
cpp复制void checkFS() {
FSInfo fs_info;
LITTLEFS.info(fs_info);
Serial.printf("总空间: %uB\n", fs_info.totalBytes);
Serial.printf("已用空间: %uB\n", fs_info.usedBytes);
Serial.printf("块大小: %uB\n", fs_info.blockSize);
Serial.printf("最大路径长度: %u\n", fs_info.maxPathLength);
Dir dir = LITTLEFS.openDir("/");
while(dir.next()) {
Serial.printf("%s (%uB)\n", dir.fileName().c_str(), dir.fileSize());
}
}
cpp复制void backupToSerial() {
File file = LITTLEFS.open("/config.json", "r");
while(file.available()) {
Serial.write(file.read());
}
Serial.println(); // 结束标记
}
python复制# PC端接收脚本示例
import serial
ser = serial.Serial('COM3', 115200)
with open('backup.bin', 'wb') as f:
while True:
data = ser.read(ser.in_waiting)
if b'EOF' in data:
break
f.write(data)
对于频繁读取的配置文件,可加载到RAM:
cpp复制struct Config {
char ssid[32];
char password[64];
} config;
void loadConfig() {
File file = LITTLEFS.open("/config.bin", "r");
if(file.readBytes((char*)&config, sizeof(config)) != sizeof(config)) {
memset(&config, 0, sizeof(config));
}
file.close();
}
创建专用文件操作任务:
cpp复制QueueHandle_t fileQueue;
void fileTask(void *pv) {
while(1) {
FileOperation op;
if(xQueueReceive(fileQueue, &op, portMAX_DELAY)) {
processFileOp(op); // 执行文件操作
}
}
}
void setup() {
fileQueue = xQueueCreate(10, sizeof(FileOperation));
xTaskCreate(fileTask, "fileTask", 4096, NULL, 2, NULL);
}
添加UPS检测电路,在断电时快速保存:
cpp复制void IRAM_ATTR onPowerLoss() {
File file = LITTLEFS.open("/lastwill.txt", "w");
file.println("系统异常断电");
file.close(); // 必须同步完成
esp_deep_sleep_start();
}
void setup() {
attachInterrupt(POWER_PIN, onPowerLoss, FALLING);
}
实际项目中,我将温湿度传感器的每小时读数通过LittleFS暂存,网络恢复后批量上传。这个方案在野外气象站连续运行了8个月,期间经历多次异常断电,数据始终保持完整。关键点在于:
对于需要更高可靠性的场景,建议结合FRAM非易失存储器使用,将LittleFS作为二级缓存。这种混合存储方案在我参与的智慧农业项目中表现优异,设备重启后能立即恢复最后状态。