ESP8266 (Web Server – Part 1): Store the web interface in the SPIFFS area (HTML, CSS, JS)

We continue our series of articles on programming the Web Server part of the ESP8266 modules. In the previous articles, we saw how to set up the Web Server, then how to improve the display using the Bootstrap framework (and use the Bootswatch themes). In the first two episodes, all the HTML code was generated directly in the Arduino code. This is not a problem for a simple project but we do not fully benefit from the capabilities of the ESP8266. We will go even further in this new series of articles. We will learn how to manage the code of the Web interface separately and store all the files in the SPIFFS file system of the ESP8266.

Project overview

We will discuss many concepts in this web interface project for ESP8266. So there will be several episodes. Here is a summary table that summarizes the themes addressed in each part. You will also find a link to the GitHub repository (if any) and a link to the article.

Part Discussed subjects GitHub repo Post
Part 1
  • How to Prepare HTML Web Interface Code
    • To do this, we will use the Pug language (formerly called Jade) that simplifies writing
    • New notions of HTML: menu navigation, image, fix footer, better management “responsive” for small screens
  • How to change the theme of the interface
    • How to store the choice to reload the theme the next time the interface is loaded
  • How to prepare and use the SPIFFS area to store HTML, JS, CSS, images
    • And send them on the ESP8266
    • First test of the WEB interface on the ESP8266
Part 2 How to interact with Arduino code. Read the post here

  • Intercept actions on the interface buttons and refresh the displays when the action was performed (Javascript + jQuery)
  • Recover requests on the Arduino, execute the request and send the reply
  • Regularly (and automatically) update the measurement table and displays (Javascript)
  • Update the symbols if the current value is greater or lower than the previous value
Part 3 How to retrieve time from internet. Read the post here
Part 4 How to create a measurement history. Read the post here

  • Handling data using the ArduinoJSON library
  • Save data (measurement history) to a file on the SPIFFS area
  • Reload the log file when ESP8266 starts
Part 5 How to add charts and gauges. Read the post here

  • Evolution of temperature and mean humidity over the last 7 hours (bar graph)
  • Display of the current measurement in the form of a thematic gauge: thermometer (temperature), water drop (humidity), gauge (atmospheric pressure)

To continue the lists, here are the languages and APIs used:

  • C ++ (Arduino code)
    • Libraries: ESP8266WebServer, DHT, Adafruit_BMP085, ArduinoJSON, FS.h
  • Javascript and jQuey for interactions with the HTML page
  • Pug
  • HTML
  • Bootstrap and Bootswatch themes
  • Bootstrap-table (plugin to easily create tables)

What are you going to need?

Before you get to the heart of the matter, you’re going to need some hardware and a proper software setup.

Equipment used

On the hardware side, we will stay in the classical, objective and have available data to display and elements to simulate interactions. So I opted for a DHT22 to retrieve temperature and humidity measurements and a BMP180 for atmospheric pressure. To simulate the outputs, simple Led will do the trick. For the ESP8266, I opted for the Wemos D1 mini which has a SPIFFS area of 3M.

Last update was on: 28 May 2018 6 h 03 min

Software and platform environment

As you know, it is possible to program the ESP8266 using the Lua language as well as C ++ from the Arduino IDE. I opted for the Arduino IDE because it is much easier to find examples, information, bookshops, forums and access to beginners and easier. As I test the alternatives (good or bad) to Raspberry Pi, all developments were done on an Orange Pi + 2e running under Armbian based on Ubuntu 16.04 LTS. But everything will work identically whatever your environment (Windows, MacOS, Raspberry Pi …).

For writing HTML, I advise you to use the language Pug (old Jade) which makes it possible to simplify the development of the code (we no longer manage the closing of the tags). Pug is not really a language, we just write the tag and we respect a formalism of writing.

To sum up, here is the configuration used and the references to the tutorials to consult if you lack information.

Note. I tried to make all the concepts tackled as synthetically as possible so that everyone could take ownership of them. The sharpest of you will certainly find better ways to write, do not hesitate to use the comments to give advice.

Orange Pi Plus 2e under Armbian (Ubuntu 16.04 LTS)

Some links : presentation, install Armbian on Orange Pi,

Geany : text editor oriented programming (for HTML, Pug, Javascript, jQuery)

Post : a text editor to develop on Raspberry Pi, Orange Pi (ARM SoC)

Pug : Language to simplify the writing of HTML

Post : how to prepare HTML interface with Pug and Geany

IDE Arduino 1.8.1

Posts :

How to transfer files in the SPIFFS area from the Arduino IDE?

The ESP8266 has a reserved zone called SPIFFS whose size varies from one model to another (some models, the oldest, do not offer). This memory area can be used to host files. We will not go further on SPIFFS storage in this article, a dedicated tutorial will be devoted to it. In order to download files in this memory area, it is necessary to install the file download tool in the SPIFFS area to the Arduino IDE available on Github. Just download and unzip the file to the tools directory. If it does not exist in your Arduino folder, just create one manually. Restart the Arduino IDE so that the download tool is added to the tool menu under the label ESP8266 Sketch Data Upload.

