ESP8266: Understanding Web Server programming with Arduino code

The ESP8266 can be programmed as a classic Arduino but its main interest is its Wi-Fi connection, which allows it to publish data to a server or an online Dashboard (ThingSpeak, Freeboard.io), or to easily create connected objects Which can be controlled from a home automation server or a mobile application (developed with Blynk or Cayenne for example). In this tutorial we will learn how to program the ESP8266 to behave like a Web Server. That is to say that we can interact with the program, the GPIO … from a web interface accessible from a web browser.

Install the ESP8266 libraries and boards on Arduino IDE

Before starting, it is necessary to check that all necessary libraries are installed.

http://arduino.esp8266.com/stable/package_esp8266com_index.json

IDE Arduino 1.6.8 esp8266 package

You can view (by curiosity) maps handled by the ESP8266 library and pasting the previous URL into a browser. In January 2017 (version 2.3.0), the following cards are managed:

  • Generic ESP8266 Module
  • Olimex MOD-WIFI-ESP8266(-DEV)
  • NodeMCU 0.9 (ESP-12 Module)
  • NodeMCU 1.0 (ESP-12E Module)
  • Adafruit HUZZAH ESP8266 (ESP-12)
  • ESPresso Lite 1.0
  • ESPresso Lite 2.0
  • Phoenix 1.0
  • Phoenix 2.0
  • SparkFun Thing
  • SweetPea ESP-210
  • WeMos D1
  • WeMos D1 mini
  • ESPino (ESP-12 Module)
  • ESPino (WROOM-02 Module)
  • WifInfo
  • ESPDuino

Go to the board manager and look for esp8266, then click install.

IDE Arduino 1.6.x ajouter carte esp8266

At the same time as the maps, all necessary libraries and many examples are installed. If the examples are not visible in the menu File -> Examples -> Examples for WeMos D1, exit and restart the Arduino IDE.

A simple program to start

We will go through steps to understand how to create a web server on an ESP8266. Open the WiFiWebServer example.

One needs at least the ESP8266WiFi.h library to create a Web server. Like all libraries, it is necessary to delve a lot to know the methods available, here is a little insight to better understand what can be done with. The source code of the library is available here. This library is an adaptation of all the libraries developed for the Arduino. You will also find some infos here

  • WiFiClient
    • uint8_t status()
    • int connect(IPAddress ip, uint16_t port)
    • int connect(const char *host, uint16_t port)
    • size_t write(uint8_t)
    • size_t write(const uint8_t *buf, size_t size)
    • size_t write_P(PGM_P buf, size_t size)
    • size_t write(Stream& stream)
    • int available()
    • read(uint8_t *buf, size_t size)
    • int peek()
    • size_t peekBytes(uint8_t *buffer, size_t length)
    • size_t peekBytes(char *buffer, size_t length)
    • void flush()
    • void stop()
    • uint8_t connected()
    • IPAddress remoteIP()
    • uint16_t remotePort()
    • IPAddress localIP()
    • uint16_t localPort()
    • bool getNoDelay()
    • void setNoDelay(bool nodelay)
    • void setLocalPortStart(uint16_t port)
    • static void stopAll();
    • static void stopAllExcept(WiFiClient * c)
  • WiFiServer
    • WiFiServer(IPAddress addr, uint16_t port)
    • WiFiServer(uint16_t port)
    • WiFiClient available(uint8_t* status = NULL)
    • bool hasClient()
    • void begin()
    • void setNoDelay(bool nodelay)
    • bool getNoDelay()
    • virtual size_t write(uint8_t)
    • uint8_t status()
    • void close()
    • void stop()
  • WiFiUDP
  • WiFiClientSecure
#include <ESP8266WiFi.h>
const char* ssid = "your-ssid";
const char* password = "your-password";

We create an instance, an object that will contain the web server. We will communicate with it on port 80, the traditional port of HTTP requests used by Internet browsers.

WiFiServer server(80);

Setup

