In this post, we are going to start to see the use of ESP8266 and ESP32 as a server, one of the most common roles in which we are going to use it and, as we anticipate, will take up quite a few posts.
We will refer to the ESP8266, but the same code is compatible for the ESP32, adjusting the name of the libraries. At the end you have the code for both the ESP8266 and the ESP32.
In the previous post about ESP8266 we saw how it works as a client and emphasized that, although it is sometimes a forgotten role, it is as important as that of the server.
Remember that in an M2M (machine to machine) the client is the device that initiates the connection. On the other hand, the server receives requests from the clients, performs the appropriate actions in the backend, and returns a response.
Why use it as a server?
In this case, we are going to use the ESP8266 as a server. Therefore, the ESP8266 server will be permanently on and receive requests from clients (for example, a computer, a mobile phone or another ESP8266).
The clients connect to the ESP8266 server and make an HTTP request to a URI. Optionally, they send information about the request as parameters.
The ESP8266 server receives and processes the request, performs the appropriate actions in the backend (turn on an LED, move a motor, read a sensor) and returns a response to the client.
Many times, the server will return an HTML file to be rendered on the client (frontend). But it should be noted that this is not always the case. Some (or all) of the URIs may simply be endpoints that offer an API to the client.
As we can see, there is quite a bit to explain, and it is going to take up quite a few posts, from easiest to hardest. We start with the simplest, like setting up a simple server with the ESP8266.
ESP8266 as a simple server
Similar to using it as a client and the ESP8266HTTPClient library, using the ESP8266 as a server is very simple thanks to the work of the community in the development of the ESP8266WebServer library.
To use it, we must first instantiate an object of the ESP8266WebServer class through one of its constructors.
ESP8266WebServer(IPAddress addr, int port = 80);
ESP8266WebServer(int port = 80);
And start it through one of its ‘begin(…)’ functions.
void begin();
void begin(uint16_t port);
Next, we have to define the “routes”, that is, the association between the URI of the request and the callback action that will be executed. For this, we have the ‘on(…)’ functions, which establish the routes.
Later, in the Loop, we simply call the ‘handleClient()’ function which is responsible for receiving requests from the clients and launching the associated callback functions in the routing.
To send the response to the client, we use the ‘send(…)’ functions. Finally, we can close the client’s connection with ‘close()’ or stop the server altogether with ‘stop()‘.
In the case of the ESP32, the library is called ‘ESP8266WebServer’ is simply called ‘WebServer’.
Example of ESP8266 as a server
We will see it better with a practical example, a typical and simple ‘Hello world’. In this example, we connect to WiFi, and create a server on port 80. Then we define 2 endpoints, ’/’ and ‘/inline’, and a default function for the case where an unrecognized URI is requested.
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include "config.h" // Replace with your network data
#include "ESP8266_Utils.hpp"
ESP8266WebServer server(80);
// Function to be executed on the URI '/'
void handleRoot()
{
server.send(200, "text/plain", "Hello, world!");
}
// Function to be executed on an unknown URI
void handleNotFound()
{
server.send(404, "text/plain", "Not found");
}
void setup(void)
{
Serial.begin(115200);
ConnectWiFi_STA();
// Routing for '/'
server.on("/", handleRoot);
// Routing for '/inline' using lambda function
server.on("/inline", []() {
server.send(200, "text/plain", "This also works");
});
// Routing for unknown URI
server.onNotFound(handleNotFound);
// Start server
server.begin();
Serial.println("HTTP server started");
}
void loop()
{
server.handleClient();
}
If we load this program into the ESP8266, we will see that it indicates the IP address of our device.
Now, if we connect with a browser to this IP address, we will see the content served from the ESP8266.
Congratulations, you now have a properly configured server!
Simplified Example
As usual, we are going to split the code to make it easier to use and maintain. To the usual ‘config.h’ and ‘ESP8266_Utils.hpp’ files, we add the following,
The main program is
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
#include "config.h" // Replace with your network data
#include "ESP8266_Utils.hpp"
#include "Server.hpp"
void setup(void)
{
Serial.begin(115200);
ConnectWiFi_STA();
InitServer();
}
void loop()
{
server.handleClient();
}
A ‘Server.hpp’ file containing the logic associated with the server’s operation. In this example, it will have this content.
// Function to be executed on the URI '/'
void handleRoot()
{
server.send(200, "text/plain", "Hello, world!");
}
// Function to be executed on an unknown URI
void handleNotFound()
{
server.send(404, "text/plain", "Not found");
}
void InitServer()
{
// Routing for '/'
server.on("/", handleRoot);
// Routing for '/inline' using lambda function
server.on("/inline", []() {
server.send(200, "text/plain", "This also works");
});
// Routing for unknown URI
server.onNotFound(handleNotFound);
// Start server
server.begin();
Serial.println("HTTP server started");
}
ESP8266WebServer in detail
The ‘on(…)’ methods accept different overloads that allow, for example, to distinguish the type of received request.
void on(const String &uri, THandlerFunction handler);
void on(const String &uri, HTTPMethod method, THandlerFunction fn);
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads
For their part, the ‘send(…)’ functions also have different variants with different parameters
void send(int code, const char* content_type = NULL, const String& content = String(""));
void send(int code, char* content_type, const String& content);
void send(int code, const String& content_type, const String& content);
void send_P(int code, PGM_P content_type, PGM_P content);
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
Where:
- Code: HTTP response code (200, 301, 303, 404…)
- Type: HTTP content type (text/plain, text/html, text/json, image/png…)
- Content: The content of the response body.
We also have other functions to send data to the client that are useful, for example, to send the information in various instructions of the ESP8266.
void setContentLength(const size_t contentLength);
void sendHeader(const String& name, const String& value, bool first = false);
void sendContent(const String& content);
void sendContent_P(PGM_P content);
void sendContent_P(PGM_P content, size_t size);
On the other hand, we have functions to receive the parameters of the request, as we will see in the next post.
const String& arg(String name) const; // get request argument value by name
const String& arg(int i) const; // get request argument value by number
const String& argName(int i) const; // get request argument name by number
int args() const; // get arguments count
bool hasArg(const String& name) const; // check if argument exists
And functions to get and manage the headers of the HTTP request.
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
const String& header(String name) const; // get request header value by name
const String& header(int i) const; // get request header value by number
const String& headerName(int i) const; // get request header name by number
int headers() const; // get header count
bool hasHeader(String name) const; // check if header exists
const String& hostHeader() const; // get request host header if available or empty String if not
As you can see, the ESP8266WebServer class offers much more than it seems and turns the ESP8266, despite its memory and processing limitations, into a true server with a large number of options.
In the next post in the ESP8266 series, we will continue to delve into the ESP8266 as a server receiving and interpreting different types of requests and their parameters. See you soon!
Download the code
All the code in this post is available for download on Github.
Version for the ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples
Version for the ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples