ESP8266 Web Server using SPIFFS (SPI Flash File System) – NodeMCU

This tutorial shows how to build a web server that serves HTML and CSS files stored on the ESP8266 NodeMCU filesystem (SPIFFS) using Arduino IDE. Instead of having to write the HTML and CSS text into the Arduino sketch, we’ll create separate HTML and CSS files.

The web server we’ll build shows how to control the ESP8266 outputs and how to display sensor readings. As an example, we’ll control an LED and display sensor readings from a BME280 sensor.

You can use the concepts learned in this tutorial to control any output or display sensor readings from other sensors.

Project Overview

Before going straight to the project, it’s important to outline what our web server will do, so that it’s easier to understand.

  • The web server controls an LED connected to the ESP8266 GPIO 2. This is the ESP8266 on-board LED. You can control any other GPIO;
  • The web server page shows two buttons: ON and OFF – to turn GPIO 2 on and off;
  • The web server page also shows the current GPIO state;
  • You’ll also use a BME280 sensor to display sensor readings (temperature, humidity, and pressure).

The following figure shows a simplified diagram to demonstrate how everything works.

Prerequisites

Before proceeding with this project, make sure you check all the following prerequisites.

1. Install ESP8266 Board in Arduino IDE

We’ll program the ESP8266 using Arduino IDE, so you must have the ESP8266 add-on installed. Follow the next tutorial to install it:

2. Filesystem Uploader Plugin

To upload files to the ESP8266 SPI Flash Filesystem (SPIFFS), we’ll use the Filesystem Uploader Plugin. Install the plugin in your Arduino IDE:

3. Installing Libraries

One of the easiest ways to build a web server using files from the filesystem is using the ESPAsyncWebServer library. 

Installing the ESPAsyncWebServer library

This library is not available to download through the Arduino IDE libraries manager. So, you need to follow the next steps to install the library:

  1. Click here to download the ESPAsyncWebServer library. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get ESPAsyncWebServer-master folder
  3. Rename your folder from ESPAsyncWebServer-master to ESPAsyncWebServer
  4. Move the ESPAsyncWebServer folder to your Arduino IDE installation libraries folder

Alternatively, you can go to Sketch > Include Library > .zip Library and select the previously downloaded library.

Installing the ESPAsyncTCP

The ESPAsyncWebServer library also needs the ESPAsyncTCP library to operate properly. Follow the next steps to install the ESPAsyncTCP library:

  1. Click here to download the ESPAsyncTCP library. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get ESPAsyncTCP-master folder
  3. Rename your folder from ESPAsyncTCP-master to ESPAsyncTCP
  4. Move the ESPAsyncTCP folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Alternatively, you can go to Sketch > Include Library > .zip Library and select the previously downloaded library.

Installing BME280 libraries

In this tutorial, we’ll display readings from a BME280 sensor (Guide with ESP8266). You need to install the following libraries:

You can install these libraries through the Arduino IDE Libraries Manager. Go to Sketch Include Libraries > Manage Libraries. Then, search for the libraries’ name to install them.

Parts Required

To proceed with this project, you need the following parts:

  • ESP8266 
  • BME280 sensor module (Guide for BME280)
  • 5 mm LED
  • 330 Ohm resistor
  • Breadboard
  • Jumper wires

Schematic Diagram

Connect all the components by following the next schematic diagram.

BME280ESP8266
Vin3.3V
GNDGND
SCLGPIO 5
SDAGPIO 4

Organizing Your Files

To build the web server you need three different files. The Arduino sketch, the HTML file and the CSS file. The HTML and CSS files should be saved inside a folder called data inside the Arduino sketch folder, as shown below:

Creating the HTML File

Create an index.html file with the following content :

<!DOCTYPE html>
<!-- 
   Welcome to Gnd_To_Vcc!!  