void setup() {
  // Ouverture du port serie en 115200 baud pour envoyer des messages de debug à l'ide par exemple
  Serial.begin(115200);
  delay(10);

  // prepare GPIO2 (sortie)
  pinMode(2, OUTPUT);
  digitalWrite(2, 0);
  
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  // On se connecte a reseau WiFi avec le SSID et le mot de passe precedemment configure
  WiFi.begin(ssid, password);
  
  // On sort de la boucle uniquement lorsque la connexion a ete etablie.
  // Il faudrait ameliorer cette partie pour un fonctionnement sur batterie car en cas d'abscence du reseau
  // on va vider la batterie !
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  // connexion OK, on demarre le server web // Start the server
  server.begin();
  Serial.println("Server started");

  // On indique sur le port serie l'adresse ip de l'ESP pour le trouver facilement / Print the IP address
  Serial.println(WiFi.localIP());
}

What happens in the loop?

We do something only if a client is connected, that is to say that we make an HTTP request on the ESP8266 from an Internet browser

WiFiClient client = server.available();
if (!client) {
  return;
}

When a client connects, it is indicated on the serial port

Serial.println("new client");
while(!client.available()){
  delay(1);
}

We retrieve the HTTP request, here the URL entered in the browser simply. The contents of the HTTP request are sent to the serial port. The buffer is emptied using the flush method.

String req = client.readStringUntil('\r');
Serial.println(req);
client.flush();

The HTTP request is checked for what to do. The easiest method is to pass orders to ESP8266 in the form of a /gpio/state (0, 1 or on / off) command. We will just make a test on a string with the command indexOf on the variable req. This type of strategy is suitable for small projects but it can quickly become a source of error with the stacking of if, else if.

int val;
if (req.indexOf("/gpio/0") != -1)
  val = 0;
else if (req.indexOf("/gpio/1") != -1)
  val = 1;
else {
  Serial.println("invalid request");
  client.stop();
  return;
}

We update the GPIO and flush the buffer

digitalWrite(2, val);
client.flush();

We now create an HTML page that contains the status of the GPIO. This is a text string that contains the HTML code of the page. The page code, the status of the GPIO

String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nGPIO is now ";
s += (val)?"high":"low";
s += "</html>\n";

We publish this page to the customer with the print command, very easy!

client.print(s);

Now you can remotely control any hardware connected to the GPIO (a relay, a led, a motor, a servo …) using a simple HTTP request. For example, you can simply execute a command from a home automation software. Here are two examples, the first for Domoticz, the second for Jeedom.

Add an HTML GUI to your projects

Now, you would definitely want to make a small interface for your ESP8266 projects. For this, we need to know some basics of HTML. We are not going to go very far in learning HTML, just learn the important elements to get started and have a working project. If you need more interface elements, I recommend w3scholls which is a reference in learning HTML.

In a project ESP8266, we can create HTML pages in dynamic, ie we build a text string that contains the code of the page that will then be displayed. That’s what we’re going to do. But the ESP8266 is also able to function as a real website, ie we can install on HTML flash pages, javascript code, CSS style sheets … We will not go Until now in this first tutorial.

I propose to create a small weather station to have data to update regularly and create a button to activate / deactivate a GPIO output (just a Led, for example.) I used the following hardware

esp8266 Wemos D1 mini Any ESP8266 ESP-12 module, for example Wemos D1 Mini
 BMP180 Atmospheric pressure

BMP180

Broches DHT22 Temperature and humidity sensor

DHT11 or DHT22

jumper dupont Jumper Dupont
breadboard Breadboard
led 3mm Led (optional, follow the WiFi activity)
resistance 220ohms Resistor 220Ω (optional)

Wiring

Here is a table of pin marking and correspondence between Arduino and ESP8266.

Sensor Pin ESP8266 Pin (Wemos D1 mini)
DHT22 VCC 5V
GND G
Data G5
BMP180 VCC 5V
GND G
SDA D2

We begin by declaring the necessary libraries. Do not forget to install the libraries from the library manager (DHT and BMP085).

Note. You might encounter an error when compiling adafruit_Sensor.h: No such file or directory. In this case, download and unpack the library manually from GitHub in the Arduino -> Library folder and then restart the IDE for it to take effect.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>

