ESP8266 SPIFFs | File System for NodeMCU

ESP8266 NodeMCU SPIFFS

SPIFFS or Serial Peripheral Interface Flash File System is a system that utilizes extra flash memory on the ESP8266 and ESP32 for storing files. It’s so named because the user reads, writes or deletes files in the memory using the serial interface. In this article, I will show you how to set up SPIFFS for your NodeMCU or WeMos D1 Mini board. Also, an example is presented on how to take advantage of this feature.

Video Tutorial

Why Use SPIFFS?

In my NodeMCU web server article, I used a sketch that presents a simple web page for controlling an external LED.

old web server sketch

When this sketch is uploaded to an ESP8266 board, the server page is now viewable in the local network through the device's IP address. This how to server page looks like on a desktop browser:

old web server page

Clicking the ON button turns on the LED while the OFF button turns off the LED. It looks super simple because the HTML for this page is just this:

<h1>Simple NodeMCU Web Server</h1>
<p>
  <a href=\"LEDOn\"><button>ON</button></a>
  <a href=\"LEDOff\"><button>OFF</button></a>
</p>

A much better looking web page is one with styling, images or scripts. We could do that but we are always mindful of the memory limitations of the ESP8266. Defining a long HTML in sketch means using a long string which jumps from one memory location to another for every compilation. This proves an inefficient memory management scheme and more likely to crash your ESP8266.

The best solution is to store the page files outside of the sketch memory location. The ESP8266 flash memory has the following allocations for the Arduino platform:

ESP8266 memory map

Note that the File System and EEPROM size may vary as per user allocation. You can use EEPROM to store data but it still limited. The SPIFFS (File System) size, in contrast, is up to 3 MB. That’s 75% of the total flash size of the ESP8266.

Setting up SPIFFS

To start using SPIFFS, download this file: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.5.0/ESP8266FS-0.5.0.zip

Inside this zip file is a folder named ESP8266FS. Extract this to /tools inside your Arduino sketchbook location. Create the tools folder if you don’t have one. By default, the sketchbook location is at Documents/Arduino for Windows and Mac or /home/Sketchbook for Linux.

After this, open your Arduino IDE. A new option should appear in Tools:

New tool: ESP8266 Sketch data upload

This tool allows you to add files to your device’s SPIFFS. To add the files, open or create a sketch and place a data folder inside the sketch folder. For example, if I would add an HTML file, that html file is inside a data folder in the same location as my .ino file.

Data folder for SPIFFS

To store the contents of the data folder to SPIFFS, just click the Tools > ESP8266 Sketch Data Upload. Of course you’ll still need to upload the sketch to your NodeMCU because that’s in another location.

In addition, you can specify how much memory you want to allocate to SPIFFS. This is one of the options as you select your ESP8266 board type:

Select SPIFFS size

Modifying the Simple Web Server Page

Now that we have SPIFFS set up, we modify the old HTML to make it more pleasing. I created three HTML pages: index.html, LEDOn.html, LEDOff.html. I also added some images and a css file for styling. These are all the contents of my data folder:

Data files for New Web Server page

When writing HTML, it’s best to code it on text-editor first and view it on a local browser. This way, you can finalize what the pages look like before uploading them to SPIFFS. Here’s my index.html file:

<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link rel='stylesheet' href='style.css'> 
</head>
<body>
      <h1>ESP8266 Web Server with SPIFFS</h1>
      <p><a href='https://www.teachmemicro.com/esp8266-spiffs-web-server-nodemcu/'>Full tutorial</a></p>
      <p> Example showing how to host a web server using ESP8266 Flash File System. This controls the on-board LED. Click button to begin.</p>
      <a href="LEDOff.html"><button id='start-button'>START!</button></a>
</body>
</html>

This is the first file the client reads on the server.

Here’s the LEOn and LEDOff html files:

html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link rel='stylesheet' href='style.css'> 
</head>
<body>
      <h1>ESP8266 Web Server with SPIFFS</h1>
      <p><a href='https://www.teachmemicro.com/esp8266-spiffs-web-server-nodemcu/'>Full tutorial</a></p>
      <a href="LEDOff.html"><img id="LED-ON" src="led-on-img.jpg"></a>
</body>
</html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link rel='stylesheet' href='style.css'> 
</head>
<body>
      <h1>ESP8266 Web Server with SPIFFS</h1>
      <p><a href='https://www.teachmemicro.com/esp8266-spiffs-web-server-nodemcu/'>Full tutorial</a></p>
      <a href="LEDOn.html"><img id="LED-OFF" src="led-off-img.jpg"></a>
</body>
</html>

Here's my CSS file:

body{
  background-color:white;
  font-family: 'Roboto', sans-serif;
  border: 1px solid;
  border-color: gray;
  border-radius: 20px;
  margin-left: 35%;
  width: 30%;
  height: 50%;
  padding: 20px;
}

#LED-ON, #LED-OFF{
  margin-left: 25%;
}

#start-button{
  width: 40%;
  height: 20%;
  background-color: darkseagreen;
  border: 1px solid forestgreen;
  border-radius: 10px;
  margin-left: 30%;
  margin-top: 10%;
}