-->
<html>
<head>
  <title>ESP8266 Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <h1>ESP8266 Web Server</h1>
  <p>GPIO state<strong> %STATE%</strong></p>
  <p>
    <a href="/on"><button class="button">ON</button></a>
    <a href="/off"><button class="button button2">OFF</button></a>
  </p>
  <p>
    <span class="sensor-labels">Temperature</span>
    <span id="temperature">%TEMPERATURE%</span>
    <sup class="units">°C</sup>
  </p>
  <p>
    <span class="sensor-labels">Humidity</span>
    <span id="humidity">%HUMIDITY%</span>
    <sup class="units">%</sup>
  </p>
  <p>
    <span class="sensor-labels">Pressure</span>
    <span id="pressure">%PRESSURE%</span>
    <sup class="units">hPa</sup>
  </p>
</body>
<script>
  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("temperature").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/temperature", true);
    xhttp.send();
  }, 10000 ) ;

  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("humidity").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/humidity", true);
    xhttp.send();
  }, 10000 ) ;

  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("pressure").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/pressure", true);
    xhttp.send();
  }, 10000 ) ;
</script>
</html>

Because we’re using CSS and HTML in different files, we need to reference the CSS file on the HTML text.

<link rel="stylesheet" type="text/css" href="style.css">

The <link> tag tells the HTML file that you’re using an external style sheet to format how the page looks. The rel attribute specifies the nature of the external file, in this case that it is a stylesheet—the CSS file—that will be used to alter the appearance of the page.

The type attribute is set to “text/css” to indicate that you’re using a CSS file for the styles. The href attribute indicates the file location; since both the CSS and HTML files will be in the same folder, you just need to reference the filename: style.css.

In the following line, we write the first heading of our web page. In this case we have “ESP8266 Web Server”. You can change the heading to any text:

<h1>ESP8266 Web Server</h1>

Then, add a paragraph with the text “GPIO state: ” followed by the GPIO state. Because the GPIO state changes accordingly to the state of the GPIO, we can add a placeholder that will then be replaced for whatever value we set on the Arduino sketch.

To add placeholder use % signs. To create a placeholder for the state, you can use %STATE%, for example.

<p>GPIO state<strong> %STATE%</strong></p>

You attribute a value to the STATE placeholder in the Arduino sketch.

Then, create an ON and an OFF buttons. When you click the on button, we redirect the web page to to root followed by /on url. When you click the off button you are redirected to the /off url.

<a href="/on"><button class="button">ON</button></a>
<a href="/off"><button class="button button2">OFF</button></a>

Finally, create three paragraphs to display the temperature, humidity and pressure.

<p>
  <span class="sensor-labels">Temperature</span>
  <span id="temperature">%TEMPERATURE%</span>
  <sup class="units">°C</sup>
</p>
<p>
  <span class="sensor-labels">Pressure</span>
  <span id="pressure">%PRESSURE%</span>
  <sup class="units">hPa</sup>
</p>
<p>
  <span class="sensor-labels">Humidity</span>
  <span id="humidity">%HUMIDITY%</span>
  <sup class="units">%</sup>
</p>

We use the %TEMPERATURE%, %HUMIDITY% and %PRESSURE% placeholders. These will then be replaced by the actual temperature readings in the Arduino sketch.

Automatic Updates

We also add a bit of JavaScript in our HTML file that is responsible for updating the temperature readings without the need to refresh the web page.

The following snipet of code is responsible for the temperature.

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("temperature").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;

To update the temperature, we have a setInterval() function that runs every 10 seconds.

Basically, it makes a request in the /temperature URL to get the latest temperature reading.

  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;

When it receives that value, it updates the HTML element with temperature id.

if (this.readyState == 4 && this.status == 200) {
  document.getElementById("temperature").innerHTML = this.responseText;
}

In summary, this previous section is responsible for updating the temperature asynchronously. The same process is repeated for the humidity and pressure readings.

Creating the CSS File

Create the style.css file with the following content:

/***
   Welcome to Gnd_To_Vcc!!
***/

