ESP Tasks for DHT11 & webserver

Hi,

I need to monitor the temp / humidity inside a box (cctv), so have written a webserver and dht11 with littlefs. However the majority of the time the webserver is timing out, so I tried to use tasks using xTaskCreate, it still times out, so I tried setting the priority, then taskYIELD(), no joy!So I tried using xTaskCreatePinnedToCore(), but the core dumps (constantly reboots) if I try to put the server on core 0.

Any ideas / solutions please?


//  FILE SYSTEM
#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>
#define FORMAT_LITTLEFS_IF_FAILED true    //  used to install the FS the first time if need be...


//  TEMPERATURE SENSOR
#include <DHT.h>
#define DHT11_PIN  21 // ESP32 pin GPIO21 connected to DHT11 sensor
DHT dht11(DHT11_PIN, DHT11);


// WIFI
#include <WiFi.h>
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";


//  WEB SERVER
WiFiServer server(80);
String header;


//  TIMER STUFF
unsigned long currentTime = millis();
unsigned long previousTime = 0;
const long timeoutTime = 10000;


float last_humi  = 0;
float last_tempC = 0;
unsigned long dht11_timer = timeoutTime;
unsigned long diff_time = 0;



TaskHandle_t Task1Handle = NULL;
TaskHandle_t Task2Handle = NULL;


int readFile(fs::FS &fs, const char *path, int print_serial=0, WiFiClient client=0) {
  //  print_serial: 0=serial, 1=both, 2=just web
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if (!file || file.isDirectory()) {
    Serial.println("- failed to open file for reading");
    return 1;
  }

  Serial.println("- read from file:");
  while (file.available()) {

    if(print_serial<2){
      Serial.write(file.read());
    }

    if(print_serial>0){
      client.println(file.readString());
    }
  }
  file.close();
  return 0;
}

void writeFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("- failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    Serial.println("- file written");
  } else {
    Serial.println("- write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Appending to file: %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("- failed to open file for appending");
    return;
  }
  if (file.print(message)) {
    Serial.println("- message appended");
  } else {
    Serial.println("- append failed");
  }
  file.close();
}

void deleteFile(fs::FS &fs, const char * path){
    Serial.printf("Deleting file: %s\r\n", path);
    if(fs.remove(path)){
        Serial.println("- file deleted");
    } else {
        Serial.println("- delete failed");
    }
}


//void dht11_reader()
void dht11_reader(void *parameter)
{
  for (;;) {
    //*
    //xTaskYield();
    taskYIELD();
    
    // read humidity
    float humi  = dht11.readHumidity();
    //vTaskDelay(pdMS_TO_TICKS(200));
    //xTaskYield();
    taskYIELD();
    
    // read temperature in Celsius
    float tempC = dht11.readTemperature();
    //vTaskDelay(pdMS_TO_TICKS(200));
    //xTaskYield();
    taskYIELD();
    
    // read temperature in Fahrenheit
    //float tempF = dht11.readTemperature(true);
  
    // check whether the reading is successful or not
    //if ( isnan(tempC) || isnan(tempF) || isnan(humi)) {
    if ( isnan(tempC) || isnan(humi)) {
      Serial.println("Failed to read from DHT11 sensor!");
    } else {
      Serial.print("Humidity: ");
      Serial.print(humi);
      Serial.print("%");
  
      Serial.print("  |  ");
  
      Serial.print("Temperature: ");
      Serial.print(tempC);
      Serial.println("°C  ~  ");
      //Serial.print(tempF);
      //Serial.println("°F");
  
      char buffer[64];
      int ret = snprintf(buffer, sizeof buffer, "%.2f,%.2f\n", tempC, humi);
  
      //appendFile(LittleFS, "/log.txt", "World!\r\n");
      appendFile(LittleFS, "/log.txt", buffer);
      //appendFile(LittleFS, "/log.txt", "\n");
      
      last_humi  = humi;
      last_tempC = tempC;
    }
    //*/
    //vTaskDelay(10000 / portTICK_PERIOD_MS);
    vTaskDelay(pdMS_TO_TICKS(30000));
  }
}


