Using the Arduino IDE programming environment, we’ll develop a standalone web server with an ESP32 that controls outputs (two LEDs). The web server is mobile responsive and may be viewed from any device on the local network that has a browser. We’ll explain the process of setting up the web server and understand how the code works.
PROJECT OVERVIEW
- The web server you’ll create controls two LEDs linked to the ESP32 GPIO 26 and GPIO 27
- you can access the ESP32 web server by typing the ESP32 IP address into a local network browser
- you can alter the state of each LED by clicking the buttons on your web server.
PARTS REQUIRED
For this tutorial you’ll need the following parts:
- ESP32 development board – ESP32 is a family of low-cost, low-power system-on-a-chip microcontrollers that have built-in Wi-Fi and dual-mode Bluetooth. Espressif Systems, a Shanghai-based Chinese firm, designed and developed the ESP32, which is manufactured by TSMC using their 40 nm technology. It is the ESP8266 microcontroller’s successor.
- 2x 5mm LED
- 2x 330 Ohm resistor
- Breadboard
- Jumper wires
SCHEMATIC
CODE
Copy the following code to your Arduino IDE, but don’t upload it yet. You need to make some changes to make it work for you.
// Load Wi-Fi library #include <WiFi.h> // Replace with your network credentials const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // Set web server port number to 80 WiFiServer server(80); // Variable to store the HTTP request String header; // Auxiliar variables to store the current output state String output26State = "off"; String output27State = "off"; // Assign output variables to GPIO pins const int output26 = 26; const int output27 = 27; // Current time unsigned long currentTime = millis(); // Previous time unsigned long previousTime = 0; // Define timeout time in milliseconds (example: 2000ms = 2s) const long timeoutTime = 2000; void setup() { Serial.begin(115200); // Initialize the output variables as outputs pinMode(output26, OUTPUT); pinMode(output27, OUTPUT); // Set outputs to LOW digitalWrite(output26, LOW); digitalWrite(output27, LOW); // Connect to Wi-Fi network with SSID and password Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Print local IP address and start web server Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin(); } void loop(){ WiFiClient client = server.available(); // Listen for incoming clients if (client) { // If a new client connects, currentTime = millis(); previousTime = currentTime; Serial.println("New Client."); // print a message out in the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected currentTime = millis(); if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor header += c; if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); // turns the GPIOs on and off if (header.indexOf("GET /26/on") >= 0) { Serial.println("GPIO 26 on"); output26State = "on"; digitalWrite(output26, HIGH); } else if (header.indexOf("GET /26/off") >= 0) { Serial.println("GPIO 26 off"); output26State = "off"; digitalWrite(output26, LOW); } else if (header.indexOf("GET /27/on") >= 0) { Serial.println("GPIO 27 on"); output27State = "on"; digitalWrite(output27, HIGH); } else if (header.indexOf("GET /27/off") >= 0) { Serial.println("GPIO 27 off"); output27State = "off"; digitalWrite(output27, LOW); } // Display the HTML web page client.println("<!DOCTYPE html><html>"); client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"); client.println("<link rel=\"icon\" href=\"data:,\">"); // CSS to style the on/off buttons // Feel free to change the background-color and font-size attributes to fit your preferences client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"); client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;"); client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}"); client.println(".button2 {background-color: #555555;}</style></head>"); // Web Page Heading client.println("<body><h1>ESP32 Web Server</h1>"); // Display current state, and ON/OFF buttons for GPIO 26 client.println("<p>GPIO 26 - State " + output26State + "</p>"); // If the output26State is off, it displays the ON button if (output26State=="off") { client.println("<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>"); } // Display current state, and ON/OFF buttons for GPIO 27 client.println("<p>GPIO 27 - State " + output27State + "</p>"); // If the output27State is off, it displays the ON button if (output27State=="off") { client.println("<p><a href=\"/27/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/27/off\"><button class=\"button button2\">OFF</button></a></p>"); } client.println("</body></html>"); // The HTTP response ends with another blank line client.println(); // Break out of the while loop break; } else { // if you got a newline, then clear currentLine currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } } } // Clear the header variable header = ""; // Close the connection client.stop(); Serial.println("Client disconnected."); Serial.println(""); } }
Setting Your Network Credentials
You need to modify the following lines with your network credentials: SSID and password. The code is well commented on where you should make the changes.
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Uploading the Code
Now, you can upload the code and the web server will work straight away. Follow the next steps to upload code to the ESP32:
1) Plug your ESP32 board in your computer;
2) In the Arduino IDE select your board in Tools > Board (in our case we’re using the ESP32 DEVKIT DOIT board);
3) Select the COM port in Tools > Port.
4) Press the Upload button in the Arduino IDE and wait a few seconds while the code compiles and uploads to your board.
5) Wait for the “Done uploading” message.
Finding the ESP IP Address
After uploading the code, open the Serial Monitor at a baud rate of 115200.
Activate the ESP32 EN button (reset). The ESP32 establishes a Wi-Fi connection and displays the ESP IP address on the Serial Monitor. You’ll need that IP address to connect to the ESP32 web server.
Accessing the Web Server
To access the web server, open your browser and put the ESP32 IP address into the address bar. It’s 192.168.1.135 in this situation.
You can see what’s going on in the background if you glance at the Serial Monitor. A new client sends an HTTP request to the ESP (in this case, your browser).
Testing the Web Server
Now you can test if your web server is working properly. Click the buttons to control the LEDs.
You can peek at the Serial Monitor at the same time to observe what’s going on in the background. When you press the button to turn GPIO 26 ON, for example, ESP32 receives a request on the /26/on URL.
The ESP32 responds by turning on the LED connected to GPIO 26 and updating its status on the web page.
The GPIO 27 button works in a similar way. Check to see if it’s operating properly.
HOW CODE WORKS
The first thing you need to do is to include the WiFi library.
#include <WiFi.h>
As mentioned previously, you need to insert your ssid and password in the following lines inside the double quotes.
const char* ssid = "";
const char* password = "";
Then, you set your web server to port 80.
WiFiServer
serve
r
(
80
)
;
The following line creates a variable to store the header of the HTTP request:
String header;
Then you establish auxiliar variables to keep track of your outputs’ current status. We’ll need to establish more variables if you want to add more outputs and store the state.
String output26State = "off";
String output27State = "off";
You also need to assign a GPIO to each of your outputs. Here we are using GPIO 26 and GPIO 27..
const int output26 = 26;
const int output27 = 27;
setup()
Now, let’s go into the setup(). First, we start a serial communication at a baud rate of 115200 for debugging purposes.
Serial.begin(115200);
You also define your GPIOs as OUTPUTs and set them to LOW.
// Initialize the output variables as outputs
pinMode(output26, OUTPUT);
pinMode(output27, OUTPUT);
// Set outputs to LOW
digitalWrite(output26, LOW);
digitalWrite(output27, LOW);
The following lines begin the Wi-Fi connection with WiFi.begin(ssid, password), wait for a successful connection and print the ESP IP address in the Serial Monitor.
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
loop()
In the loop() we program what happens when a new client establishes a connection with the web server.
The ESP32 is always listening for incoming clients with the following line:
WiFiClient client = server.available(); // Listen for incoming clients
When a request is received from a client, we’ll save the incoming data. The while loop that follows will be running as long as the client stays connected. We don’t recommend changing the following part of the code unless you know exactly what you are doing.
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
/ that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
The next set of if and else lines determines which button in your web page was pressed and adjusts the outputs accordingly. As we’ve seen before, depending on which button is pushed, we make requests to different URLs.
// turns the GPIOs on and off
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("GPIO 26 on");
output26State = "on";
digitalWrite(output26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
Serial.println("GPIO 26 off");
output26State = "off";
digitalWrite(output26, LOW);
} else if (header.indexOf("GET /27/on") >= 0) {
Serial.println("GPIO 27 on");
output27State = "on";
digitalWrite(output27, HIGH);
} else if (header.indexOf("GET /27/off") >= 0) {
Serial.println("GPIO 27 off");
output27State = "off";
digitalWrite(output27, LOW);
}
If you click the GPIO 26 ON button, for example, the ESP32 receives a request on the /26/ON URL (we can see that information on the HTTP header on the Serial Monitor). So we can see if the header has the GET /26/on expression. If it does, the output26state variable is set to ON, and the ESP32 turns on the LED.
This works similarly for the other buttons. So, if you want to add more outputs, you should modify this part of the code to include them.
Displaying the HTML web page
The following step is to construct the web page. The ESP32 will respond to your browser with HTML code that will be used to construct the web page.
This expressive client is used to send the web page to the client.println()
. As an argument, you should type what you want to communicate to the client.
The first thing we should send is always the following line, which indicates that we are sending HTML.
<!DOCTYPE HTML><html>
Then, the following line makes the web page responsive in any web browser.
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
And the following is used to prevent requests on the favicon. – You don’t need to worry about this line.
client.println("<link rel=\"icon\" href=\"data:,\">");
Styling the Web Page
After that, we have some CSS text to design the buttons and the appearance of the web page. We use the Helvetica font and set the information to be shown as a block with the centre aligned.
client.
println
(
"<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"
);
We style our buttons with the #4CAF50 color, without border, text in white color, and with this padding: 16px 40px. We also set the text-decoration to none, define the font size, the margin, and the cursor to a pointer.
client.
println
(
".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;"
);
client.
println
(
"text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}"
);
We also define the style for a second button, with all the properties of the button we’ve defined earlier, but with a different color. This will be the style for the off button.
client.
println
(
".button2 {background-color: #555555;}</style></head>"
);
Setting the Web Page First Heading
In the next line you can set the first heading of your web page. Here we have “ESP32 Web Server”, but you can change this text to whatever you like.
// Web Page Heading
client.
println
(
"<h1>ESP32 Web Server</h1>"
);
Displaying the Buttons and Corresponding State
Then, you write a paragraph to display the GPIO 26 current state. As you can see we use the output26State variable, so that the state updates instantly when this variable changes.
client.
println
(
"<p>GPIO 26 - State " + output26State + "</p>"
);
Then, we display the on or the off button, depending on the current state of the GPIO. If the current state of the GPIO is off, we show the ON button, if not, we display the OFF button.
if
(output26State
==
"off"
) {
client.
println
(
"<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>"
);
}
else
{
client.
println
(
"<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>"
);
}
We use the same procedure for GPIO 27.
Closing the Connection
Finally, when the response ends, we clear the header variable, and stop the connection with the client with client.stop().
// Clear the header variable
header
=
""
;
// Close the connection
client.
stop
();
CONCLUSION
In this tutorial, we’ve shown you how to build a web server with the ESP32. We’ve shown you a simple example that controls two LEDs,