ESP32 freeRTOS issue with writing to EEPROM

Hi Experts,

I have a good background of coding with C# and .net but I am new to Arduino and C++

so here is the project detail and problem

Hardware:
ESP32 Wroom DevKitC
DHT22 Temperature sensor

the project is using freeRTOS to run a few tasks simultaneously, for now I have two tasks.

  1. reads temperature (which will later be sent to azure)
  2. monitor if wifi credentials are available then try to connect to it and stores the credentials in EEPROM and keep checking for wifi connectivity, if it is connected keep the LED constantly on, if disconnected, blink the LED and also trigger the wifi connect method again.

i want these tasks to be performed asynchronously.

when device starts it looks for credentials in EEPROM and stores them in global variables, which then are taken care by task 2

if there are no saved credentials it turns on the AP mode and launches a captive portal to configure wifi, the credentials are stored in global variables. which again are taken care by task 2

the reading part is done in setup() so that works fine but the writing part is done in a task which tries to connect to wifi when credentials are available.

Problem:

if reading or writing to EEPROM is done in setup, everything works fine, but if it is executed in a task, as soon as I call EEPROM.commit() the program crashes.

1 Like

here is the code, I didn't write the code for LED yet but that part is fairly simple.

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif

#include <WiFi.h>
#include <AsyncTCP.h>
#include <DNSServer.h>
#include <ESPAsyncWebServer.h>
#include <DHT.h>
#include "EEPROM.h"
#include <Arduino.h>  // for type definitions

#define _DHT_PIN    26
#define _DHTTYPE    DHT22


// define two tasks for Blink & AnalogRead
void ReadTemp( void *pvParameters );
void HandleGeneral( void *pvParameters );

DHT dht(_DHT_PIN, _DHTTYPE);
DNSServer dnsServer;
AsyncWebServer server(80);


float current_temp = 0;
bool APMode = false;
String portalTitle = "My ESP32 Hub";

String ssid = "";
String pwd = "";


struct hub_settings_t
{
  const char* ssid;
  const char* pwd;
} hubSettings;


bool hasWifi = false;
bool isConnected = false;
bool isConnecting = false;


void handleRequest(AsyncWebServerRequest *request) {
  if (request->host() != toStringIp(WiFi.softAPIP())) {
    if (APMode) {
      AsyncResponseStream *response = request->beginResponseStream("text/plain");
      response->setCode(302);
      response->addHeader("Location", String("http://") + toStringIp(WiFi.softAPIP()));
      request->send(response);
    }
  } else {
    AsyncResponseStream *response = request->beginResponseStream("text/html");
    response->printf("<!DOCTYPE html><html> <head> <meta charset=\"UTF-8\" name=\"viewport\" content=\"width=device-width,initial-scale=1\"/> <title>%s</title> <style>body, html{margin: 0px; padding: 0px; font-family: Arial, Helvetica, sans-serif; box-sizing: border-box;}nav{background-color: #dc143c; color: #fff; height: 50px; line-height: 50px; font-size: 1.4em; text-align: center;}.content{max-width: 300px; margin: 0 auto; padding-top: 30px;}input{font-size: 1em;}input[type=\"text\"], input[type=\"password\"]{padding: 6px; border: 1px solid #a5a5a5; border-radius: 3px; margin: 6px 0; width: 100%; box-sizing: border-box;}button{font-size: 1em; background-color: #dc143c; color: #fff; padding: 10px 14px; border: none; border-radius: 3px; cursor: pointer; width: 100%;}.field-label{margin-top: 10px; margin-bottom: 10px;}.form-field{margin-top: 10px; margin-bottom: 10px;}</style> </head> <body> <nav>%s</nav> <div class=\"content\">", portalTitle, portalTitle);
    if (request->url() == "/") {
      response->print("<div> <form action=\"save\" method=\"post\" > <div class=\"field-label\"> SSID: </div><div class=\"form-field\"> <input type=\"text\" name=\"ssid\"/> </div><div class=\"field-label\"> Password: </div><div class=\"form-field\"> <input type=\"password\" name=\"pwd\"/> </div><div><button type=\"submit\">Connect</button></div></form> </div>");
    } else if (request->url() == "/save") {
      if (request->hasParam("ssid", true)) {
        ssid = request->getParam("ssid", true, false)->value();
        hubSettings.ssid = ssid.c_str();
      }
      if (request->hasParam("pwd", true)) {
        pwd = request->getParam("pwd", true, false)->value();
        hubSettings.pwd = pwd.c_str();
      }
      Serial.print("wifi credentials received ");
      Serial.println(hubSettings.ssid);
      Serial.println(hubSettings.pwd);
      response->print("<div>Connecting to WiFi</div>");
      if (hubSettings.ssid != NULL && hubSettings.pwd != NULL) {
        hasWifi = true;
      }
    }
    response->print("</div></body></html>");
    request->send(response);
  }
}