void webserve(void *parameter)
{
  for (;;) {
    //*
    //Serial.println("Check Client.");
    WiFiClient client = server.available();   // Listen for incoming clients
  
    if (client) {                             // If a new client connects,
      Serial.println("New Client.");
  
  
      client.println("HTTP/1.1 200 OK");
      client.println("Content-type:text/html");
      //client.println("Connection: close");
      client.println();
  
      // Display the HTML web page
      client.println("<!DOCTYPE html><html>");
      client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
  
      client.println("<body><h1>ESP32 Web Server</h1>");
  
      client.println("Temperature: ");
      client.println(last_tempC);
      client.println(" C<br />");
      
      client.println("Humidity: ");
      client.println(last_humi);
      client.println(" %<br />");
  
      client.println("<pre>");
      readFile(LittleFS, "/log.txt", 1, client);
      client.println("</pre><br />");
      
      client.println("</body></html>");
      
      // The HTTP response ends with another blank line
      client.println();
  
      // Close the connection
      client.stop();
      Serial.println("Client disconnected.");
      Serial.println("");
    }
    //*/
    //vTaskDelay(pdMS_TO_TICKS(200));
    taskYIELD();
  }
}


void setup() {
  // put your setup code here, to run once:

  //  SERIAL
  Serial.begin(115200);
  while (!Serial)
  {
    ; //  SERIAL: wait USB port
  }
  Serial.println("\n\nINIT\n");


  //  FILE SYSTEM
  if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
    Serial.println("LittleFS Mount Failed");
    return;
  }
  //writeFile(LittleFS, "/log.txt", "Hello ");
  //appendFile(LittleFS, "/log.txt", "World!\r\n");
  //readFile(LittleFS, "/log.txt");

  deleteFile(LittleFS, "/log.txt");
  
  if (readFile(LittleFS, "/log.txt")){
    Serial.println("Log not exist!");
    writeFile(LittleFS, "/log.txt", "INIT\n");
  }
  //  RESET
  //writeFile(LittleFS, "/log.txt", "INIT\n");
  
  
  //  WIFI
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  WiFi.setTxPower(WIFI_POWER_8_5dBm);     //    ??? on an ESP32C3 Super Mini, kept getting: _onStaArduinoEvent(): Reason: 2 - AUTH_EXPIRE
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();


  //  TEMPERATURE
  dht11.begin(); // initialize the DHT11 sensor
  delay(200);

  xTaskCreate(dht11_reader, "Blink1", 2048*5, NULL, 1, NULL);
  xTaskCreate(webserve, "Blink2", 2048, NULL, 1, NULL);
  /*
  xTaskCreatePinnedToCore(
    dht11_reader,             // Task function
    "Task1",           // Task name
    10000,             // Stack size (bytes)
    NULL,              // Parameters
    1,                 // Priority
    &Task1Handle,      // Task handle
    1                  // Core 1
  );

  xTaskCreatePinnedToCore(
    webserve,            // Task function
    "Task2",          // Task name
    10000,            // Stack size (bytes)
    NULL,             // Parameters     
    2,                // Priority
    &Task2Handle,     // Task handle
    1                 // Core 0
  );
  */
  
}

void loop() {
  // put your main code here, to run repeatedly:

}

Maybe look at some WebServer examples in the Arduino IDE. You never call handleClient() on the server object.

Interesting, it isn't how I used to do it, I basically hacked a quick demo from Rui Santos for this...

Thanks

NOTE:

I think I blew the board so am just testing with another board using just the webserver code and even though better it is still giving a blank page often... So very possibly right on there!

Yea, you could be right. handleClient() may only be needed when you use the "server.on..." callbacks.

I believe it was the webserver, thankyou!


//  FILE SYSTEM
#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>
#define FORMAT_LITTLEFS_IF_FAILED true    //  used to install the FS the first time if need be...


//  TEMPERATURE SENSOR
#include <DHT.h>
#define DHT11_PIN  21 // ESP32 pin GPIO21 connected to DHT11 sensor
DHT dht11(DHT11_PIN, DHT11);


// NTP
#include <time.h>
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
//Worldwide  pool.ntp.org
//Asia  asia.pool.ntp.org
//Europe  europe.pool.ntp.org
//North America north-america.pool.ntp.org
//Oceania oceania.pool.ntp.org
//South America south-america.pool.ntp.org

// See list of timezone strings https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
//const char* tzInfo = "EST5EDT,M3.2.0,M11.1.0";
const char* tzInfo = "GMT0BST,M3.5.0/1,M10.5.0";  //  Europe/London


// WIFI
#include <WiFi.h>
#include <WebServer.h>
//const char* ssid = "YOUR_SSID";
//const char* password = "YOUR_PASS";


//  WEB SERVER
WebServer server(80);


//  CONFIG
long check_timeout = 30000;

//  VARS
float last_humi  = 0;
float last_tempC = 0;


