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
相关产品推荐
相关产品推荐

