ESP8266 as a SPIFFs File Server

How to setup an ESP8266 board as a server using SPIFFs

This how-to guide describes using an ESP8266 chip to setup a server to do what servers do... serve files!  If you want to know how to retrieve files from a web server, see my post on using the ESP8266 as a HTTP client.  Although HTML and other files can be contained within the program itself ("sketch" in the Arduino parlance), it is inconvenient, especially if you want to deposit images on the ESP-board, or change your files in and out easily.  The solution is use to SPIFFs, a file system that uses the on-board SPI flash chip, which can be written directly from your computer (and read and written from programmatically within your sketch too).  The amount of memory will vary depending upon the board in question.

How to upload files to SPIFFs
For this tutorial, I have been using a NodeMCU ESP-12E board, though it should work on any supported ESP8266 board with on-board SPI flash.  The NodeMCU ESP-12E has 4 Mb of flash memory available, but the Arduino IDE lets us decide how we carve that 4 Mb up between program memory and the SPIFFs file system.  In this case I just want to serve a few small files, so I selected "4M (1M SPIFFS)" which gives us 3 Mb for program memory and 1 Mb for files, though as this code is fairly contained, it would probably make sense to select "4M (3M SPIFFS) so we can expand our file space later.  If you are using an ESP-01S board, your options are slightly more limited as you may only have 512kb or 1 Mb of flash.


To upload files from your computer to the ESP8266 board they need to be placed in a folder called "data" within the sketch folder (either navigate to the sketch folder, press Ctrl+K when the sketch window is active, or go to the menu; Sketch --> Go to Sketch Folder).  Place the files you want uploaded in this folder.  Now we need to install a plugin for the Arduino IDE.  Go here and download the "ESP8266FS-0.2.0.zip" file.  Navigate to your Arduino folder, under My Documents, i.e. for me it was: C:\Users\Duncan\Documents\Arduino.  In the Arduino folder create a new folder called "tools" (if one does not already exist).  Unzip the ESP8266FS-0.2.0.zip file and move the entire ESP8266FS folder and sub-folders and files into Arduino\tools folder you created.  You should end up with a path containing something like:
C:\Users\Duncan\Documents\Arduino\tools\ESP8266FS\tool\esp8266fs.jar.

Restart the Arduino IDE and "ESP8266 Sketch Data Upload" should appear as a new option under the Tools menu!  Now it's really easy to upload the sketch data.  Simply click this option - if you get an error, the chances are that you have the console window open, so close it and try again.  The ESP-12E board can be programmed easily over USB, but for boards like the ESP-01S, you'll need to put the board into programming mode manually by holding GPIO0 low during RESET (see previous blog post here).  [As a side note, the ESP-01S board with a certain type of PUYA SPI flash chips have problems being written to by the sketch but not by the Arduino IDE plugin.  A patch is available for this, which I will blog about how to apply soon].

How to serve uploaded SPIFF files
Now we have our files, for example index.html, images and text files, uploaded to the ESP8266 board flash, how can we serve them?  The answer is very simply, with all the functionality written for us already.  I found this was a great reference: https://tttapa.github.io/ESP8266/Chap11%20-%20SPIFFS.html .  I reproduce the code we need here:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <FS.h>   // Include the SPIFFS library

ESP8266WiFiMulti wifiMulti;     // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'

ESP8266WebServer server(80);    // Create a webserver object that listens for HTTP request on port 80

String getContentType(String filename); // convert the file extension to the MIME type
bool handleFileRead(String path);       // send the right file to the client (if it exists)

void setup() {
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer
  delay(10);
  Serial.println('\n');

  wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");   // add Wi-Fi networks you want to connect to

  Serial.println("Connecting ...");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
    delay(250);
    Serial.print('.');
  }
  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID());              // Tell us what network we're connected to
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());           // Send the IP address of the ESP8266 to the computer

  if (MDNS.begin("esp8266")) {              // Start the mDNS responder for esp8266.local
    Serial.println("mDNS responder started");
  } else {
    Serial.println("Error setting up MDNS responder!");
  }

  SPIFFS.begin();                           // Start the SPI Flash Files System
  
  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
  });

  server.begin();                           // Actually start the server
  Serial.println("HTTP server started");
}

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

String getContentType(String filename) { // convert the file extension to the MIME type
  if (filename.endsWith(".html")) return "text/html";
  else if (filename.endsWith(".css")) return "text/css";
  else if (filename.endsWith(".js")) return "application/javascript";
  else if (filename.endsWith(".ico")) return "image/x-icon";
  return "text/plain";
}

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
    file.close();                                       // Then close the file again
    return true;
  }
  Serial.println("\tFile Not Found");
  return false;                                         // If the file doesn't exist, return false
}
By looking completing the network credentials, uploading to the ESP8266 board and looking at the IP address (i.e. 192.168.0.45), we can navigate there in a web browser.  If you had previously uploaded an "index.html" file, you will see this displayed here!  You can then access other files you've uploaded to SPIFFs by navigating to 192.168.0.45/FILENAME_HERE.  From this starting point we can start to get more sophisticated and update local files on the ESP8266 from the web browser, but I will save this for a future post. 

UPDATE: How to use an ESP8266 as an HTTP client

Happy coding! :)

Comments

Popular posts from this blog

Getting started with the Pro Micro Arduino Board

Arduino and Raspberry Pi serial communciation