The two images are for showing on and off bulbs:

All in all these files are around 56 kB. Still too small for our 3 MB SPIFFS!

As for the sketch, there are also some changes to the original.

/*

ESP8266 WiFi Server with SPIFFS
by Roland Pelayo

Full tutorial: https://www.teachmemicro.com/esp8266-spiffs-web-server-nodemcu/

Rev 1.0 - Initial code - September 19, 2020

*/

//Declare libraries
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <FS.h>

//provide your own WiFi SSID and password
const char* ssid = "<Your WiFi SSID>";
const char* password = "<Your WiFi Password>";

//Create WebServer instance
ESP8266WebServer server(80);

void setup() {
  //Setup on-board LED
  pinMode(2, OUTPUT);
  //For debugging
  Serial.begin(115200);
  //Initiate WiFi Connection
  WiFi.begin(ssid, password);
  Serial.println("");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  //Print your WiFi's SSID (might be insecure)
  Serial.println(ssid);
  Serial.print("IP address: ");
  //Print your local IP address (needed for browsing the app)
  Serial.println(WiFi.localIP());

  if(!SPIFFS.begin())
  {
    // Serious problem
    Serial.println("SPIFFS Mount failed");
  } else {
    Serial.println("SPIFFS Mount succesfull");
  }

  server.onNotFound([]() { // If the client requests any URI
    if (!handleFileRead(server.uri())) // send it if it exists
      server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error
  });

  //start web server
  server.begin();
  //Just stating things
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient(); //make the ESP32 respond to web clients
}

bool handleFileRead(String path) { // send the right file to the client (if it exists)
  Serial.println("handleFileRead: " + path);
  if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file
  String contentType = getContentType(path); // Get the MIME type
  if (SPIFFS.exists(path)) { // If the file exists
    File file = SPIFFS.open(path, "r"); // Open it
    size_t sent = server.streamFile(file, contentType); // And send it to the client
    if(path == "/LEDOn.html"){
      digitalWrite(2, LOW);
    }else if(path == "/LEDOff.html"){
      digitalWrite(2, HIGH);
    }
    file.close(); // Then close the file again
    return true;
  }
  Serial.println("\tFile Not Found");
  return false; // If the file doesn't exist, return false
}

String getContentType(String filename){
  if(filename.endsWith(".htm")) return "text/html";
  else if(filename.endsWith(".html")) return "text/html";
  else if(filename.endsWith(".css")) return "text/css";
  else if(filename.endsWith(".jpg")) return "image/jpeg";
  return "text/plain";
}

First is the inclusion of the FS library:

#include <FS.h>

This already comes with the ESP8266 core so no need to install it.

Then we start SPIFFS like this:

if (!SPIFFS.begin())
{
  // Serious problem
  Serial.println("SPIFFS Mount failed");
} else {
  Serial.println("SPIFFS Mount succesfull");
}

This way, if SPIFFS fail, we will know via serial monitor.

We will not be using server.on() as before. Instead, we’ll do the opposite:

server.onNotFound([]() { // If the client requests any URI
  if (!handleFileRead(server.uri())) // send it if it exists
    server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error
});

The way the server works now is that it will search for files that a client requests. When the client accesses a URL on the server, the file associated with that URL is read from SPIFFS. If the file is not found, a “404:Not Found” is shown on the page. File reading is in a function I call handleFileRead():

bool handleFileRead(String path) { // send the right file to the client (if it exists)
  Serial.println("handleFileRead: " + path);
  if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file
  String contentType = getContentType(path); // Get the MIME type
  if (SPIFFS.exists(path)) { // If the file exists
    File file = SPIFFS.open(path, "r"); // Open it
    size_t sent = server.streamFile(file, contentType); // And send it to the client
    if(path == "/LEDOn.html"){
       digitalWrite(2, LOW);
    }else if(path == "/LEDOff.html"){
       digitalWrite(2, HIGH);
    }
    file.close(); // Then close the file again
    return true;
  }
  Serial.println("\tFile Not Found");
  return false;
}

This is also where we control the LED. When the user accesses the file LEDOn.html, we turn on the LED by making it LOW (the LED is active LOW). Alternatively, when the user accesses the file LEDOff.html, we turn off the LED.

You’ll also notice this function:

String getContentType(String filename){
  if(filename.endsWith(".htm")) return "text/html";
  else if(filename.endsWith(".html")) return "text/html";
  else if(filename.endsWith(".css")) return "text/css";
  else if(filename.endsWith(".jpg")) return "image/jpeg";
  return "text/plain";
}

This returns the MIME type of the file being accessed. This is important as browsers need to know what type of file they are presenting. This is similar to how operating systems view different file extensions.

Upload the sketch, open the serial monitor to acquire the IP address and use that in your browser. This should appear:

Click START and you’ll be on the LEDOff page:

Click the bulb image to turn on the on-board LED!

With a dedicated file system, we have improved our basic ESP8266 web server!

Leave a Reply

Your email address will not be published. Required fields are marked *