Arduino ESP32 持久化配置实战:基于文件系统的参数存储方案

1次阅读
没有评论

共计 2883 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

背景痛点

在 ESP32 开发过程中,我们经常需要保存设备的运行参数,比如 Wi-Fi 配置、传感器校准值等。这些参数需要在设备重启后依然能够保持,传统的做法有以下几种:

Arduino ESP32 持久化配置实战:基于文件系统的参数存储方案

  • 使用全局变量:断电后数据丢失,无法持久化
  • 使用 EEPROM:存储空间有限(通常只有几百字节),且擦写次数有限(约 10 万次)
  • 使用 Preferences 库:比 EEPROM 更友好,但仍受限于键值对的简单结构

相比之下,文件系统方案(SPIFFS/LittleFS)具有明显优势:

  1. 存储空间大(通常可使用整个 Flash 的剩余空间)
  2. 支持结构化数据存储(如 JSON 格式)
  3. 可实现多配置文件管理
  4. 数据可读性强,便于调试

技术实现

1. 文件系统初始化

ESP32 支持两种文件系统:SPIFFS 和 LittleFS。后者是前者的改进版,具有更好的性能和可靠性。初始化流程如下:

  1. 安装 LittleFS 库(通过 Arduino 库管理器)
  2. 在 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. 数据完整性保障

为防止突发断电导致数据损坏:

  1. 先写入临时文件
  2. 写入完成后再重命名为目标文件
  3. 添加校验字段(如 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);
  }
}

思考与扩展

  1. 参数加密 :敏感信息如 Wi-Fi 密码可考虑加密存储
  2. 单元测试 :使用 PlatformIO 的单元测试框架验证配置读写
  3. OTA 更新 :与 WebServer 结合实现远程配置更新

完整示例代码可参考 GitHub 仓库: 示例链接

总结

通过文件系统存储运行参数,我们实现了:

  • 灵活的结构化数据存储
  • 断电数据持久化
  • 动态配置更新能力

相比传统方案,文件系统更适合存储复杂配置和大量数据。实际项目中,可根据需求选择 SPIFFS 或 LittleFS,并注意做好错误处理和性能优化。

正文完
 0
评论(没有评论)