String readFile2(fs::FS &fs, const char *path) {
  String s = "";
  
  File file = fs.open(path);
  if (!file || file.isDirectory()) {
    Serial.println("- failed to open file for reading");
    return s;
  }
  
  while (file.available()) {
    s += file.readString();
  }
  file.close();
  return s;
}

void writeFile(fs::FS &fs, const char *path, const char *message) {
  //Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("- failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    //Serial.println("- file written");
  } else {
    //Serial.println("- write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char *path, const char *message) {
  //Serial.printf("Appending to file: %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("- failed to open file for appending");
    return;
  }
  if (file.print(message)) {
    //Serial.println("- message appended");
  } else {
    //Serial.println("- append failed");
  }
  file.close();
}

void deleteFile(fs::FS &fs, const char * path){
    //Serial.printf("Deleting file: %s\r\n", path);
    if(fs.remove(path)){
        //Serial.println("- file deleted");
    } else {
        //Serial.println("- delete failed");
    }
}

void syncTime() {
  // Start NTP using the two servers above
  configTime(0, 0, ntpServer1, ntpServer2);

  // Set the timezone for your region
  setenv("TZ", tzInfo, 1);
  tzset();

  // Wait until a valid time is received from the NTP server
  // 1577836800 is the Unix time for Jan 1, 2020
  time_t now = 0;
  while (time(&now) < 1577836800) {
    delay(500);
  }
}

String get_time() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo, 2000)) {
    Serial.println("Failed to obtain time");
    return "";
  }
  char formattedTime[80];  // Buffer to store the formatted string
  strftime(formattedTime, sizeof(formattedTime), "%A_%B_%d_%Y_%H:%M:%S", &timeinfo);   //  Friday_May_22_2026_00:14:37
  //Serial.println(formattedTime);
  return formattedTime;
}


void dht11_reader(void *parameter)
{
  for (;;) {
    taskYIELD();
    
    // read humidity
    float humi  = dht11.readHumidity();
    taskYIELD();
    
    // read temperature in Celsius
    float tempC = dht11.readTemperature();
    taskYIELD();
    
    //float tempF = dht11.readTemperature(true);  //  Fahrenheit
  
    // check whether the reading is successful or not
    //if ( isnan(tempC) || isnan(tempF) || isnan(humi)) {
    if ( isnan(tempC) || isnan(humi)) {
      Serial.println("Failed to read DHT11 sensor!");
    } else {
      Serial.print("Humidity: ");
      Serial.print(humi);
      Serial.print("%");
  
      Serial.print("  |  ");
  
      Serial.print("Temperature: ");
      Serial.print(tempC);
      Serial.println("°C  ~  ");
      //Serial.print(tempF);
      //Serial.println("°F");


      String stime = get_time();
      
      char buffer2[stime.length() + 1];
      stime.toCharArray(buffer2, stime.length() + 1);
      
      //  STORE
      char buffer[64];
      int ret = snprintf(buffer, sizeof buffer, "%.2f,%.2f,%s\n", tempC, humi, buffer2);
      appendFile(LittleFS, "/log.txt", buffer);

      //  TRACK ?????
      last_humi  = humi;
      last_tempC = tempC;
    }
    vTaskDelay(pdMS_TO_TICKS(check_timeout));
  }
}


void setup() {

  //  SERIAL
  Serial.begin(115200);
  while (!Serial)
  {
    ; //  SERIAL: wait USB port
  }
  Serial.println("\n\nINIT\n");


  //  FILE SYSTEM
  if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
    Serial.println("LittleFS Mount Failed");
    return;
  }

  deleteFile(LittleFS, "/log.txt");     //  TEMPOARY RESET
  if (readFile2(LittleFS, "/log.txt")==""){
    Serial.println("Log not exist!");
    writeFile(LittleFS, "/log.txt", "INIT\n");
  }
  
  
  //  WIFI
  Serial.print("Connecting...");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  WiFi.setTxPower(WIFI_POWER_8_5dBm);     //    ??? on an ESP32C3 Super Mini, kept getting: _onStaArduinoEvent(): Reason: 2 - AUTH_EXPIRE
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());


  //  WEBSERVER
  server.on("/", handle_OnConnect);
  server.onNotFound(handle_NotFound);
  server.begin();
  Serial.println("HTTP server started");


  //  NTP
  syncTime();
  Serial.println(get_time());
  

  //  TEMPERATURE
  dht11.begin(); // initialize the DHT11 sensor
  delay(200);
  xTaskCreate(dht11_reader, "Blink1", 2048*5, NULL, 1, NULL);
}

