Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

ESP32 WEB Server with Asynchronous Technology and Internal File System

3.81/5 (15 votes)
16 Jun 2022CPOL5 min read 31.7K   715  
A work for exploring the Asynchronous WEB Server, the SPIFFS File system and the exploitation of the dual core
This work is my insight into the ESP32 board and deals with a creation of a not complex but non trivial application where an Asynchronous WEB Server can produce pages based on an internal File system and the control of the application by serial link runs in separate core.

Introduction

In the article is highlighted the ease with which one can create a complex application using the Asynchronous WEB Server, how and where to store the necessary files (JavaScript sources, style sheets, images, ...) and how to take advantage of the dual core board.

The WEB Server can be accessed by any device that has a browser, this application, in particular, was created with cell phone access in mind.

You can also get the sources from my site.

Background

The user must be practical in using Arduino IDE and programming in C ++.

The article assumes that the user has installed some components, however in a dedicated paragraph, it can find the necessary references to be able to use the application.

The user accesses the application which sends to the browser a form in which it is possible to change the time on the board, request the humidity and temperature values (DHT22 sensor)1) and view the contents of the File system.

Note for Arduino IDE

If the IDE isn't able to associate COM port this means the lack of the appropriate driver, below the procedure I followed to solve.

  • I have identified the USB to serial converter chip on my ESP32 board:
    • on Windows device manager appears under Other devices the chip name, (my is CP2102 USB to UART Bridge Controller),
    • I downloaded the .zip file that contains the chip driver from the manufacturer's website;
  • I installed the driver acting with the right mouse button on the silabser.inf file and choosing install.

Using the Code

The source included is a folder with all data needed. When it is installed on ESP32 board, you must activate the Sermig_Condor WiFi service and access the IP address 192.168.1.1 by a browser.

Eventually, some commands can be sent to the board via the serial port.

The main components are:

  • The asynchronous WEB Server
  • The SPIFFS (SPI Flash File Storage) file system is hosted in Flash memory. The File System contains all files needed to the application:
    • index.html
    • formgen.js a script for generating forms (see my article A JavaScript Form Generator)
    • formgen.css a style sheet for the generated form
    • image files
  • The use of both ESP32 board processor cores.

WEB Server Creation and Behavior

