The ESP32‘s built-in WiFi makes it an ideal microcontroller for creating standalone web servers to control devices from any browser. This comprehensive guide walks you through building a responsive web server with the Arduino IDE to control outputs like LEDs or relays. The server operates on your local network and can be accessed from smartphones, tablets, or computers. Updated for 2026, this tutorial provides the complete code, step-by-step wiring, and an in-depth explanation of how it all works.

Project Overview: Your First IoT Control Panel
This project transforms your ESP32 into a dedicated web server that provides a simple, mobile-friendly interface. You will build a system where:
-
Two LEDs (representing any output device) are connected to the ESP32‘s GPIO pins.
-
The ESP32 connects to your local WiFi network.
-
You type the board’s IP address into any web browser to access a control page.
-
Clicking buttons on the webpage instantly turns the connected LEDs ON or OFF.
This foundational project demonstrates the core principles of IoT control, which you can later scale to manage relays for lights, motors, sensors, or other home automation devices.
Prerequisites and Hardware Setup
Software Requirements
-
Arduino IDE (version 2.x or later recommended) installed on your computer.
-
ESP32 Board Package installed in the Arduino IDE. You can install it via Tools > Board > Boards Manager... and searching for “ESP32 by Espressif Systems”.
-
Basic familiarity with uploading code to the ESP32.
Hardware Components
You will need the following components:
Circuit Wiring Diagram
Connect the components as shown below. Always double-check your connections before powering the circuit.
(Insert clear diagram or schematic here showing:
-
ESP32 GPIO26 → Resistor → LED Anode (Long leg); LED Cathode → GND.
-
ESP32 GPIO27 → Resistor → Second LED Anode; LED Cathode → GND.
-
ESP32 Vin/GND to breadboard power rails if needed.
)
⚠️ Important: The ESP32‘s GPIO pins are not 5V tolerant. Use the correct 3.3V logic levels. Ensure the longer leg (anode) of the LED connects to the GPIO pin via a resistor, and the shorter leg (cathode) connects to ground.
The Complete ESP32 Web Server Code
Copy the complete code below into your Arduino IDE. You must update the ssid and password variables with your own WiFi network credentials before uploading.
#include <WiFi.h>
#include <WiFiClient.h>
const char* ssid = "YOUR_WIFI_NETWORK_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
WiFiServer server(80);
const int ledPin_26 = 26;
const int ledPin_27 = 27;
String state_26 = "off";
String state_27 = "off";
String header;
unsigned long currentTime = millis();
unsigned long previousTime = 0;
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
pinMode(ledPin_26, OUTPUT);
pinMode(ledPin_27, OUTPUT);
digitalWrite(ledPin_26, LOW);
digitalWrite(ledPin_27, LOW);
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected successfully!");
Serial.print("Board IP Address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
if (client) {
Serial.println("New Client Connected.");
String currentLine = "";
currentTime = millis();
previousTime = currentTime;
while (client.connected() && (currentTime - previousTime <= timeoutTime)) {
currentTime = millis();
if (client.available()) {
char c = client.read();
Serial.write(c);
header += c;
if (c == '\n') {
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("Turning GPIO 26 ON");
state_26 = "on";
digitalWrite(ledPin_26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
Serial.println("Turning GPIO 26 OFF");
state_26 = "off";
digitalWrite(ledPin_26, LOW);
} else if (header.indexOf("GET /27/on") >= 0) {
Serial.println("Turning GPIO 27 ON");
state_27 = "on";
digitalWrite(ledPin_27, HIGH);
} else if (header.indexOf("GET /27/off") >= 0) {
Serial.println("Turning GPIO 27 OFF");
state_27 = "off";
digitalWrite(ledPin_27, LOW);
}
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<title>ESP32 Web Server</title>");
client.println("<style>");
client.println("html {font-family: Arial, sans-serif; display: inline-block; text-align: center;}");
client.println(".button {background-color: #4CAF50; border: none; color: white; padding: 15px 32px;");
client.println("text-decoration: none; display: inline-block; font-size: 18px; margin: 4px 2px; cursor: pointer;}");
client.println(".button-off {background-color: #555555;}");
client.println("</style></head>");
client.println("<body>");
client.println("<h1>ESP32 Web Server Control Panel</h1>");
client.println("<p>LED on GPIO 26 is: <strong>" + state_26 + "</strong></p>");
if (state_26 == "off") {
client.println("<p><a href=\"/26/on\"><button class=\"button\">TURN ON</button></a></p>");
} else {
client.println("<p><a href=\"/26/off\"><button class=\"button button-off\">TURN OFF</button></a></p>");
}
client.println("<p>LED on GPIO 27 is: <strong>" + state_27 + "</strong></p>");
if (state_27 == "off") {
client.println("<p><a href=\"/27/on\"><button class=\"button\">TURN ON</button></a></p>");
} else {
client.println("<p><a href=\"/27/off\"><button class=\"button button-off\">TURN OFF</button></a></p>");
}
client.println("</body></html>");
client.println();
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
}
}
header = "";
client.stop();
Serial.println("Client disconnected.");
Serial.println();
}
}
How to Use and Test Your Web Server
1. Upload the Code
-
In the Arduino IDE, select your ESP32 board under Tools > Board.
-
Select the correct COM port under Tools > Port.
-
Click the upload button.
2. Find the ESP32‘s IP Address
-
Open the Serial Monitor (Tools > Serial Monitor).
-
Set the baud rate to 115200.
-
Press the EN/RST button on your ESP32 board.
-
Watch the output. After “WiFi connected successfully!”, you will see a line like:
Board IP Address: 192.168.1.XXX
Copy this IP address.
3. Access the Control Panel
-
On any device connected to the same local WiFi network (phone, laptop, tablet), open a web browser (Chrome, Safari, Firefox, etc.).
-
In the browser’s address bar, type the IP address you copied (e.g., http://192.168.1.XXX) and press Enter.
-
The ESP32 control panel webpage should load, displaying two buttons for controlling the LEDs.
4. Test the Functionality
-
Click the “TURN ON” button for GPIO 26. The corresponding LED should light up, and the webpage will update its state to “on”.
-
Click the “TURN OFF” button to turn it off.
-
Repeat for GPIO 27. Observe the Serial Monitor to see the real-time HTTP requests (GET /26/on) as you click.
How the Code Works: A Technical Deep Dive
1. Network Foundation
The WiFi.h library provides all necessary functions. The WiFiServer server(80); object listens for incoming connections on port 80, the standard port for HTTP traffic. The connection process in setup() is blocking but includes visual feedback via the Serial Monitor.
2. Handling Client Requests
The core of the server is in the loop(). The server.available() function checks for a new client. When a browser connects, it sends an HTTP GET request. The code reads this request character by character, storing it in the header string.
3. Parsing Requests and Controlling GPIOs
The program searches the incoming header for specific URL patterns:
-
"GET /26/on" → Sets GPIO 26 HIGH and updates state_26 to "on".
-
"GET /26/off" → Sets GPIO 26 LOW and updates state_26 to "off".
This is how a button click in the browser translates into a physical action on the ESP32.
4. Generating Dynamic HTML
After processing the request, the code constructs an HTML page on the fly. It uses the current state_XX variables to display the correct status and button (ON or OFF). The CSS styling within the <style> tags makes the page responsive and clean.
5. Connection Management
The timeoutTime constant prevents the server from hanging if a client disconnects unexpectedly. The connection is explicitly closed with client.stop() after sending the HTML page, freeing up resources for the next request.
Troubleshooting Common Issues
Next Steps: Expanding Your Project
This web server is a foundational blueprint. You can expand it by:
-
Adding More Outputs: Control relays, servos, or LED strips by defining more GPIO pins and adding corresponding buttons/URLs in the code.
-
Reading Sensor Data: Display data from sensors (like DHT11 for temperature) on the webpage by adding their readings to the HTML generation block.
-
Adding Security: Implement basic access control or move to more advanced web server frameworks like AsyncTCP and ESPAsyncWebServer for handling multiple connections efficiently.
-
Accessing Remotely: Use port forwarding on your router or a cloud service (with appropriate security measures) to control your ESP32 from outside your home network.
By mastering this standalone web server, you’ve taken a significant step into the world of IoT with the ESP32. The principles you’ve learned here—handling HTTP requests, generating dynamic content, and controlling hardware—form the basis for countless home automation and monitoring projects.