void loop() {
  server.handleClient();
}

void handle_OnConnect() {
  server.send(200, "text/html", createHTML());
}

void handle_NotFound() {
  server.send(404, "text/plain", "Not found");
}

String createHTML() {
  String str = "<!DOCTYPE html> <html>";
  str += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">";
  str += "<style>";
  str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
  str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
  str += ".current {font-size: 20px; font-weight: 300; line-height: 1; margin: 0px; color: #4285f4;}";
  str += "</style>";
  str += "</head>";
  str += "<body>";
  str += "<h1 class=\"title\">TEMERATURE & HUMIDITY</h1>";

  //  CURRENT
  str += "<div class=\"current\">";
  str += "Temperature: " + String(last_tempC) + " C<br />";
  str += "Humidity: " + String(last_humi) + " %<br />";
  str += "</div>";

  //  LOG
  String s = readFile2(LittleFS, "/log.txt");
  str += "<div class=\"temps\"><pre>";
  str += s;
  str += "</pre></div>";
  
  str += "</body>";
  str += "</html>";
  return str;
}

It was working fine, then it wasn't... Totally stopped serving for Firefox, but sometimes serves to curl... but only when plugged into the laptop... even though nmap sees the IP but not the port (except when plugged into the laptop) :roll_eyes: :face_with_symbols_on_mouth: :shaking_face: :face_with_spiral_eyes: :distorted_face: :weary_face: :sob:

So, DuckDuck AI says... :goose: :goose: :soon_arrow:

The ESP32-C3 does not support the WebServer.h library as it is primarily designed for the ESP32 WROOM series. Instead, consider using the ESPAsyncWebServer.h library, which is more suitable for the ESP32-C3.

Tomorrow is another day...

Couldn't sleep, grrrr...

My problem was probably either !Serial causing it to hang once off USB, or me upping the tx power, but also went for the Async server too...

:laughing:

Hi,

I would try to simplify this and avoid running the webserver in a separate FreeRTOS task. Have you tried running this project in a baremetal setup, without FreeRTOS? It could be an issue either with webserver or the sensor or filesystem.

You could also try this alternative below based on the Mongoose webserver. That is to rule out any issues with the webserver. I took your DHT11/LittleFS behaviour and replaced your webserver logic with Mongoose, from this example which was tested and proven to work. You would need to add the 3 mongoose files from the linked example for it to compile. Let me know if this works for you!

#include "mongoose.h"

#include "WiFi.h"

#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>
#include <DHT.h>

#define FORMAT_LITTLEFS_IF_FAILED true

#define DHT11_PIN 21
DHT dht11(DHT11_PIN, DHT11);

#define WIFI_SSID "YOUR_SSID" // Change this
#define WIFI_PASS "YOUR_PASS" // And this

struct mg_mgr mgr;

float last_humi  = 0;
float last_tempC = 0;
unsigned long dht11_timer = 10000;

void writeFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("- failed to open file for writing");
    return;
  }

  if (file.print(message)) {
    Serial.println("- file written");
  } else {
    Serial.println("- write failed");
  }

  file.close();
}

void appendFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Appending to file: %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("- failed to open file for appending");
    return;
  }

  if (file.print(message)) {
    Serial.println("- message appended");
  } else {
    Serial.println("- append failed");
  }

  file.close();
}

void deleteFile(fs::FS &fs, const char * path) {
  Serial.printf("Deleting file: %s\r\n", path);

  if (fs.remove(path)) {
    Serial.println("- file deleted");
  } else {
    Serial.println("- delete failed");
  }
}

void readFile(struct mg_connection *c, fs::FS &fs, const char *path) {
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if (!file || file.isDirectory()) {
    Serial.println("- failed to open file for reading");
    mg_printf(c, "failed to open file for reading\n");
    return;
  }

  while (file.available()) {
    mg_printf(c, "%c", file.read());
  }

  file.close();
}

void dht11_reader(void) {
  if (millis() - dht11_timer < 30000) return;
  dht11_timer = millis();

  float humi  = dht11.readHumidity();
  float tempC = dht11.readTemperature();

  if (isnan(tempC) || isnan(humi)) {
    Serial.println("Failed to read from DHT11 sensor!");
  } else {
    Serial.print("Humidity: ");
    Serial.print(humi);
    Serial.print("%");

    Serial.print("  |  ");

    Serial.print("Temperature: ");
    Serial.print(tempC);
    Serial.println("°C  ~  ");

    char buffer[64];
    snprintf(buffer, sizeof buffer, "%.2f,%.2f\n", tempC, humi);

    appendFile(LittleFS, "/log.txt", buffer);

    last_humi  = humi;
    last_tempC = tempC;
  }
}