void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_AP_STA);
  WiFi.setAutoConnect(false);
  Serial.println("Initializing");
  EEPROM.begin(512);

  if (!isFirstRun()) {
    ReadSettings(1, hubSettings);
  }

  if (hubSettings.ssid != NULL && hubSettings.pwd != NULL) {
    hasWifi = true;
  }
  dht.begin();
  if (!hasWifi) {
    SetupWifi();
  }

  xTaskCreatePinnedToCore(
    HandleGeneral
    ,  "HandleGeneral"   // A name just for humans
    ,  1024  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL
    ,  ARDUINO_RUNNING_CORE);


  xTaskCreatePinnedToCore(
    ReadTemp
    ,  "ReadTemp"   // A name just for humans
    ,  1024  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL
    ,  ARDUINO_RUNNING_CORE);

}

void loop()
{
  // Empty. Things are done in Tasks.
}

void SetupWifi(void) {
  Serial.println("Setting up AP + Captive Portal");
  WiFi.softAP("ESP32-Hub", "12345678");
  APMode = true;
  dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
  dnsServer.start(53, "*", WiFi.softAPIP());

  server.on("/generate_204", HTTP_GET, handleRequest); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
  server.on("/fwlink", HTTP_GET, handleRequest); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
  server.onNotFound(handleRequest);
  server.on("/", HTTP_GET, handleRequest);
  server.on("/save", HTTP_POST, handleRequest);
  server.begin();
  Serial.print("Portal Initialized at ");
  Serial.println(WiFi.softAPIP());
}


String toStringIp(IPAddress ip) {
  String res = "";
  for (int i = 0; i < 3; i++) {
    res += String((ip >> (8 * i)) & 0xFF) + ".";
  }
  res += String(((ip >> 8 * 3)) & 0xFF);
  return res;
}



void ReadTemp(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  for (;;) // A Task shall never return or exit.
  {

    float t = dht.readTemperature();
    if (isnan(t)) {
      Serial.println("Failed to read from DHT sensor!");
    } else {
      current_temp = t;
      Serial.print("Current Temperature ");
      Serial.println(String(t));
    }
    vTaskDelay(2000);
  }
}

void HandleGeneral(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  for (;;)
  {
    if (APMode) {
      dnsServer.processNextRequest();
    }
    if (!isConnected) {
      connectWifi();
    }
    checkWifi();
    vTaskDelay(500);
  }
}

void connectWifi() {
  if (hasWifi && isConnected == false && isConnecting == false) {
    Serial.print("connecting to ");
    Serial.println(hubSettings.ssid);
    Serial.println(hubSettings.pwd);
    isConnecting = true;
    WiFi.begin(hubSettings.ssid, hubSettings.pwd);
    //WiFi.begin(hubSettings.ssid.c_str(), hubSettings.pwd.c_str());
  }
}

void checkWifi() {
  if (WiFi.status() == WL_CONNECTED) {
    if (isConnected == false) {
      isConnected = true;
      isConnecting = false;
      onConnect();
    }
  } else if ( WiFi.status() == WL_DISCONNECTED) {
    if (isConnected == true) {
      isConnected = false;
      onDisconnect();
    }
  }
}

void onConnect() {
  Serial.println("wifi connected");
  Serial.println(WiFi.localIP());
  WiFi.enableAP(false);
  dnsServer.stop();
  APMode = false;
  vTaskDelay(100);
  SaveSettings(0,false);
  SaveSettings(1,hubSettings);
}

void onDisconnect() {
  Serial.println("wifi disconnected");

}

bool isFirstRun() {
  bool fr = true;
  ReadSettings(0, fr);
  return fr;
}



template <class T> int SaveSettings(int ee, const T& value)
{
  const byte* p = (const byte*)(const void*)&value;
  unsigned int i;
  EEPROM.begin(sizeof(value));
  for (i = 0; i < sizeof(value); i++)
    EEPROM.write(ee++, *p++);
  EEPROM.commit();
  return i;
}

template <class T> int ReadSettings(int ee, T& value)
{
  byte* p = (byte*)(void*)&value;
  unsigned int i;
  EEPROM.begin(sizeof(value));
  for (i = 0; i < sizeof(value); i++)
    *p++ = EEPROM.read(ee++);
  EEPROM.commit();
  return i;
}

I've figured out a workaround for this, posting it here just in case if anybody else is stuck here

  1. I used EEPROM.begin(512) in setup()
  2. increased the stack size of the task that is committing the EEPROM

and it seems to be working fine now

if i use EEPROM.begin in the task it still crashes

1 Like