ide arduino esp8266fs tools spiffs upload

Preparation of the WEB interface: HTML code

So we’ll use the Pug language to prepare the code for the HTML interface.

Header of the page

To create our interface, we will need new resources. At first, we’ll just go and get them on the internet. We’ll see in the next tutorial how to add them in the SPIFFS area. We will therefore create a header that contains the resources to be loaded by the browser (jquery, bootstrap.js, bootstrap-table.js, bootstrap.css, bootstrap-table.css).

A few comments :

  • The order does not matter
  • We retrieve the versions “minified”, more compact but difficult to understand
  • The name = 'viewport' tag is reserved for Bootstrap.
  • The http-equiv = 'refresh' tag that prompted the browser to refresh the display regularly has been removed. We will manage the display refresh otherwise.

Adding a navigation bar (body)

Let’s move to the body of the page. Below the project title (tag h1), we will add a navigation bar to group the information by theme (measurements, GPIO graphics, configuration). With Bootstrap, it is handled by the .nav and .nav-tabs classes. Each tab (tab) consists of a li tag. The active attribute is used to indicate the default active tab. A tag a allows to indicate the title of the tab and especially to indicate to which element to go when one clicks (href=”#iddestination”). The data-toggle attribute makes the tab clickable.

Then it only remains to create a div of class tab-content then a div tab-pane for each tab. For example,

To make the display more fun, you can also add an animation when you move from one tab to another:

Similarly, the first active tab is displayed by default (for the first opening of the page).

There is nothing more to program to create a navigation bar and navigate from one tab to another!

Adding a Bootstrap-table

Bootstrap-table is a well known and very powerful Bootstrap plugin for creating tables and performing operations (project page on GitHub). It allows to be updated very easily). For now, we’ll just put it on the Measures tab. A table field is created. We will simply place a th tag for each column. Each column is assigned a data-field which will be associated with a key from the JSON sent by the Arduino code. Finally we attribute a data-formatter, it is a function that is called whenever a row is added to the table and which allows to format the content. We will see in the next tutorial how to fill this table.

Here’s what we’ll get (with the default Bootstrap theme).

esp8266 bootstrap web server nav tab barre navigation onglets


Function gadget, but useful to learn other notions of HTML, footer fixed. The footer does not move when moving from one tab to another. You can use this field to indicate all kinds of information (date, time, Wi-Fi reception quality, CPU load …). To fix the footer, we will just add a row and give it the Parameters in the HTML style attribute:

  • Position: absolute, the row is absolutely positioned
  • Bottom: 0, at the bottom of the page at 0 pixels of the edges
  • Width: 100%, and this line takes the full width of the available browser

In this line we will add 3 columns. The first one will contain an image (my logo :-)), and two links. The content is not important, what is important is to see how to make content responsive, ie it adapts to all screens and how to load an image from the SPIFFS area.

To display an image, add an img tag, in the source (src), indicate the path as directory/image.jpg. You can also impose width=”30″ (width of 30 pixels) and height=”30″ .

Bootstrap uses a grid system. If you decrease the size of the browser, the cells will start to reduce then and put themselves under the others. To do this, we use the classes col-md-x. The sum of the x’s must not exceed 12 (the screen is divided into 12 columns). To adapt to the smallest screens (smartphone in particular), we can for example combine with a class col-xs-x. It is a very simplistic approach but it works and it is not the main objective of the article.

Here is the rendering obtained for different screen widths. Normal size

Smartphone (simulated)

Change Dynamic Bootstrap Theme

In the previous tutorial, it was very easy to change the theme because it was enough to pass the selected theme when building the HTML code of the page. Here it will be necessary to introduce a little javascript (and jquery) to get there. I was inspired by the code proposed by wdfz in this tutorial.

We will start by modifying the selection list. The hidden field that was used to store the selected theme and then sent to Arduino code using a POST HTTP request is no longer needed. We will intercept a click on the class change-style-menu-item and we will retrieve the selected style contained in the attribute rel. The HTML code of the selection box becomes the following.

Now we add a script. When you click on the change-style-menu-item class with a jQuery command. The value of the rel attribute is retrieved with the command $(this).attr('rel') . This contains the object that has just been clicked. The attribute is accessed with the method attr()

To understand and see what happens, you can open your browser’s developer tools and set a breakpoint.

esp8266 web server change theme bootstrap recupere attribut rel

Now that we know the selected theme, we build the URL that points to the corresponding website. This is done to refresh the label of the button by including the name of the new theme. Finally, if the storage in the local database is available (this will depend on the browser used, not the ESP8266), saves the theme.

Now it only remains to rebuild the page with this theme. For this to work, you must add a title = “main” attribute in the link to the CDN Bootstrap

We modify the href inside link[title=”main”].

We still have two functions to write, the first returns true if online storage is available

And finally the last function that allows to reload the theme if it was recorded in the local database.

Now, everything happens directly on the browser, the ESP8266 is no longer responsible for rebuilding the page at each change of theme, this role is devolved to the browser.