Program variables are defined. Modify the WiFi network to which you will connect and the WiFi network password.

#define ssid      "ssid"       // WiFi SSID
#define password  "password"  // WiFi password
#define DHTTYPE   DHT22       // DHT type (DHT11, DHT22)
#define DHTPIN    D4          // Broche du DHT / DHT Pin
#define LEDPIN    D3          // Led
float   t = 0 ;
float   h = 0 ;
float   p = 0;
String  etatLed = "OFF";

We create the dht, bmp and server objects

DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP085 bmp;
ESP8266WebServer server ( 80 );

This first function makes it possible to construct the HTML code of the main page of the program. It is a simple chain of character. It is an assembly of chains. You can easily include the value or state of a variable (for example, the status of an output). In return, the function returns a string containing the HTML code of the page.

String getPage(){
  String page = "<html lang=fr-FR><head><meta http-equiv='refresh' content='10'/>";
  page += "<title>ESP8266 Demo - www.projetsdiy.fr</title>";
  page += "<style> body { background-color: #fffff; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }</style>";
  page += "</head><body><h1>ESP8266 Demo</h1>";
  page += "<h3>DHT22</h3>";
  page += "<ul><li>Temperature : ";
  page += t;
  page += "°C</li>";
  page += "<li>Humidite : ";
  page += h;
  page += "%</li></ul><h3>BMP180</h3>";
  page += "<ul><li>Pression atmospherique : ";
  page += p;
  page += " mbar</li></ul>";
  page += "<h3>GPIO</h3>";
  page += "<form action='/' method='POST'>";
  page += "<ul><li>D3 (etat: ";
  page += etatLed;
  page += ")<INPUT type='radio' name='LED' value='1'>ON";
  page += "<INPUT type='radio' name='LED' value='0'>OFF</li></ul>";
  page += "<INPUT type='submit' value='Actualiser'>";
  page += "<br><br><p><a hrf='https://diyprojects.io'>diyprojects.io</p>";
  page += "</body></html>";
  return page;
}

Let’s see a little better how the code is built

HTML code Explication
<html lang=fr-FR>

<head>

<meta http-equiv=’refresh’ content=’10’/>

<title>ESP8266 Demo – www.projetsdiy.fr</title>

<style> body { background-color: #fffff; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }</style>

</head>

Lang: set the language of the page

Head: this is the header of the page. It contains different meta (parameters)

  • http-equiv = ‘refresh’: this is a page that the browser will need to refresh. For more types, go here
  • content = ’10 ‘: every 10 seconds

title: the title of the page displayed in the browser bar

style: a style for the page (background color, font to use, text color

<body> Content of the page
<h1>ESP8266 Demo</h1> Title
<h3>DHT22</h3> sub-title (littler) for DHT22 sensor
<ul>

<li>Température : xx°C</li>

<li>Humidité : xx%</li>

</ul>

The ul block is used to display the information in a list. The same for the BMP180
<form action=’/’ method=’POST’>

<ul>

<li>D3 (etat: xx)

<INPUT type=’radio’ name=’LED’ value=’1′>ON

<INPUT type=’radio’ name=’LED’ value=’0′>OFF

</li>

</ul>

<INPUT type=’submit’ value=’Actualiser’>

</form>

 

To update the GPIO, a form is used.Here we use a radio button to change the state (On / Off) and then submit the contents of the form with a button.

The name option lets you name the variable that will contain the state you want to retrieve in the Arduino code. Here LED.

</body> Any open tag must be closed (better!)

The handleRoot function is used to monitor whether a request to update the GPIO is received by monitoring whether the LED argument is returned by the page. If so, execute the handleSubmit function. It’s up to us

void handleRoot(){ 
  if ( server.hasArg("LED") ) {
    handleSubmit();
  } else {
    server.send ( 200, "text/html", getPage() );
  }  
}

The handleSubmit function processes updating the GPIO. The status of the LED variable is retrieved. It is a chain of character, so we must test “1” and not 1. We take advantage of it to affect the state of the GPIO in the variable etatLed in the form of a string, it is more fun to read. Finally we update the display of the HTML page with server.send. The updated page is retrieved by calling the getPage () function.

void handleSubmit() {
  // Actualise le GPIO / Update GPIO 
  String LEDValue;
  LEDValue = server.arg("LED");
  Serial.println("Set GPIO "); Serial.print(LEDValue);
  if ( LEDValue == "1" ) {
    digitalWrite(LEDPIN, 1);
    etatLed = "On";
    server.send ( 200, "text/html", getPage() );
  } else if ( LEDValue == "0" ) {
    digitalWrite(LEDPIN, 0);
    etatLed = "Off";
    server.send ( 200, "text/html", getPage() );
  } else {
    Serial.println("Err Led Value");
  }
}

Now that all functions are created, you can call the setup() function. It initializes the BMP180, the WiFi connection, connects the function that takes care of the main page and finally launches the web server

void setup() {
  Serial.begin ( 115200 );
  // Initialisation du BMP180 / Init BMP180
  if ( !bmp.begin() ) {
    Serial.println("BMP180 KO!");
    while(1);
  } else {
    Serial.println("BMP180 OK");
  }
  
  WiFi.begin ( ssid, password );
  // Attente de la connexion au réseau WiFi / Wait for connection
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 ); Serial.print ( "." );
  }
  // Connexion WiFi établie / WiFi connexion is OK
  Serial.println ( "" ); 
  Serial.print ( "Connected to " ); Serial.println ( ssid );
  Serial.print ( "IP address: " ); Serial.println ( WiFi.localIP() );

  // On branche la fonction qui gère la premiere page / link to the function that manage launch page 
  server.on ( "/", handleRoot );

  server.begin();
  Serial.println ( "HTTP server started" );
}

