Esp32 server with bounded resources

By | 2018-02-17

If you have a web server set up in your ESP32 device, you might want to add resources like images or java-scripts, that are referenced from your web documents.

I can imagine, that a quite few people does not know, that ESP32 (and ESP8266 as well) has a built in file storage area in the flash memory. For a web server context, the idea is that you can upload your binary content into this file area, where your web server can forward these files to the client requests.

You can access files in this area with the SPIFFS library.

This is an example as matters stands in February 2018.

#include <FS.h>;
#include <SPIFFS.h>;
#include <WiFi.h>;
#include <WiFiClient.h>;

#define BUF_LEN 512

const char* ssid = "Free WiFi";
const char* password = "***for connecting***";

// TCP server at port 80 will respond to HTTP requests
WiFiServer server(80);

// Our local FS will be the internal SPIFFS of the esp32
fs::FS localFs = SPIFFS;
static uint8_t buf[BUF_LEN];

void setup(void)
{
  Serial.begin(115200);

  // Set up SPIFFS
  if (!SPIFFS.begin())
  {
    Serial.println("SPIFFS Mount Failed");
  }

  setupWifi();

  // Start TCP (HTTP) server
  server.begin();
  Serial.println("TCP server started");
}

void setupAP()
{
  // Set up access point
  WiFi.softAP(ssid);

  Serial.print("Created ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());
}

void setupWifi()
{
  // Connect to WiFi network
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop(void)
{
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client)
  {
    return;
  }
  Serial.println("");
  Serial.println("New client");

  // Wait for data from client to become available
  while (client.connected() &amp;&amp; !client.available())
  {
    delay(1);
  }

  // Read the first line of HTTP request
  String req = client.readStringUntil('\r');

  // First line of HTTP request looks like "GET /path HTTP/1.1"
  // Retrieve the "/path" part by finding the spaces
  int addr_start = req.indexOf(' ');
  int addr_end = req.indexOf(' ', addr_start + 1);
  if (addr_start == -1 || addr_end == -1)
  {
    Serial.print("Invalid request: ");
    Serial.println(req);
    return;
  }
  req = req.substring(addr_start + 1, addr_end);
  Serial.print("Request: ");
  Serial.println(req);
  client.flush();

  String s;
  if (req == "/")
  {
    IPAddress ip = WiFi.softAPIP();
    String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
    s = "HTTP/1.1 200 OK\r\n";
    s += "Content - Type: text / html\r\n";
    s += "\r\n"; // End of header block
    s += "<!DOCTYPE HTML>\r\n";
    s += "<html>Hello from ESP32";
    s += "<img src='/sample.jpg'/>";
    s += "</html>\r\n\r\n";
    Serial.println("Sending 200");
  }
  else
  {
    Serial.print("Sending ");
    Serial.println(req);

    // Open file from FS
    File file = localFs.open(req);
    if(!file || file.isDirectory()){
      // File not found
      Serial.println("Sending 404");
      s = "HTTP / 1.1 404 Not Found\r\n";
      s += "\r\n"; // End of header block
      s += "Resource not found\r\n";
    }
    else
    {
      int toSend = file.size();
      Serial.print("Reading bytes ");
      Serial.println(toSend);
      client.println("HTTP / 1.1 200 OK");
      // TODO: Handle content type
//      client.println("Content - Type: image / jpeg");
      client.print("Content - Length: ");
      client.println(toSend);
      client.println(); // End of header block
      // Copy file content to client via buffer
      while(toSend > 0)
      {
        size_t len = toSend > BUF_LEN ? BUF_LEN : toSend;
        file.read(buf, len);
        client.write(buf, len);
        toSend -= len;
        Serial.print(".");
      }
      file.close();
      s = ""; // Request already served no more lines to send.
    }
  }

  // Send content to client
  client.print(s);
  Serial.println("Done with client");
}

As you can see in the code we handle unidentified URLs as resources can be found on the file system.

To upload files to this flash area you will need to use the “Arduino ESP32 filesystem uploader” from https://github.com/me-no-dev/arduino-esp32fs-plugin

The idea is that you have “data” folder inside your current sketch folder, and you can upload all files in this folder with the tool.

I have personally faced with some issues around this tool, as some files was not uploaded correctly to the flash with this tool.