html {
  font-family: Arial;
  display: inline-block;
  margin: 0px auto;
  text-align: center;
}
h1 {
  color: #0F3376;
  padding: 2vh;
}
p {
  font-size: 1.5rem;
}
.button {
  display: inline-block;
  background-color: #008CBA;
  border: none;
  border-radius: 4px;
  color: white;
  padding: 16px 40px;
  text-decoration: none;
  font-size: 30px;
  margin: 2px;
  cursor: pointer;
}
.button2 {
  background-color: #f44336;
}
.units {
  font-size: 1.2rem;
 }
.sensor-labels {
  font-size: 1.5rem;
  vertical-align:middle;
  padding-bottom: 15px;
}

This is just a basic CSS file to set the font size, style and color of the buttons and align the page. We won’t explain how CSS works. A good place to learn about CSS is the W3Schools website.

ESP8266 Asynchronous Web Server Sketch

Copy the following code to the Arduino IDE . Then, you need to type your network credentials (SSID and password) to connect the ESP8266 to your local network.

/*
  Welcome to Gnd_To_Vcc!!
*/

// Import required libraries
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Set LED GPIO
const int ledPin = 2;
// Stores LED state
String ledState;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

String getTemperature() {
  float temperature = bme.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float temperature = 1.8 * bme.readTemperature() + 32;
  Serial.println(temperature);
  return String(temperature);
}
  
String getHumidity() {
  float humidity = bme.readHumidity();
  Serial.println(humidity);
  return String(humidity);
}

String getPressure() {
  float pressure = bme.readPressure()/ 100.0F;
  Serial.println(pressure);
  return String(pressure);
}

// Replaces placeholder with LED state value
String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if(digitalRead(ledPin)){
      ledState = "ON";
    }
    else{
      ledState = "OFF";
    }
    Serial.print(ledState);
    return ledState;
  }
  else if (var == "TEMPERATURE"){
    return getTemperature();
  }
  else if (var == "HUMIDITY"){
    return getHumidity();
  }
  else if (var == "PRESSURE"){
    return getPressure();
  }  
}
 
void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

  // Initialize the sensor
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  // Initialize SPIFFS
  if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // Route to load style.css file
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });

  // Route to set GPIO to HIGH
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(ledPin, HIGH);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // Route to set GPIO to LOW
  server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(ledPin, LOW);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", getTemperature().c_str());
  });
  
  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", getHumidity().c_str());
  });
  
  server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", getPressure().c_str());
  });

  // Start server
  server.begin();
}
 
void loop(){
  
}

Uploading Code and Files

Save the Arduino sketch as ESP8266_SPIFFS_Web_Server .

  • Go to Sketch > Show Sketch folder, and create a folder called data. Save the HTML and CSS files inside that folder;
  • In Tools Board, select the ESP8266 board you’re using;
  • Then, go to Tools Flash size and select 4M (1M SPIFFS).

Finally, upload the files to your board. Go to Tools > ESP8266 Data Sketch Upload and wait for the files to be uploaded.

Then, press the Arduino IDE upload button to upload the code to the ESP8266.

When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP8266 on-board RST button, and it should print the ESP8266 IP address.

Demonstration

Open a browser and type your ESP8266 IP address. The following web page should load.

Press the ON and OFF buttons to control the ESP8266 on-board LED. You can also visualize the latest sensor readings. The sensor readings are updated automatically without the need to refresh the web page.

Conclusion

Using the ESP8266 SPI Flash File System (SPIFFS) is specially useful to store HTML and CSS files to serve to a client – instead of having to write all the code in the Arduino sketch.

We have other related projects that you may like:

Thanks for reading.

Published by Gnd_To_Vcc

Here to spread my knowledge . Knowledge should always be spread not stored.

4 thoughts on “ESP8266 Web Server using SPIFFS (SPI Flash File System) – NodeMCU

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create your website with WordPress.com
Get started
%d bloggers like this: