
What is an HTTP Server?
Think of an HTTP server as a waiter in a restaurant:
- You (browser) ask for something → HTTP Request
- Waiter (HTTP server) brings you what you asked for → HTTP Response
- The waiter can bring you food (HTML pages), drinks (JSON data), or anything else!
On ESP32:
- Your ESP32 becomes a web server
- You can access it from any browser on the same WiFi network
- You type
http://192.168.0.120in browser → ESP32 responds with a webpage!
What Can You Do With HTTP Server?
Real-World Examples:
- Control LED from Phone 🔦
- Open browser on phone
- Click “Turn ON” button
- LED on ESP32 turns on!
- Display Sensor Data 🌡️
- ESP32 reads temperature sensor
- You open webpage
- See current temperature: “Temperature: 25°C”
- Smart Home Control 🏠
- Control lights, fans, garage door
- All from a web interface
- No app needed – just browser!
- Configuration Portal ⚙️
- First-time setup for WiFi credentials
- Enter SSID and password through web form
- ESP32 saves and connects
How HTTP Works (Simple Explanation)
The Request-Response Cycle
1. Browser says: "GET /hello" ↓ 2. ESP32 receives request ↓ 3. ESP32 finds handler for "/hello" ↓ 4. Handler function runs ↓ 5. ESP32 sends response: "Hello World!" ↓ 6. Browser displays: "Hello World!"
HTTP Methods (Like Different Types of Questions)
| Method | What It Means | Example |
|---|---|---|
| GET | “Give me something” | Get webpage, get sensor data |
| POST | “Here’s some data, save it” | Submit form, upload file |
| PUT | “Update this data” | Update settings |
| DELETE | “Remove this” | Delete a record |
For beginners: You’ll mostly use GET and POST!
Basic Concepts Explained
1. URI (Uniform Resource Identifier)
Think of it as addresses in your website:
http://192.168.0.120/ ← Homepage (/) http://192.168.0.120/led ← LED control page (/led) http://192.168.0.120/sensor ← Sensor data (/sensor) http://192.168.0.120/about ← About page (/about)
The part after the IP address is the URI path.
2. Handler Function
A function that handles a specific URI:
// When someone visits "/hello", this function runs
esp_err_t hello_handler(httpd_req_t *req)
{
httpd_resp_send(req, "Hello World!", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
Think of it like:
- Someone rings doorbell → You check who it is → You respond appropriately
- Browser visits /hello → ESP32 checks handler → Runs hello_handler()
3. Request (httpd_req_t)
Contains information about what the browser asked for:
- Which URI? (/led or /sensor?)
- What method? (GET or POST?)
- Any data sent? (form data, parameters)
- Who’s asking? (IP address, user agent)
4. Response
What you send back to the browser:
- HTML page
- JSON data
- Plain text
- Images, CSS, JavaScript
- Error message (404 Not Found)
Simple HTTP Server Example
Let’s build a complete working example step by step!
Step 1: Include Required Headers
#include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "nvs_flash.h" #include "esp_netif.h" #include "esp_http_server.h" // ← HTTP Server header! #include "lwip/err.h" #include "lwip/sys.h" static const char *TAG = "http_server";
Step 2: Create a Simple Handler
// Handler for homepage (/)
esp_err_t root_handler(httpd_req_t *req)
{
// The HTML to send
const char* html = "<html><body>"
"<h1>Hello from ESP32!</h1>"
"<p>Welcome to my web server</p>"
"</body></html>";
// Send the response
httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
What this does:
- Function is called when someone visits
http://192.168.0.120/ - Creates an HTML string
- Sends it to the browser
- Returns ESP_OK to indicate success
The parameters:
httpd_req_t *req= Request information (what browser asked for)- Return
ESP_OK= Everything went well - Return
ESP_FAIL= Something went wrong
Step 3: Register the Handler
// Define the URI and link it to the handler
httpd_uri_t root_uri = {
.uri = "/", // The path (homepage)
.method = HTTP_GET, // HTTP method (GET)
.handler = root_handler, // Function to call
.user_ctx = NULL // Optional: extra data to pass
};
Breaking this down:
.uri = "/"→ This handler responds to homepage.method = HTTP_GET→ Only responds to GET requests.handler = root_handler→ Calls this function.user_ctx = NULL→ We don’t need extra data (advanced feature)
Step 4: Start the Server
httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
// Server configuration
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80; // Standard HTTP port
config.lru_purge_enable = true; // Clean up old connections
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
// Start the server
if (httpd_start(&server, &config) == ESP_OK) {
// Register URI handlers
httpd_register_uri_handler(server, &root_uri);
ESP_LOGI(TAG, "Server started successfully!");
return server;
}
ESP_LOGE(TAG, "Failed to start server!");
return NULL;
}
What each part does:
httpd_handle_t server = NULL;
- Creates a variable to hold our server
NULLmeans “not started yet”
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
- Gets default server settings
- Port 80 (standard HTTP)
- Maximum 4 connections
- Other default settings
config.server_port = 80;
- Use port 80 (standard HTTP port)
- This means you can visit:
http://192.168.0.120(no port number needed) - If you use 8080:
http://192.168.0.120:8080
if (httpd_start(&server, &config) == ESP_OK) {
- Try to start the server
- If successful, register handlers
httpd_register_uri_handler(server, &root_uri);
- Tell server: “When someone visits ‘/’, call root_handler()”
Step 5: Complete Working Example
Here’s everything together:
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_http_server.h"
#include "freertos/event_groups.h"
#define WIFI_SSID "YourWiFiName"
#define WIFI_PASS "YourPassword"
#define WIFI_MAX_RETRY 5
static const char *TAG = "web_server";
static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
// ==================== HTTP HANDLERS ====================
// Handler for homepage
esp_err_t root_handler(httpd_req_t *req)
{
const char* html =
"<!DOCTYPE html>"
"<html>"
"<head>"
" <title>ESP32 Server</title>"
" <style>"
" body { font-family: Arial; margin: 40px; }"
" h1 { color: #333; }"
" button { padding: 10px 20px; font-size: 16px; margin: 10px; }"
" </style>"
"</head>"
"<body>"
" <h1>ESP32 Web Server</h1>"
" <p>Welcome! Server is running.</p>"
" <a href='/hello'><button>Say Hello</button></a>"
" <a href='/info'><button>Device Info</button></a>"
"</body>"
"</html>";
httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// Handler for /hello
esp_err_t hello_handler(httpd_req_t *req)
{
const char* html =
"<!DOCTYPE html>"
"<html>"
"<head><title>Hello</title></head>"
"<body>"
" <h1>Hello from ESP32!</h1>"
" <p>This is a simple response from your ESP32 server.</p>"
" <a href='/'>Back to Home</a>"
"</body>"
"</html>";
httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// Handler for /info
esp_err_t info_handler(httpd_req_t *req)
{
// Get device info
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
char html[512];
snprintf(html, sizeof(html),
"<!DOCTYPE html>"
"<html>"
"<body>"
" <h1>Device Information</h1>"
" <p>Chip: ESP32 with %d cores</p>"
" <p>Flash: %dMB</p>"
" <p>Free Heap: %d bytes</p>"
" <a href='/'>Back to Home</a>"
"</body>"
"</html>",
chip_info.cores,
spi_flash_get_chip_size() / (1024 * 1024),
esp_get_free_heap_size()
);
httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// ==================== URI DEFINITIONS ====================
httpd_uri_t uri_root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_handler,
.user_ctx = NULL
};
httpd_uri_t uri_hello = {
.uri = "/hello",
.method = HTTP_GET,
.handler = hello_handler,
.user_ctx = NULL
};
httpd_uri_t uri_info = {
.uri = "/info",
.method = HTTP_GET,
.handler = info_handler,
.user_ctx = NULL
};
// ==================== SERVER FUNCTIONS ====================
httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.lru_purge_enable = true;
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
if (httpd_start(&server, &config) == ESP_OK) {
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &uri_root);
httpd_register_uri_handler(server, &uri_hello);
httpd_register_uri_handler(server, &uri_info);
return server;
}
ESP_LOGI(TAG, "Error starting server!");
return NULL;
}
void stop_webserver(httpd_handle_t server)
{
httpd_stop(server);
}
// ==================== WIFI EVENT HANDLER ====================
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < WIFI_MAX_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG, "connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_sta finished.");
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s", WIFI_SSID);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s", WIFI_SSID);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
// ==================== MAIN ====================
void app_main(void)
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
// Connect to WiFi
wifi_init_sta();
// Start web server
start_webserver();
ESP_LOGI(TAG, "");
ESP_LOGI(TAG, "================================");
ESP_LOGI(TAG, " Web Server is Running!");
ESP_LOGI(TAG, " Open browser and go to:");
ESP_LOGI(TAG, " http://192.168.0.120");
ESP_LOGI(TAG, " (Use your ESP32's IP address)");
ESP_LOGI(TAG, "================================");
ESP_LOGI(TAG, "");
}
How to Test the Server
Step 1: Upload the Code
# Update WiFi credentials first! #define WIFI_SSID "YourWiFiName" #define WIFI_PASS "YourPassword" # Build and flash idf.py build idf.py flash monitor
Step 2: Get ESP32’s IP Address
From serial monitor, look for:
I (10762) web_server: got ip:192.168.0.120
Step 3: Open Browser
On your phone or computer (connected to same WiFi):
- Open browser
- Type:
http://192.168.0.120 - You should see the homepage!
Step 4: Try Different Pages
http://192.168.0.120/→ Homepagehttp://192.168.0.120/hello→ Hello pagehttp://192.168.0.120/info→ Device info
Understanding httpd_resp_send()
This is the most important function – it sends data to the browser!
esp_err_t httpd_resp_send(httpd_req_t *req,
const char *buf,
ssize_t buf_len)
Parameters:
req= The request object (who’s asking)buf= The data to send (HTML, JSON, text)buf_len= Length of data, orHTTPD_RESP_USE_STRLENto calculate automatically
Examples:
// Send HTML
httpd_resp_send(req, "<h1>Hello</h1>", HTTPD_RESP_USE_STRLEN);
// Send JSON
httpd_resp_send(req, "{\"temp\":25}", HTTPD_RESP_USE_STRLEN);
// Send plain text
httpd_resp_send(req, "Temperature: 25C", HTTPD_RESP_USE_STRLEN);
// Send with known length
char buffer[100];
int len = sprintf(buffer, "Value: %d", 123);
httpd_resp_send(req, buffer, len);
Advanced Features Explained
1. Query Parameters (GET with data)
URL: http://192.168.0.120/led?state=on&brightness=80
Extract parameters:
esp_err_t led_handler(httpd_req_t *req)
{
char query[100];
char state[10];
char brightness[10];
// Get the query string
if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
// Extract 'state' parameter
if (httpd_query_key_value(query, "state", state, sizeof(state)) == ESP_OK) {
ESP_LOGI(TAG, "State: %s", state); // "on"
}
// Extract 'brightness' parameter
if (httpd_query_key_value(query, "brightness", brightness, sizeof(brightness)) == ESP_OK) {
ESP_LOGI(TAG, "Brightness: %s", brightness); // "80"
int level = atoi(brightness); // Convert to integer
}
}
httpd_resp_send(req, "LED updated!", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
2. POST Data (Form Submission)
HTML Form:
<form action="/submit" method="POST">
<input name="name" type="text">
<input name="email" type="text">
<button type="submit">Submit</button>
</form>
Handler to receive POST data:
esp_err_t submit_handler(httpd_req_t *req)
{
char content[100];
// Get content length
int total_len = req->content_len;
int cur_len = 0;
int received = 0;
// Read the data
if (total_len >= sizeof(content)) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Content too long");
return ESP_FAIL;
}
while (cur_len < total_len) {
received = httpd_req_recv(req, content + cur_len, total_len);
if (received <= 0) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive data");
return ESP_FAIL;
}
cur_len += received;
}
content[cur_len] = '\0';
// Now 'content' has: "name=John&email=john@example.com"
ESP_LOGI(TAG, "Received POST data: %s", content);
// Parse it
char name[50], email[50];
httpd_query_key_value(content, "name", name, sizeof(name));
httpd_query_key_value(content, "email", email, sizeof(email));
ESP_LOGI(TAG, "Name: %s, Email: %s", name, email);
httpd_resp_send(req, "Form submitted!", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// Register with POST method
httpd_uri_t uri_submit = {
.uri = "/submit",
.method = HTTP_POST, // ← POST, not GET!
.handler = submit_handler,
.user_ctx = NULL
};
3. Send JSON Response
Perfect for APIs!
esp_err_t sensor_api_handler(httpd_req_t *req)
{
// Set content type to JSON
httpd_resp_set_type(req, "application/json");
// Read sensor (example)
float temperature = 25.5;
int humidity = 65;
// Create JSON response
char json[200];
snprintf(json, sizeof(json),
"{"
" \"temperature\": %.1f,"
" \"humidity\": %d,"
" \"status\": \"ok\""
"}",
temperature, humidity
);
httpd_resp_send(req, json, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
Access in browser: http://192.168.0.120/api/sensor
Response:
{
"temperature": 25.5,
"humidity": 65,
"status": "ok"
}
4. Set Custom Headers
esp_err_t handler(httpd_req_t *req)
{
// Set custom headers
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
httpd_resp_send(req, "Hello", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
5. Send Error Responses
esp_err_t handler(httpd_req_t *req)
{
// Check if something is wrong
if (sensor_error) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
"Sensor not responding");
return ESP_FAIL;
}
// Or send 404
httpd_resp_send_404(req);
// Or custom error
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid parameter");
return ESP_FAIL;
}
Practical Example: LED Control
Complete example to control LED from webpage!
#include "driver/gpio.h"
#define LED_PIN GPIO_NUM_2
// LED state
static bool led_state = false;
// Handler for LED control
esp_err_t led_control_handler(httpd_req_t *req)
{
char query[50];
char state[10];
// Get query parameters
if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
if (httpd_query_key_value(query, "state", state, sizeof(state)) == ESP_OK) {
if (strcmp(state, "on") == 0) {
led_state = true;
gpio_set_level(LED_PIN, 1);
ESP_LOGI(TAG, "LED turned ON");
} else if (strcmp(state, "off") == 0) {
led_state = false;
gpio_set_level(LED_PIN, 0);
ESP_LOGI(TAG, "LED turned OFF");
}
}
}
// Send updated webpage
char html[500];
snprintf(html, sizeof(html),
"<!DOCTYPE html>"
"<html>"
"<head>"
" <title>LED Control</title>"
" <style>"
" body { font-family: Arial; text-align: center; margin-top: 50px; }"
" button { padding: 20px 40px; font-size: 20px; margin: 10px; }"
" .on { background-color: #4CAF50; color: white; }"
" .off { background-color: #f44336; color: white; }"
" .status { font-size: 24px; margin: 20px; }"
" </style>"
"</head>"
"<body>"
" <h1>ESP32 LED Control</h1>"
" <div class='status'>LED is: <strong>%s</strong></div>"
" <a href='/led?state=on'><button class='on'>Turn ON</button></a>"
" <a href='/led?state=off'><button class='off'>Turn OFF</button></a>"
"</body>"
"</html>",
led_state ? "ON 💡" : "OFF 🔦"
);
httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
void setup_led(void)
{
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 0);
}
// In app_main(), after starting server:
void app_main(void)
{
// ... WiFi setup ...
setup_led();
start_webserver();
ESP_LOGI(TAG, "Go to http://192.168.0.120/led to control LED");
}
How to use:
- Open
http://192.168.0.120/led - Click “Turn ON” → LED lights up!
- Click “Turn OFF” → LED turns off!
Configuration Options
Server Configuration Structure
typedef struct httpd_config {
unsigned int task_priority; // Task priority (default: 5)
size_t stack_size; // Stack size (default: 4096)
unsigned short server_port; // Port (default: 80)
unsigned short ctrl_port; // Control port (default: 32768)
unsigned short max_open_sockets; // Max connections (default: 7)
unsigned short max_uri_handlers; // Max URI handlers (default: 8)
unsigned short max_resp_headers; // Max response headers (default: 8)
unsigned short backlog_conn; // Listen backlog (default: 5)
bool lru_purge_enable; // Clean old connections (default: false)
unsigned short recv_wait_timeout; // Socket recv timeout (default: 5)
unsigned short send_wait_timeout; // Socket send timeout (default: 5)
} httpd_config_t;
Common customizations:
httpd_config_t config = HTTPD_DEFAULT_CONFIG(); // Use different port config.server_port = 8080; // Access with http://192.168.0.120:8080 // Allow more connections config.max_open_sockets = 10; // Increase stack for complex handlers config.stack_size = 8192; // Enable LRU purge (recommended!) config.lru_purge_enable = true;
Common Patterns
Pattern 1: API Endpoint
// GET /api/status
esp_err_t api_status_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "application/json");
char json[100];
snprintf(json, sizeof(json),
"{\"uptime\": %ld, \"free_heap\": %d}",
esp_timer_get_time() / 1000000,
esp_get_free_heap_size()
);
httpd_resp_send(req, json, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
Pattern 2: File Download
// Download a file
esp_err_t download_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "application/octet-stream");
httpd_resp_set_hdr(req, "Content-Disposition", "attachment; filename=\"data.txt\"");
const char* data = "This is file content\nLine 2\nLine 3";
httpd_resp_send(req, data, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
Pattern 3: Redirect
// Redirect to another page
esp_err_t redirect_handler(httpd_req_t *req)
{
httpd_resp_set_status(req, "302 Found");
httpd_resp_set_hdr(req, "Location", "/");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
Troubleshooting
Problem: Can’t access server from browser
Check:
- ESP32 and device on same WiFi network
- Using correct IP address
- Port 80 is not blocked
- Server actually started (check logs)
Test:
# From computer, ping ESP32 ping 192.168.0.120 # If ping works, try curl curl http://192.168.0.120
Problem: Server starts but pages don’t load
Check:
- Handlers are registered:
httpd_register_uri_handler() - URI matches exactly (case-sensitive!)
- Method matches (GET vs POST)
Debug:
ESP_LOGI(TAG, "Registering handler for: %s", uri_root.uri); httpd_register_uri_handler(server, &uri_root); ESP_LOGI(TAG, "Handler registered!");
Problem: POST data not received
Check:
- Content-Length header present
- Using
httpd_req_recv()to read data - Buffer is large enough
Problem: Server crashes
Common causes:
- Stack overflow (increase
stack_size) - Buffer overflow (check array sizes)
- NULL pointer access
Fix:
// Increase stack size
config.stack_size = 8192;
// Check pointers
if (req == NULL) {
return ESP_FAIL;
}
Best Practices
1. Always Set Content Type
// For HTML httpd_resp_set_type(req, "text/html"); // For JSON httpd_resp_set_type(req, "application/json"); // For plain text httpd_resp_set_type(req, "text/plain");
2. Check Return Values
if (httpd_start(&server, &config) != ESP_OK) {
ESP_LOGE(TAG, "Failed to start server");
return NULL;
}
3. Use Static HTML for Large Pages
// Instead of inline strings
static const char* html_page =
"<!DOCTYPE html>"
"<html>..."
"</html>";
httpd_resp_send(req, html_page, HTTPD_RESP_USE_STRLEN);
4. Enable LRU Purge
config.lru_purge_enable = true;
This automatically closes old connections when limit is reached.
5. Handle Errors Gracefully
esp_err_t handler(httpd_req_t *req)
{
if (error_condition) {
httpd_resp_send_500(req);
return ESP_FAIL;
}
// Normal processing
return ESP_OK;
}
Summary
HTTP Server on ESP32:
✅ What it does:
- Makes ESP32 a web server
- Accessible from any browser on same WiFi
- Can serve HTML pages, JSON APIs, control hardware
✅ Key concepts:
- URI = The path (/led, /sensor)
- Handler = Function that responds to a URI
- Request = What browser asks for
- Response = What ESP32 sends back
✅ Basic flow:
- Start WiFi
- Configure and start HTTP server
- Register URI handlers
- Access from browser
✅ Common uses:
- Control LEDs, motors, relays
- Display sensor data
- Configuration web interface
- REST API for IoT applications
Quick Start Template
// 1. Include header
#include "esp_http_server.h"
// 2. Create handler
esp_err_t my_handler(httpd_req_t *req) {
httpd_resp_send(req, "<h1>Hello</h1>", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// 3. Define URI
httpd_uri_t my_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = my_handler,
.user_ctx = NULL
};
// 4. Start server
httpd_handle_t start_server(void) {
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
if (httpd_start(&server, &config) == ESP_OK) {
httpd_register_uri_handler(server, &my_uri);
}
return server;
}
// 5. In app_main()
void app_main(void) {
wifi_init_sta(); // Connect to WiFi
start_server(); // Start HTTP server
}
That’s it! You now understand ESP32 HTTP Server! 🎉
Ready to build your own web-controlled projects!