C++
...
#include <WiFi.h>
#include <AsyncTCP.h>
#include "ESPAsyncWebServer.h"
...
// SSID & Password ******************************************************
const char* ssid = "Sermig_Condor";
const char* password = "";
// IP Address ***********************************************************
IPAddress local_ip(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
AsyncWebServer server(80);  // Object of WebServer(HTTP port, 80 is default)
---
void setup() {
  ...
  WiFi.softAP(ssid, password);
  delay(2000);  // to avoid crash on WiFi connection
  WiFi.softAPConfig(local_ip, gateway, subnet);
  Serial.printf("Connect to Access Point: %s\n",ssid);
  server.on("/", HTTP_ANY, [](AsyncWebServerRequest *request) {
  ...
  });
  server.onNotFound([](AsyncWebServerRequest *request){request->send(404);});
  server.serveStatic("/", SPIFFS, "/");
  server.begin();
  ...
}
void loop() {
    // this can be empty
}

In the above fragment, note the required sources, the setting of the access parameters (name, IP address, etc.) and the processing of requests.

The dealing of the requests is on:

  • server.on(... where the first parameters is the resource request in the example is the root, the second parameter tells what type of request is accepted, HTTP_ANY is both HTTP_GET and HTTP_PUT. This means that the eventual parameters of a request can be in the inline form ?key=value as in a submitted form; the third parameter is the function which will process the request. server.on must be present for each request that needs special processing.
  • server.onNotFound(... for deals with unknown resource requested.
  • server.serveStatic("/", SPIFFS, "/"); this tells WEB Server to send the files request by the browser that are contained in the SPIFFS Filesystem such as images, style sheets, JavaScript sources, etc. This means we don't have to deal with mime types of files requested.

The choice for interacting with the board has been to put the request directly after the address:

C++
server.on("/", HTTP_ANY, [](AsyncWebServerRequest *request) {
 if(request->hasArg("fnz")) {
   String type = request->arg("fnz");
   AsyncResponseStream *response = request->beginResponseStream("text/html");
   if (type == "Dir") {           // send Filesystem contents
      response->print(dir());
      request->send(response);
   } else if (type == "Clock") {  // set an internal Clock
      setTime(toSeconds(request->arg("time").c_str()));
      response->printf("New time: %s", getTime());
      request->send(response);
   } else if (type == "Temp") {   // get humidity and temperature
      response->printf("%s %s",getTime(),getTemperature());
      request->send(response);
   }
  } else request->send(SPIFFS, "/index.html");  // send the form

The fragment above handles the request that has the form /[GET data] the object request exposes methods for getting the parameters and for sending the responses, if no parameters are found, it is sent the initial page, i.e., the file index.html; so the requests and responses can be:

192.168.1.1 The form is sent (index.html)
192.168.1.1/fnz=Clock&time=hh:mm:ss An internal clock is updated
192.168.1.1/fnz=Dir The content of Filesystem is sent
192.168.1.1/fnz=Temp The humidity and temperature values are sent

The data for the requests are sent as plain text but can contain HTML tags like the function below that shows the Filesystem contents:

C++
String dir(void) {
   String x = "<table>";
   File root = SPIFFS.open("/");
   if(!root) {
        return "Failed to open directory";
    }
    if(!root.isDirectory()) {
        return "Not a directory";
    }
    File file = root.openNextFile();
    while(file) {
         x += "<tr><td>"+String(file.name());
        if(file.isDirectory()){
          x += "<td>DIR";
        } else {
          x += "<td style='text-align:right'>"+String(file.size());
        }
        file = root.openNextFile();
    }
    x += "<tr><td>Occupied space<td style='text-align:right'>"+String(SPIFFS.usedBytes());
    x += "<tr><td>Total space<td style='text-align:right'>"+String(SPIFFS.totalBytes());
    return x+"</table>";
}

It is possible to interact with the board by Serial link sending the commands:

  • d[ir] for displaying the contents of the FileSystem
  • r resets the board
  • t for seeing humidity and temperature

The serial listener runs in core 0, in the fragment below, it is shown how this is realized.

C++
void serialListener( void * pvParameters ) {  // this runs in core 0
  Serial.print("Serial Listener running on core ");
  Serial.println(xPortGetCoreID());
  delay(100);
  for(;;){
    while (Serial.available() > 0) {
      char cmd = Serial.read() | 0b00100000;
      switch (cmd) {
        case 100:         // d[ir]
          dir_ls();
          break;
        case 114:         // r reset Board
          Serial.print("\nReset board\n");
          delay(200);
          ESP.restart();
          break;
        case 116:         // t get temperature/humidity
          sprintf(workBuffer, "%s %s", getTime(),getTemperature());
          Serial.println(workBuffer);
          break;
      }
    }
    vTaskDelay(150 / portTICK_PERIOD_MS);   // 150 millisecond
  }
}
void setup() { 
...
  xTaskCreatePinnedToCore (
      serialListener,     /* Function to implement the task */
      "WiFi-WEBServer",   /* Name of the task */
      10000,              /* Stack size in words */
      NULL,               /* Task input parameter */
      1,                  /* Priority of the task */
      NULL,               /* Task handle. */
      0);                 /* Core where the task should run */
}

Notes on Components

Asynchronous WEB Server

Documentation: https://github.com/me-no-dev/ESPAsyncWebServer

We need the two libraries below:

and unzip under the Arduino libraries folder taking care to delete the suffix -master from the folder name.

SPIFFS Filesystem

SPIFFS Filesystem is useful but has certain limitations see here the documentation.

  • It has a flat structure, i.e., it does not support directories.
  • For now, it does not detect or handle bad blocks.
  • The flash memory is limited to 10,000 write cycles.
  • File name with extension is limited to 31 characters.

Installation (from this tutorial):

  • Create a folder named data in the sketch folder where put the "site" files.
  • Download the file ESP32FS-1.0.zip.
  • Unzip the downloaded .zip folder to the Arduino Tools folder (if is locate in ...\ArduinoData\packages and if it not exists must be created).

If the installation is successful in the IDE menu Tools appears the item ESP32 Sketch Data Upload that it permits to upload the contents of the folder data into the SPIFFS File system.2)

We can see the File system dimension by Tools −> Partition Scheme.

Conclusion

The Asynchronous WEB Server together with a File system allows to build non-trivial applications where the developer only has to worry about the logic of the application.

Notes

1) The application however can run without this sensor.
2) The upload uses the serial port so close any serial monitor.

History

  • 27th April, 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)