Weekly tutorial: WebServer.h and ESP32

By Savalio (Slightly edited by Swee post-migration to Jekyll)

If you want to find more in-depth tutorial for this cool web server library you found, you’re in the right place! I’ll teach you everything you need to make your own STA-mode web server!

Introduction

This library is like ESPAsyncWebServer.h, but much more simple.
It takes less lines of code and less parameters to write code with the same functionality in WebServer.h than in the ESPAsyncWebServer.h. So, let’s dive in!

Part 1: Hardware prep

You won’t need much for this project since it’s more software-focused one. Supplies:

  • ESP32-compatible board. I am using my favorite teeny-tiny Xiao ESP32-S3 Sense for this, but it can work with any ESP32 board.

  • Programming cable (duh)

Optional:

  • External LED

NOTE: If you don’t want to change code much, use an ESP32 board with a built-in user LED.

Part 2: Code Prep and Functionality

Now that you have your beloved ESP32, we can get to the code! Here’s the code, just paste it into the Arduino IDE and change the ssid and password variables to your credentials.

/*
CODE BY SAVALIO
 
https://blogs.swee.codes/team#Savalio
*/
#include <WiFi.h> // All the nessesary libraries
#include <WebServer.h>
 
// CHANGE TO YOUR SSID AND PASSWORD //
const char* ssid = "..."; // WiFi name
const char* password = "..."; // WiFi password
 
bool LEDState = false; // Sets up a boolean to control the LED's state
String displayText = "You have not set the display text yet..."; // Sets up a string for the display text
 
WebServer server(80); // Declares the WebServer object to use the functions
 
void setup() {
  Serial.begin(115200); // Begins the Serial monitor
  pinMode(LED_BUILTIN, OUTPUT); // Sets the pin of the built-in LED to output
 
  Serial.println("Connecting to WiFi:"); // Prints out the name of the network ESP32 is trying to connect
  Serial.println(ssid);
  WiFi.begin(ssid, password); // Connect to your local WiFi network
 
  // Checks if your ESP32 is connected to WiFi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
 
  Serial.println("Connected!"); 
  Serial.println("Local IP:"); Serial.println(WiFi.localIP()); // Prints out the Local IP so you can view the web page
 
  server.on("/", handle_OnConnect); // If user is on the home page, then call handle_OnConnect
  server.on("/setLEDState", handle_SetLEDState); // If the user is on the setLEDState page, then call handle_SetLEDState
  server.on("/setHomeText", handle_SetHomeText); // If the user is on the setHomeText page, then call handle_SetHomeText
  server.onNotFound(handle_NotFound); // If the user is on any other page, tell them that the page was not found
  server.begin(); // Gets the server up and running
  Serial.println("HTTP server started");
 
}
 
void loop() {
  server.handleClient(); // THIS MUST BE IN THE LOOP! IT HANDLES THE CLIENT
  if (LEDState) { // Switches on/off the LED based off of the LEDState boolean
    digitalWrite(LED_BUILTIN, HIGH);
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }
}
 
void handle_OnConnect() {
  String html = String("<center> \n") + String("<h1>ESP32 Web Server tutorial example</h1>");
  html += String("<h4>Display text: ") + displayText + String("</h4>"); // Adding the user set display text at the top 
  html += R"(
    <form action="/setLEDState">
      <input type="submit" value="Turn on/off the built-in LED">
    </form>
    <form action="/setHomeText">
      <label for="txt">Display text:</label>
      <input type="text" id="txt" name="txt" placeholder="Blahblahblah...">
      <input type="submit" value="Set text">
    </form>
    </center>
  )";
  server.send(200, "text/html", html); // Sends the html web page to the user
}
 
void handle_SetLEDState() {
  String html; // Defines the html object in the function so errors won't scare you
 
  if (LEDState) { // Switches the LED state
    html = R"(
      <center>
      <h1>Succesfully turned off the Built-in LED!</h1>
      <form action="/">
        <input type="submit" value="Go home">
      </form>
      </center>
    )";
    LEDState = false;
  } else {
    html = R"(
      <center>
      <h1>Succesfully turned on the Built-in LED!</h1>
      <form action="/">
        <input type="submit" value="Go home">
      </form>
      </center>
    )";
    LEDState = true;
  }
  server.send(200, "text/html", html); // Sends the html web page to the user
}
 
void handle_SetHomeText() {
  displayText = server.arg("txt"); // Sets the display text to the server argument txt
  String html = R"(
    <center>
    <h1>Succesfully turned off the Built-in LED!</h1>
    <form action="/">
      <input type="submit" value="Go home">
    </form>
    </center>
  )";
  server.send(200, "text/html", html); // Sends the html web page to the user
}
 
void handle_NotFound() {
  server.send(200, "text/html", "<h1>404: Page Not Found</h1>");
}

Now, upload the code to the ESP32, and look at the serial monitor and search for the local IP. It should look something like this:

Local IP:
10.0.0.90

Type in the IP into your browser window. It should look like this:

If you click on the top button, you will see this:

Now, look at your ESP32. It should look like this:

On the other hand, if you enter “Hello!” into the input field and press “Set Text” button and press “Go Home” in the next web page, you should see this:

Part 3: Code Explanation

Part 3.1:

void setup()

In this function, we connect to the WiFi network with the WiFi.begin(ssid, password) function, and wait until we connect and then print out the Local IP.

Then, we set the functionality for the web pages and begin the web server with this chunk of code:

server.on("/", handle_OnConnect);
server.on("/setLEDState", handle_SetLEDState); 
server.on("/setHomeText", handle_SetHomeText); 
server.onNotFound(handle_NotFound);
server.begin();

If you have noticed, the server.on function calls the functions without requiring parenthesis, which inhibits you from providing any variables, which you, most likely, will never need.

Here’s the structure of the functions we’ve discussed:

server.on(String page, functionName);
server.onNotFound(functionName);

Part 3.2:

void loop()

There’s nothing much to talk about. You can see that there’s server.handleClient() function, which has no arguments. It it self-explanatory: it handles clients (shocking).

Part 3.3: Special functions

Now we’re in the interesting territory. We have the rawliteral String named html and this function:

server.send(200, "text/html", html)

The first argument is the HTTP status code, 200 meaning it was successful.

The second argument is the mimetype (if you’ve played with Linux before, you’ve probably heard of this), it tells the browser what type of file the path is using, text/html is the mimetype for HTML. (Plain text mimetype will be text/plain)

The last argument is the content of the file, it is self-explanatory.

server.send(200, String fileType, String content);

And in the function handle_SetHomeText() you will find this function setting the displayText String:

displayText = server.arg("txt");

This function searches for an argument you define in the parenthesis.
The arguments are defined after the question mark like in this screenshot of a YouTube video argument (e.g. https://youtube.com/watch?v=<id>)

The arguments let us save the data we entered in this html block to a variable:

<form action="/setHomeText">
 <label for="txt">Display text:</label>
 <input type="text" id="txt" name="txt" placeholder="Blahblahblah...">
 <input type="submit" value="Set text">
</form>

Here’s the structure of the server.arg():

String var = server.arg(String arg);

Now what?

You can modify the code to have more arguments for a web page, style the web page with CSS, use it to play/pause GIFs on the Arduino’s TFT with the AnimatedGIF.h library, controlling relays, and more!

If you liked the tutorial, subscribe to the newsletter for more tutorials like this!

Comments

© 2025 Swee, Savalio, and other SweeBlogs contributors.
Made with Jekyll