All that remains is to execute the loop() function to regularly read the measurements on the sensors. Unlike the previous example based on the ESP8266WiFi library, here the ESP8266WebServer needs to connect the callback function server.handleClient() which monitors the presence of a client and delivers the requested HTML page.

void loop() {
  server.handleClient();
  t = dht.readTemperature();
  h = dht.readHumidity();
  p = bmp.readPressure() / 100.0F;
  delay(1000);
}

Here is the assembled source code of the project that you simply paste into a new project and then upload it

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>

#define ssid      "xx"       // WiFi SSID
#define password  "xxxxxx"  // WiFi password
#define DHTTYPE   DHT22       // DHT type (DHT11, DHT22)
#define DHTPIN    D4          // Broche du DHT / DHT Pin
#define LEDPIN    D3          // Led
float   t = 0 ;
float   h = 0 ;
float   p = 0;
String  etatLed = "OFF";

// Création des objets / create Objects
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP085 bmp;
ESP8266WebServer server ( 80 );

String getPage(){
  String page = "<html lang=fr-FR><head><meta http-equiv='refresh' content='10'/>";
  page += "<title>ESP8266 Demo - www.projetsdiy.fr</title>";
  page += "<style> body { background-color: #fffff; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }</style>";
  page += "</head><body><h1>ESP8266 Demo</h1>";
  page += "<h3>DHT22</h3>";
  page += "<ul><li>Temperature : ";
  page += t;
  page += "°C</li>";
  page += "<li>Humidite : ";
  page += h;
  page += "%</li></ul><h3>BMP180</h3>";
  page += "<ul><li>Pression atmospherique : ";
  page += p;
  page += " mbar</li></ul>";
  page += "<h3>GPIO</h3>";
  page += "<form action='/' method='POST'>";
  page += "<ul><li>D3 (etat: ";
  page += etatLed;
  page += ")";
  page += "<INPUT type='radio' name='LED' value='1'>ON";
  page += "<INPUT type='radio' name='LED' value='0'>OFF</li></ul>";
  page += "<INPUT type='submit' value='Actualiser'>";
  page += "<br><br><p><a hrf='https://diyprojects.io'>diyprojects.io</p>";
  page += "</body></html>";
  return page;
}
void handleRoot(){ 
  if ( server.hasArg("LED") ) {
    handleSubmit();
  } else {
    server.send ( 200, "text/html", getPage() );
  }  
}