void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
  if (ev == MG_EV_HTTP_MSG) {
    struct mg_http_message *hm = (struct mg_http_message *)ev_data;

    if (mg_match(hm->uri, mg_str("/api/dht11"), NULL)) {
      mg_http_reply(c, 200, "", "{%m: %.2f, %m: %.2f}\n",
                    MG_ESC("temperature"), last_tempC,
                    MG_ESC("humidity"), last_humi);

    } else if (mg_match(hm->uri, mg_str("/log.txt"), NULL)) {
      mg_printf(c,
                "HTTP/1.1 200 OK\r\n"
                "Content-Type: text/plain\r\n"
                "\r\n");

      readFile(c, LittleFS, "/log.txt");

    } else {
      mg_printf(c,
                "HTTP/1.1 200 OK\r\n"
                "Content-Type: text/html\r\n"
                "\r\n");

      mg_printf(c, "<!DOCTYPE html><html>");
      mg_printf(c, "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
      mg_printf(c, "</head>");

      mg_printf(c, "<body><h1>ESP32 Web Server</h1>");

      mg_printf(c, "Temperature: ");
      mg_printf(c, "%.2f", last_tempC);
      mg_printf(c, " C<br />");

      mg_printf(c, "Humidity: ");
      mg_printf(c, "%.2f", last_humi);
      mg_printf(c, " %%<br />");

      mg_printf(c, "<pre>");
      readFile(c, LittleFS, "/log.txt");
      mg_printf(c, "</pre><br />");

      mg_printf(c, "</body></html>");
    }
  }
}

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(50);
  Serial.println("\n\nINIT\n");
  if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
    Serial.println("LittleFS Mount Failed");
    return;
  }
  deleteFile(LittleFS, "/log.txt");
  writeFile(LittleFS, "/log.txt", "INIT\n");
  dht11.begin();
  delay(200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  WiFi.setTxPower(WIFI_POWER_8_5dBm);
  while(WiFi.status() != WL_CONNECTED) Serial.print("."), delay(100);
  Serial.println("\nConnected to the WiFi network");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  mg_mgr_init(&mgr);
  mg_log_set(MG_LL_DEBUG);
  mg_log_set_fn([](char ch, void *) { Serial.print(ch); }, NULL);
  mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
}

void loop() {
  dht11_reader();
  mg_mgr_poll(&mgr, 0);
}

extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) __attribute__((weak));

extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) {
  if (ip6_addr_isany_val(inp->ip6_addr[0].u_addr.ip6)) {
    pbuf_free(p);
    return 1;
  }

  return 0;
}

There is no "bare metal" setup for ESP32 in the Arduino ecosystem. FreeRTOS is always running. The standard Arduino setup() and loop() functions run in the loopTask.

I would translate the question into : "have you tried the simple webserver example" and see if that works.

On a different note.

There is always a chance that the HTML code just isn't correct.
Then there is the memory management.
In general, functions that return a String run the risk of fragmenting the heap, though that should normally not cause issues straight away.

String createHTML() {
  String str = "<!DOCTYPE html> <html>";
  str += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">";
  str += "<style>";
  str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
  str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
  str += ".current {font-size: 20px; font-weight: 300; line-height: 1; margin: 0px; color: #4285f4;}";
  str += "</style>";
  str += "</head>";
  str += "<body>";
  str += "<h1 class=\"title\">TEMERATURE & HUMIDITY</h1>";

  //  CURRENT
  str += "<div class=\"current\">";
  str += "Temperature: " + String(last_tempC) + " C<br />";
  str += "Humidity: " + String(last_humi) + " %<br />";
  str += "</div>";

  //  LOG
  String s = readFile2(LittleFS, "/log.txt");
  str += "<div class=\"temps\"><pre>";
  str += s;
  str += "</pre></div>";
  
  str += "</body>";
  str += "</html>";
  return str;
}`

As it is built in to the latest core, i have to disagree.

The solution is not in over-complicating the issue.

Maybe GitHub - BojanJurca/Multitasking-Http-Ftp-Telnet-Ntp-Smtp-Servers-and-clients-for-ESP32-Arduino-Library: Multitasking HTTP, FTP, Telnet, NTP, SMTP servers and clients for ESP32 - Arduino library · GitHub can help you? Multitasking is already supported and you can set timeouts on your own.