ESP32 (101) HTTP Server

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.120 in browser → ESP32 responds with a webpage!

What Can You Do With HTTP Server?

Real-World Examples:

  1. Control LED from Phone 🔦
    • Open browser on phone
    • Click “Turn ON” button
    • LED on ESP32 turns on!
  2. Display Sensor Data 🌡️
    • ESP32 reads temperature sensor
    • You open webpage
    • See current temperature: “Temperature: 25°C”
  3. Smart Home Control 🏠
    • Control lights, fans, garage door
    • All from a web interface
    • No app needed – just browser!
  4. 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)

MethodWhat It MeansExample
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
  • NULL means “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):

  1. Open browser
  2. Type: http://192.168.0.120
  3. You should see the homepage!

Step 4: Try Different Pages

  • http://192.168.0.120/ → Homepage
  • http://192.168.0.120/hello → Hello page
  • http://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, or HTTPD_RESP_USE_STRLEN to 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:

  1. Open http://192.168.0.120/led
  2. Click “Turn ON” → LED lights up!
  3. 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:

  1. ESP32 and device on same WiFi network
  2. Using correct IP address
  3. Port 80 is not blocked
  4. 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:

  1. Handlers are registered: httpd_register_uri_handler()
  2. URI matches exactly (case-sensitive!)
  3. 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:

  1. Content-Length header present
  2. Using httpd_req_recv() to read data
  3. Buffer is large enough

Problem: Server crashes

Common causes:

  1. Stack overflow (increase stack_size)
  2. Buffer overflow (check array sizes)
  3. 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:

  1. Start WiFi
  2. Configure and start HTTP server
  3. Register URI handlers
  4. 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!

Leave a Reply

Your email address will not be published.