void handleSubmit() {
  // Actualise le GPIO / Update GPIO 
  String LEDValue;
  LEDValue = server.arg("LED");
  Serial.println("Set GPIO "); Serial.print(LEDValue);
  if ( LEDValue == "1" ) {
    digitalWrite(LEDPIN, 1);
    etatLed = "On";
    server.send ( 200, "text/html", getPage() );
  } else if ( LEDValue == "0" ) {
    digitalWrite(LEDPIN, 0);
    etatLed = "Off";
    server.send ( 200, "text/html", getPage() );
  } else {
    Serial.println("Err Led Value");
  }
}

void setup() {
  Serial.begin ( 115200 );
  // Initialisation du BMP180 / Init BMP180
  if ( !bmp.begin() ) {
    Serial.println("BMP180 KO!");
    while(1);
  } else {
    Serial.println("BMP180 OK");
  }
  
  WiFi.begin ( ssid, password );
  // Attente de la connexion au réseau WiFi / Wait for connection
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 ); Serial.print ( "." );
  }
  // Connexion WiFi établie / WiFi connexion is OK
  Serial.println ( "" ); 
  Serial.print ( "Connected to " ); Serial.println ( ssid );
  Serial.print ( "IP address: " ); Serial.println ( WiFi.localIP() );

  // On branche la fonction qui gère la premiere page / link to the function that manage launch page 
  server.on ( "/", handleRoot );

  server.begin();
  Serial.println ( "HTTP server started" );
  
}

void loop() {
  server.handleClient();
  t = dht.readTemperature();
  h = dht.readHumidity();
  p = bmp.readPressure() / 100.0F;
  delay(1000);
}

Retrieve the IP address of the Wemos by opening the serial monitor and then connect to it from a web browser to access the interface of the mini weather station.

Here, we now know how to create a web server using an ESP8266, drive the GPIO and display measurements from sensors. We have seen here only the basic rudiments to begin but they already cover much of what is needed to develop small connected objects. In the next tutorial we will see how to have a better graphical interface using Bootstrap, a framework developed by a developer working at Tweeter which allows to create more beautiful graphical interfaces in HTML.

Here are other articles that may interest you to go further from this tutorial. Good developments!

Note. All the tutorial was developed on the IDE Arduino 1.8.1 installed on an Orange Pi Plus 2e (presented here) running under Armbian (Ubuntu 16.04 LTS). Follow this tutorial to learn how to install the Arduino IDE on Linux (ARM or x86).

Subscribe to the weekly newsletter

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

  • Its code works as you move along the article, but the final code isnt working

    • Thank you Shivam. I’ll check that.

    • Exact, I had forgotten to declare de library ! #include . Thank you very much Shivam.

  • Gerard Venter

    Thanks for the project. I have compiled and uploaded the final sketch, and as I did not have a BMP180 handy I commented out the check to prevent it going into a KO loop. Everything seems fine. I get feedback on the serial monitor. I get “Connected to NETGEARXX
    IP address: 192.168.1.35
    HTTP server started” but when I try to access the ip address it times out. First time I experience this with the ESP8266. Turned off firewall and antivirus. Still times out. Any suggestions are appreciated. I also tried commenting out the p = bmp.readPressure() / 100.0F; but this too did not help.

    • Thank you very much Gerard. Have you changed the entry point? You must enter the page you want to reach on the web server that runs on esp8266. for example 192.168.1.35/measures. In my opinion, your code is correct. I hope this will help you solve the problem 🙂

      • Darshan Palsamkar

        Hello,
        All code is running properly but the led is not toggling please Help..

    • Gerard Venter

      Solved my problem. Connected to a different network AP and it works! Now to fing the issue on my router WiFi. Thanks.

      • Awesome. Thank you very much for your return. I hope to see you soon for other projects.

  • flagtrax

    The line :
    String req = client.readStringUntil(‘r’);
    generates a compiler error ¨ client was not declared in this scope¨
    Using arduino IDE 1.8.4
    Any suggestions?

DIY Projects