Put everything together

Here are the Arduino and HTML codes. Nothing is yet functional at this stage of development (except for the dynamic change of the theme).

Arduino/ESP8266 Code

Create a new project and paste the following code. Here are the differences from the previous tutorial:

  • We call the library FS.h which allows access to the zone SPIFFS
  • To access the files stored in the SPIFFS area, you must initialize the library by calling the SPIFFS.begin() function in the setup().
  • The server is told which pages and static resources are to be available to the browser at the moment /img and the root of the site (/) that points to the index.html page.

Do not forget to change the ssid and password of the WiFi network.

Select the ESP8266 (here Wemos D1 mini) and then upload the project to the ESP8266.

Generate HTML code

Go to the project directory and create a new data folder. Add a subfolder img

Create a new document under Geany and paste the following code. Save the file with the name index.pug at the root of the data folder. Compile the HTML code using pug-cli (F8).

Alternatively, create a new document with a simple text editor and paste the HTML code of the resulting project. Save the file with the name index.html in the data directory of the Arduino project.

If the serial monitor is open, close it. Go to the Tools menu and select the speed upload speed of 921600 baud. The download is quite long, so go as fast as possible. Finally, go to the Tools menu and click ESP8266 Sketch Data Upload. The operation starts immediately.

esp8266 upload spiffs sketch data ide arduino

Once the download is complete, return the upload speed to 115200 baud and open the terminal to verify that the ESP8266 is properly connected to the WiFi network. Retrieve the IP address. Open a browser and enter the IP address of the ESP8266. Bravo, it works!

esp8266 spiffs wemos d1mini web server bootstrap dht22 bmp180

In summary, we saw how to create an HTML interface from any room using the Pug language. You can dynamically change the Bootstrap theme and store the choice in the browser database for the next upload. We have put in place a navigation bar which allows to group the elements by themes. We know how to use the SPIFFS area to store all the files necessary for the operation of the interface. There is still much to do!

Subscribe to the weekly newsletter

No spam and no other use will be made of your email. You can unsubscribe anytime.

  • Bruno Ricardo

    and the part 2?

  • Bruno Ricardo

    I really like this article man… good job!!!

    • Thank you very much Bruno. I have added all links to the following articles in the summary table at the beginning of the article. Tell me if you’re missing things. See you soon.

  • Bruno Ricardo

    Every time that I used in offline mode when I made a request the esp crash and after of a time he’s comeback, what’s wrong?

  • Usman Shahid

    hii i am unable to understand about root folder

  • Yves GUILLET

    Hello ,
    Pour me former ,je me forme !!!! de la partie 1 à la dernière , il faut à chaque fois recommencer le Debug …pas grand chose de correct , manque du code d’un coté et d’un autre(code , Html) ,disparition de composants (DS18B20) , appels de fonctions inexistants , …etc …dommage , mais j’ai finalement autre chose de mieux à faire
    En bref , un navrant gros manque de rigueur .

    • Bonjour Yves. Ce qui est navrant c’est la critique pour la critique, c’est tellement frenchy ! En informatique, tout devient très rapidement obsolète. Les bibliothèques, les langages, les cartes évoluent en permanence et je n’ai (malheureusement) pas le temps de vérifier et mettre à jour tous les tutoriels du blog (version française et anglaise). C’est l’idée de la communauté ou chacun apporte sa pierre en respectant le travail des autres.. et les autres par la même occasion !!!! La mienne consiste à partager mon expérience et mes idées. Ce qui aurait été plus intelligent et constructif, c’est de me faire remonter les erreurs pour que je puisse apporter les corrections nécessaires. La toile est vaste, j’espère que vous trouverez mieux ailleurs !

      • Yves GUILLET

        Désolé , mais quand je ne sais pas et que veux apprendre , je me tourne vers un “tutoriel” que je suis pas à pas ; si à chaque pas je suis obligé de passer , selon les parties , un certain temps à corriger les manques ou les erreurs , je n’apprends rien et cela m’empêche de comprendre les mécanismes mis en oeuvre et finalement , c’est décourageant , d’ou mon précédent message .La seule véritable critique , c’est simplement que l’excellent texte n’est pas soutenu par le code proposé et tant que cela ne fonctionne pas sur la base fournie je ne peux pas envisager d’adapter le code à mes idées .
        Quant à faire remonter les erreurs , à moins de noter toutes les manips que vous êtes amené à faire ….je ne sais pas faire .

        Pour le reste , vous avez raison , tout va très vite …mon 1er PC date de 1981 et j’ai du me frotter
        à des choses qui vous paraîtrons ridicules comme l’assembleur , Basic, Pascal ,…et Internet n’existait pas ! il fallait tout se “palucher ” ,la moindre fonction .
        Maintenant , je continue à m’ intéresser et à essayer de m’adapter aux multiples solutions possibles utilisables pour arriver à un résultat , malheureusement , pour une plateforme donnée; Arduino , Linux , Windows, ….
        Et maintenant JSON ; j’ai pas encore tout intégré …

DIY Projects