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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:35:57