ESP-01S重启读取EEPROM中WiFi数据时反复重启问题求助
解决ESP设备EEPROM存储WiFi配置结构体触发重启的问题
我在开发带AP配网功能的ESP项目,目的是把WiFi信息存入EEPROM,让设备重启后自动连接。现在数据看似写入成功,但重启后执行EEPROM.get()时设备反复重启,看门狗触发复位。wifi_usuario.accesoAP是标记EEPROM是否已写入的标志,原代码如下:
struct wifiConfig { String ssid; String clave; String ipDir; String gatewaydir; bool accesoAP; } wifi_usuario = {"","","","",false}, wifi_usuario_leida = {"","","","",false}; void setup() { EEPROM.begin(sizeof(struct wifiConfig)); EEPROM.get(0, wifi_usuario_leida); if(!wifi_usuario.accesoAP) { Serial.println("Almacenado por primera vez datos en la EEPROM"); EEPROM.put(0, wifi_usuario); EEPROM.commit(); } else if(wifi_usuario_leida.accesoAP) { Serial.println("Se leerán los datos del wifi almacenados desde el modo AP"); EEPROM.get(0, wifi_usuario_leida); Serial.println("Los datos de la red a conectarse han sido previamente grabados"); Serial.println("Asignando parámetros de red inalámbrica"); ssid = wifi_usuario_leida.ssid; Serial.print("Asignado el ssid: "); Serial.println(ssid); pass = wifi_usuario_leida.clave; Serial.print("Asignado el pass: "); Serial.println(pass); ip = wifi_usuario_leida.ipDir; Serial.print("Asignada la IP: "); Serial.println(ip); gateway = wifi_usuario_leida.gatewaydir; Serial.print("Asignado el gateway: "); Serial.println(gateway); Serial.println("Parámetros de red inalámbrica asignados"); } if(initWifi()) { //Do some stuff to configure the ESP as STA mode. } else { //Do some stuff to configure the ESP as AP mode to store wifi credentials. //In this part of the code the data is stored in the EEPROM like follows WiFi.softAP("PORTÓN-WIFI-MANAGER", NULL); IPAddress IP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(IP); Serial.println("Almacenando parámetros de red en la estructura."); wifi_usuario.ssid = ssid; wifi_usuario.clave = pass; wifi_usuario.ipDir = ip; wifi_usuario.gatewaydir = gateway; wifi_usuario.accesoAP = true; Serial.println("Almacenados los datos en la estructura, almacenando estructura en la EEPROM"); EEPROM.put(0, wifi_usuario); if(EEPROM.commit()) { Serial.println("Datos almacenados en la EEPROM"); EEPROM.get(0, wifi_usuario_leida); Serial.println(wifi_usuario_leida.ssid); Serial.println(wifi_usuario_leida.clave); Serial.println(wifi_usuario_leida.ipDir); Serial.println(wifi_usuario_leida.gatewaydir); Serial.println(wifi_usuario_leida.accesoAP); } request->send(200, "text/plain", "Red configurada. El módulo se reiniciará, por favor conéctese a su enrutador y vaya a la dirección IP: " + ip); Serial.println("En 4 segundos se estará reiniciando el ESP."); delay(4000); ESP.restart(); } }
重启后执行EEPROM.get(0, wifi_usuario_leida)触发重启,核心问题是结构体中的String类型不适合存入EEPROM:String是动态内存对象,存储的是内存指针和长度而非实际字符串内容,写入EEPROM的指针地址在重启后会变成无效内存,读取时ESP访问非法内存,触发看门狗复位。同时原代码逻辑存在错误:判断!wifi_usuario.accesoAP时,wifi_usuario是每次启动都初始化的本地变量,值永远为false,导致每次启动都会执行第一次的EEPROM写入,覆盖之前的配置。
解决方案
1. 替换结构体中的String为固定长度char数组
EEPROM适合存储固定大小的数据,将String换成固定长度的char数组,确保结构体大小固定,避免动态内存问题:
// 定义固定长度:SSID最大32字符,密码最大64,IP/网关最大15字符 struct wifiConfig { char ssid[32]; char clave[64]; char ipDir[16]; char gatewaydir[16]; bool accesoAP; } wifi_usuario = {"","","","",false}, wifi_usuario_leida = {"","","","",false};
2. 修改字符串赋值逻辑
使用strncpy将String内容复制到char数组,避免溢出:
在AP配网后的赋值部分修改:
// 替换原String赋值 strncpy(wifi_usuario.ssid, ssid.c_str(), sizeof(wifi_usuario.ssid)-1); wifi_usuario.ssid[sizeof(wifi_usuario.ssid)-1] = '\0'; // 确保字符串结束符 strncpy(wifi_usuario.clave, pass.c_str(), sizeof(wifi_usuario.clave)-1); wifi_usuario.clave[sizeof(wifi_usuario.clave)-1] = '\0'; strncpy(wifi_usuario.ipDir, ip.c_str(), sizeof(wifi_usuario.ipDir)-1); wifi_usuario.ipDir[sizeof(wifi_usuario.ipDir)-1] = '\0'; strncpy(wifi_usuario.gatewaydir, gateway.c_str(), sizeof(wifi_usuario.gatewaydir)-1); wifi_usuario.gatewaydir[sizeof(wifi_usuario.gatewaydir)-1] = '\0'; wifi_usuario.accesoAP = true;
3. 修正初始化判断逻辑
原代码判断的是初始变量wifi_usuario.accesoAP,永远为false,应该判断从EEPROM读取的wifi_usuario_leida.accesoAP:
void setup() { EEPROM.begin(sizeof(struct wifiConfig)); EEPROM.get(0, wifi_usuario_leida); // 修正判断逻辑:检查EEPROM中是否已有有效配置 if(!wifi_usuario_leida.accesoAP) { Serial.println("Almacenando configuración inicial en EEPROM"); EEPROM.put(0, wifi_usuario); EEPROM.commit(); } else { Serial.println("Leyendo configuración WiFi desde EEPROM"); // 这里不需要重复get,之前已经读取过了 Serial.println("Los datos de la red a conectarse han sido previamente grabados"); Serial.println("Asignando parámetros de red inalámbrica"); String ssid = String(wifi_usuario_leida.ssid); String pass = String(wifi_usuario_leida.clave); String ip = String(wifi_usuario_leida.ipDir); String gateway = String(wifi_usuario_leida.gatewaydir); Serial.print("Asignado el ssid: "); Serial.println(ssid); Serial.print("Asignado el pass: "); Serial.println(pass); Serial.print("Asignada la IP: "); Serial.println(ip); Serial.print("Asignado el gateway: "); Serial.println(gateway); Serial.println("Parámetros de red inalámbrica asignados"); } // 后续initWifi逻辑不变 if(initWifi()) { // STA模式配置 } else { // AP配网模式逻辑,已修改赋值部分 } }
4. 注意事项
- 每次修改结构体后,建议先清空EEPROM(比如写入全0)再测试,避免旧数据导致的结构不匹配。
EEPROM.begin()的参数必须是sizeof(struct wifiConfig),确保分配足够的EEPROM空间。
内容的提问来源于stack exchange,提问作者vram
相关产品推荐
相关产品推荐

