One of the great advantages of the ESP8266 is it's Wi-Fi-interface and the very fast processor. (It also has a, compared to other microcontrollers, large data-storage.)
So it is easier than ever to generate a nice UI (user interface) to control your project. (You do not even need an additional element.) In this post I will show you how you can introduce a HTML5-based graphical user interface.

Start a server

Our first step is to set up the server. This can be done with two lines of code.

#include <ESP8266WebServer.h>
ESP8266WebServer server(80);    //listen on port 80
//from now on the server is started and will serve your content!
void setup() {}
void loop() {}

Manage the content

So as you might know a webpage is, most often, build from a bunch of files, which are served by the webserver. So the first question which comes to my mind is: How to get all the .html, .css and .js files in this tiny microcontroller. After some investigation there are two possible solutions: filesystem vs. variable.

Use the filesystem and serve the files from there

The ESP8266 Arduino Core comes with a filesystem-module. This module allows you to access all the stored files on your ESP8266. But first you need to upload them. Just step through the following tutorial and you will be able to upload all your UI-related files. If we add the following lines of code to our sketch your server will give you an index-page if you call it's address in your browser.

#include <FS.h>
...
void setup () {
 ...
 server.serveStatic("/", SPIFFS, "/index.html");
 ...
}
...

From my point of view this store is basically the right place for all the static files in our projects. (It is also large enough to store them.) But at the moment I see one big drawback. What happens if we want to update this files and can not access our microcontroller (e.g.: It is mounted on the top of your roof.)? Your sketch can be updated via OTA. I do not know if there is a possibility to do the same thing with the filesystem! (Maybe I will write an extra post on this, if I have found a solution.)

Put the content in variables and serve them

Have a brief look at the following lines of code. I will explain them later on.

...
const char SITE_index[] PROGMEM = R"=====(
  <html>
   <head>
    <title>Temperaturesensor 1</title>
   </head>
   <body>
    <p>Temperature: <span id="temperature"></span></p>
    <p>Humidity: <span id="humidity"></span></p>
   </body>
  </html>
)=====";
...
void setup() {
 ...
 server.on ( "/", []() { server.send ( 200, "text/html", SITE_index );  } );
 ...
}
void loop() {}

Okay, so let's start from top to bottom. First we declare a character-array which holds our site. This should not be new to you. But you may not know the PROGMEM keyword. This keyword states that the variable has to be hold in the flash memory and not in the volatile SRAM, where all the other variables are stored at runtime. This has one big advantage: you do not populate the limited SRAM with your sites. The next unknown part for many people is the [raw string literal](raw string literal), which is represented by the following code: R"=====()=====" (I would like to state that the multiple equal-signs act as a token. You can also replace them with every other combination you can think of. But it is important that the text does not contain this sequence!) The point of this raw string literal is that you do not need to escape your html code.
The drawback of this solution is that all the sites are saved to the space reserved for the sketch.

Dynamic content

If you remember the first "chapter", we have this two possibilities to serve static content. If we think of dynamic content the first solution (using the filesystem) will not work that easy, because we would need to load the content from the file, manipulate it and pass this modified content to the client. (You would need to handle placeholders.) So if you want to serve this dynamic part on every load and do not want to update it when the side has been loaded, you can solve this problem with the second method. Just split the content in multiple parts, inject your variables at the right places and reassemble it.

But this is not the way a "product" should work. We do not want to reload the complete site every time we want to see an updated value! To get rid of this issue we will use some additional REST-endpoints. For simplicity let's assume we have a DHT22 attached to our thing and we would like to be able to see all the information from this sensor.

void setup() {
 ...
 server.on ( "/temperature", getTemperature );
 ...
}
void getTemperature() {
    ...
    server.send ( 200, "application/json", temperature );
}

If you now call http://<IP-Adress of your ESP8266>/temperature you will get the current temperature. But this is not very efficient if we think of the fact that our DHT22 can also serve the humidity, because we would need to set up two requests. To get all this information in a single request we can use JSON (javascript object notation). This gives us the possibility to get multiple variables and do not need to separate them by ourself.

void getDHTdata() {
    ...
    server.send ( 200, "application/json", "{\"temperature\":" + String(temperature) + ", \"humidity\":" + String(humidity) + "}" );
}

The only part left is to implement a function which gets the updated values and displays them. This can be done with a few lines of JavaScript.

function updateData() {
 console.log("updateData()");
 var xhttp = new XMLHttpRequest();
 xhttp.onreadystatechange = function() {
  if (this.readyState == 4 && this.status == 200) {
   console.log("updateData(): got Data:", this.responseText);
   var DHTdata = JSON.parse(this.responseText);
   document.getElementById("temperature").innerHTML = DHTdata.temperature;
   document.getElementById("humidity").innerHTML = DHTdata.humidity;
  }
 };
 xhttp.open("GET", "/DHT", true);
 xhttp.send();
}

 updateData();
 window.setInterval(updateData, 5000);

Now you can add some other features like a history or displaying the correct measuring unit.
I think I will extend this starter-project in the next days and write a separate topic on this.


I uploaded my basic code. Please be aware, that I basically use the Soft-AP for development purposes. So if you want to use this project in your environment, you may change to the Station mode.

Previous Post Next Post