You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32如何在自动重启前仅存储变更后的浮点数至EEPROM?

ESP32浮点数EEPROM存储与millis()溢出解决方案

一、浮点数的EEPROM存储实现

ESP32的EEPROM仅支持字节级读写,而float类型占4字节(32位),可以通过类型转换或union联合体实现字节级操作:

方法1:使用union联合体

#include <EEPROM.h>

// 联合体实现float与字节数组的互转
union FloatBytes {
  float value;
  uint8_t bytes[4];
};

// 从指定EEPROM地址读取浮点数
float readFloatFromEEPROM(int addr) {
  FloatBytes fb;
  for (int i = 0; i < 4; i++) {
    fb.bytes[i] = EEPROM.read(addr + i);
  }
  return fb.value;
}

// 将浮点数写入指定EEPROM地址
void writeFloatToEEPROM(int addr, float value) {
  FloatBytes fb;
  fb.value = value;
  for (int i = 0; i < 4; i++) {
    EEPROM.write(addr + i, fb.bytes[i]);
  }
  EEPROM.commit(); // ESP32需调用commit才会写入硬件闪存
}

方法2:使用memcpy直接内存复制

#include <EEPROM.h>
#include <cstring>

float readFloatFromEEPROM(int addr) {
  float result;
  uint8_t buffer[4];
  for (int i = 0; i < 4; i++) {
    buffer[i] = EEPROM.read(addr + i);
  }
  memcpy(&result, buffer, sizeof(result));
  return result;
}

void writeFloatToEEPROM(int addr, float value) {
  uint8_t buffer[4];
  memcpy(buffer, &value, sizeof(buffer));
  for (int i = 0; i < 4; i++) {
    EEPROM.write(addr + i, buffer[i]);
  }
  EEPROM.commit();
}

二、仅在阈值变更时写入EEPROM

1. 初始化与变更标记

  • 启动时从EEPROM加载初始阈值,保存为基准值
  • 用布尔变量标记阈值是否变更,修改阈值时置为true
float minTempThreshold, maxTempThreshold;
float originalMinTemp, originalMaxTemp;
bool thresholdsChanged = false;

void setup() {
  EEPROM.begin(512); // 初始化EEPROM,按需调整大小
  
  // 加载初始阈值
  minTempThreshold = readFloatFromEEPROM(0);    // 地址0存储min阈值
  maxTempThreshold = readFloatFromEEPROM(4);    // 地址4存储max阈值
  originalMinTemp = minTempThreshold;
  originalMaxTemp = maxTempThreshold;
  
  // 注册关机回调,重启前检查是否需要保存
  esp_register_shutdown_handler(shutdownCallback);
}

2. 修改阈值时标记变更

// 示例:更新阈值的函数
void updateTempThresholds(float newMin, float newMax) {
  // 若存在浮点精度问题,可改用误差范围判断:abs(newMin - minTempThreshold) > 0.001
  if (newMin != minTempThreshold || newMax != maxTempThreshold) {
    minTempThreshold = newMin;
    maxTempThreshold = newMax;
    thresholdsChanged = true; // 标记阈值已变更
  }
}

3. 关机回调中执行保存

void shutdownCallback() {
  if (thresholdsChanged) {
    writeFloatToEEPROM(0, minTempThreshold);
    writeFloatToEEPROM(4, maxTempThreshold);
    thresholdsChanged = false; // 重置标记,避免重复写入
  }
}

三、millis()溢出的安全处理

millis()返回unsigned long类型(32位),溢出后会从0重新计数,使用无符号数减法计算时间差即可避免逻辑错误:

unsigned long previousMillis = 0;
const long interval = 1000; // 1秒间隔

void loop() {
  unsigned long currentMillis = millis();
  
  // 溢出后依然能正确计算时间差
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    // 执行定时任务,比如检查阈值状态
    checkThresholdStatus();
  }
}

原理:无符号数溢出后,currentMillis - previousMillis会自动按模2^32计算,比如0 - 4294967295的结果为1,符合实际时间差逻辑。

四、额外注意事项

  • ESP32的EEPROM是模拟闪存区域,EEPROM.commit()不要频繁调用(闪存有写入寿命限制)
  • 首次运行时可设置初始化标记,避免读取无效数据:
void setup() {
  EEPROM.begin(512);
  // 检查是否为首次运行(地址8存储初始化标记)
  uint8_t initFlag = EEPROM.read(8);
  if (initFlag != 0xAA) {
    // 设置默认阈值
    minTempThreshold = 20.0f;
    maxTempThreshold = 30.0f;
    writeFloatToEEPROM(0, minTempThreshold);
    writeFloatToEEPROM(4, maxTempThreshold);
    EEPROM.write(8, 0xAA); // 写入初始化标记
    EEPROM.commit();
    originalMinTemp = minTempThreshold;
    originalMaxTemp = maxTempThreshold;
  } else {
    // 加载已有阈值
    minTempThreshold = readFloatFromEEPROM(0);
    maxTempThreshold = readFloatFromEEPROM(4);
    originalMinTemp = minTempThreshold;
    originalMaxTemp = maxTempThreshold;
  }
  esp_register_shutdown_handler(shutdownCallback);
}

内容的提问来源于stack exchange,提问作者johnnyh20

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:58:11