Designing a Web Server Based on ESP-IDF for ESP32

1. Code

The web server based on the ESP32 IDF framework includes WiFi connection, HTTP service, front-end interaction, and hardware control functions:

#include <stdio.h>
#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_http_server.h"
#include "driver/gpio.h"
#include "cJSON.h"

// WiFi configuration
#define WIFI_SSID "your_SSID"
#define WIFI_PASS "your_PASSWORD"
#define LED_GPIO GPIO_NUM_2  // Onboard LED pin

// Global variables
static const char* TAG = "WEB_SERVER";
static httpd_handle_t server = NULL;
static bool led_state = false;

// WiFi connection initialization
static void wifi_init_sta(void) {
    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(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "WiFi initialization complete, connecting to AP...");
}

// Event handling
static int s_retry_num = 0;
// Event handling
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 < 6) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "Retry to connect to the AP");
        } else {
            ESP_LOGI(TAG, "Failed to connect to the AP");
        }
    } 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 address: " IPSTR, IP2STR(&event->ip_info.ip));
        start_webserver();
        s_retry_num = 0;
    }
}

// HTTP request handler function
static esp_err_t root_get_handler(httpd_req_t* req) {
    extern const char index_html_start[] asm("_binary_index_html_start");
    extern const char index_html_end[] asm("_binary_index_html_end");

    extern const unsigned int len2 asm("index_html_length");

    printf("len2=%d\n", len2);
    printf("len=%d\n", index_html_end - index_html_start);
    printf("strlen(index_html_start)=%d\n", strlen(index_html_start));
    httpd_resp_set_type(req, "text/html");
    // strlen(index_html_start) gets a length that is 2 bytes more than the actual length
    httpd_resp_send(req, index_html_start, len2);
    return ESP_OK;
}

static esp_err_t led_post_handler(httpd_req_t* req) {
    char buf[10];
    int ret = httpd_req_recv(req, buf, sizeof(buf));

    if (ret > 0) {
        buf[ret] = '\0';
        if (strcmp(buf, "toggle") == 0) {
            led_state = !led_state;
            gpio_set_level(LED_GPIO, led_state);
            httpd_resp_sendstr(req, led_state ? "ON" : "OFF");
        }
    }
    return ESP_OK;
}

static esp_err_t sensor_get_handler(httpd_req_t* req) {
    // Simulated sensor data (replace with actual sensor readings in real projects)
    cJSON *root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "temperature", 25.6);
    cJSON_AddNumberToObject(root, "humidity", 65.2);

    const char* json_str = cJSON_PrintUnformatted(root);
    httpd_resp_set_type(req, "application/json");
    httpd_resp_sendstr(req, json_str);

    cJSON_Delete(root);
    free((void*)json_str);
    return ESP_OK;
}

// Start Web server
void start_webserver() {
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.max_uri_handlers = 8;

    // Register URI handlers
    httpd_uri_t uri_get = {
        .uri = "/",
        .method = HTTP_GET,
        .handler = root_get_handler,
        .user_ctx = NULL
    };

    httpd_uri_t uri_led = {
        .uri = "/led",
        .method = HTTP_POST,
        .handler = led_post_handler,
        .user_ctx = NULL
    };

    httpd_uri_t uri_sensor = {
        .uri = "/sensor",
        .method = HTTP_GET,
        .handler = sensor_get_handler,
        .user_ctx = NULL
    };

    if (httpd_start(&server, &config) == ESP_OK) {
        httpd_register_uri_handler(server, &uri_get);
        httpd_register_uri_handler(server, &uri_led);
        httpd_register_uri_handler(server, &uri_sensor);
        ESP_LOGI(TAG, "HTTP server started successfully");
    }
}

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);

    // Initialize GPIO
    gpio_reset_pin(LED_GPIO);
    gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);

    // Initialize network
    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));

    // Register event handler
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    // Start WiFi
    wifi_init_sta();
}

2. Accompanying HTML File (<span>index.html</span>)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 Control Panel</title>
<style>
    body { font-family: Arial; text-align: center; margin: 40px;}
    .btn {
        padding: 15px 30px;
        font-size: 18px;
        background: #4CAF50;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
    }
    .status { margin-top: 20px; font-size: 20px;}
</style>
</head>
<body>
<h1>ESP32 Control Panel</h1>
<button class="btn" onclick="toggleLED()">Toggle LED State</button>
<div class="status">
<p>Sensor Data:<span id="sensorData">Loading...</span></p>
</div>

<script>
    function toggleLED() {
        fetch('/led', {
            method: 'POST',
            body: 'toggle'
        })
        .then(response => response.text())
        .then(state => {
            alert('Current LED State: ' + state);
        });
    }

    // Update sensor data every 2 seconds
    setInterval(() => {
        fetch('/sensor')
        .then(response => response.json())
        .then(data => {
            document.getElementById('sensorData').innerHTML =
                `${data.temperature}℃ / ${data.humidity}%`;
        });
    }, 2000);
</script>
</body>
</html>

3. Deployment Steps

  1. Prepare Development Environment

    # Create project directory structure
    mkdir -p my_web_server/main components/web_files
    
  2. File Organization

    my_web_server/
    ├── CMakeLists.txt
    ├── main/
    │   ├── CMakeLists.txt
    │   └── main.c         # The above C code
    └── components/
        └── web_files/
            └── index.html  # The above HTML file
    
  3. Configure CMakeLists.txtMain directory CMakeLists.txt:

    cmake_minimum_required(VERSION 3.5)
    include($ENV{IDF_PATH}/tools/cmake/project.cmake)
    project(my_web_server)
    

    Main/CMakeLists.txt:

    idf_component_register(SRCS "main.c"
                     INCLUDE_DIRS "."
                     EMBED_FILES "../components/web_files/index.html")
    
  4. Compile and Flash

    idf.py set-target esp32
    idf.py build
    idf.py -p PORT flash monitor  # Replace PORT with the actual serial port
    
  5. Test and Validate

  • Check the IP address output on the serial port
  • Access the browser at <span>http://[ESP32_IP]/</span>
  • Click the button to test LED control
  • Observe sensor data updates

4. Function Description

  1. Hardware Control: Control the onboard LED via GPIO2
  2. Data Interfaces:
  • GET <span>/</span>: Returns the control page
  • POST <span>/led</span>: Toggles the LED state
  • GET <span>/sensor</span>: Retrieves simulated sensor data
  • Front-end Features:
    • Real-time display of sensor data (updated every 2 seconds)
    • Button to control LED state
    • Responsive interface design

    The code implements a complete web server functionality, and additional URI handling logic can be added to extend features.Designing a Web Server Based on ESP-IDF for ESP32

    5. Additional Notes on the Web Page

    After compiling the web page, it generates <span>index.html.S</span>.

    Designing a Web Server Based on ESP-IDF for ESP32

    Its content is as follows:

    /* * Data converted from C:/work/demo/others/hello/hello/components/web_files/index.html
     */
    .data
    .section .rodata.embedded
    
    .global index_html
    index_html:
    
    .global _binary_index_html_start
    _binary_index_html_start: /* for objcopy compatibility */
    .byte 0x0d,0x0a,0x0d,0x0a,0x3c,0x21,0x2d,0x2d,0x20,0x73,0x61,0x76,0x65,0x64,0x20,0x66
    .byte 0x72,0x6f,0x6d,0x20,0x75,0x72,0x6c,0x3d,0x28,0x30,0x30,0x32,0x33,0x29,0x68,0x74
    .byte 0x74,0x70,0x3a,0x2f,0x2f,0x31,0x39,0x32,0x2e,0x31,0x36,0x38,0x2e,0x32,0x33,0x36
    .byte 0x2e,0x31,0x32,0x39,0x2f,0x20,0x2d,0x2d,0x3e,0x0d,0x0a,0x3c,0x68,0x74,0x6d,0x6c
    .byte 0x3e,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x6d,0x65,0x74,0x61,0x20,0x68,0x74,0x74
    .byte 0x70,0x2d,0x65,0x71,0x75,0x69,0x76,0x3d,0x22,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74
    .byte 0x2d,0x54,0x79,0x70,0x65,0x22,0x20,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3d,0x22
    .byte 0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73
    .byte 0x65,0x74,0x3d,0x55,0x54,0x46,0x2d,0x38,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20
    .byte 0x0d,0x0a,0x20,0x20,0x20,0x20,0x3c,0x6d,0x65,0x74,0x61,0x20,0x6e,0x61,0x6d,0x65
    .byte 0x3d,0x22,0x76,0x69,0x65,0x77,0x70,0x6f,0x72,0x74,0x22,0x20,0x63,0x6f,0x6e,0x74
    .byte 0x65,0x6e,0x74,0x3d,0x22,0x77,0x69,0x64,0x74,0x68,0x3d,0x64,0x65,0x76,0x69,0x63
    .byte 0x65,0x2d,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x69,0x6e,0x69,0x74,0x69,0x61,0x6c
    .byte 0x2d,0x73,0x63,0x61,0x6c,0x65,0x3d,0x31,0x2e,0x30,0x22,0x3e,0x0d,0x0a,0x20,0x20
    .byte 0x20,0x20,0x3c,0x74,0x69,0x74,0x6c,0x65,0x3e,0x45,0x53,0x50,0x33,0x32,0xe6,0x8e
    .byte 0xa7,0xe5,0x88,0xb6,0xe9,0x9d,0xa2,0xe6,0x9d,0xbf,0x3c,0x2f,0x74,0x69,0x74,0x6c
    .byte 0x65,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x3c,0x73,0x74,0x79,0x6c,0x65,0x3e,0x0d
    .byte 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x64,0x79,0x20,0x7b,0x20
    .byte 0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x20,0x41,0x72,0x69
    .byte 0x61,0x6c,0x3b,0x20,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x20
    .byte 0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x20
    .byte 0x34,0x30,0x70,0x78,0x3b,0x20,0x7d,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x2e,0x62,0x74,0x6e,0x20,0x7b,0x20,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x20,0x31
    .byte 0x35,0x70,0x78,0x20,0x33,0x30,0x70,0x78,0x3b,0x20,0x0d,0x0a,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a
    .byte 0x65,0x3a,0x20,0x31,0x38,0x70,0x78,0x3b,0x20,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e
    .byte 0x64,0x3a,0x20,0x23,0x34,0x43,0x41,0x46,0x35,0x30,0x3b,0x20,0x0d,0x0a,0x20,0x20
    .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3a
    .byte 0x20,0x77,0x68,0x69,0x74,0x65,0x3b,0x20,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x20,0x6e,0x6f
    .byte 0x6e,0x65,0x3b,0x20,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a
    .byte 0x20,0x35,0x70,0x78,0x3b,0x20,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x20,0x20,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x20,0x70,0x6f,0x69,0x6e
    .byte 0x74,0x65,0x72,0x3b,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0d
    .byte 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2e,0x73,0x74,0x61,0x74,0x75,0x73
    .byte 0x20,0x7b,0x20,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x20,0x32
    .byte 0x30,0x70,0x78,0x3b,0x20,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x20
    .byte 0x32,0x30,0x70,0x78,0x3b,0x20,0x7d,0x0d,0x0a,0x20,0x20,0x20,0x20,0x3c,0x2f,0x73
    .byte 0x74,0x79,0x6c,0x65,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a
    .byte 0x3c,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x3c,0x68,0x31,0x3e
    .byte 0x45,0x53,0x50,0x33,0x32,0xe6,0x8e,0xa7,0xe5,0x88,0xb6,0xe9,0x9d,0xa2,0xe6,0x9d
    .byte 0xbf,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x3c,0x62,0x75,0x74
    .byte 0x74,0x6f,0x6e,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22,0x62,0x74,0x6e,0x22,0x20
    .byte 0x6f,0x6e,0x63,0x6c,0x69,0x63,0x6b,0x3d,0x22,0x74,0x6f,0x67,0x67,0x6c,0x65,0x4c
    .byte 0x45,0x44,0x28,0x29,0x22,0x3e,0xe5,0x88,0x87,0xe6,0x8d,0xa2,0x4c,0x45,0x44,0xe7
    .byte 0x8a,0xb6,0xe6,0x80,0x81,0x3c,0x2f,0x62,0x75,0x74,0x74,0x6f,0x6e,0x3e,0x0d,0x0a
    .byte 0x20,0x20,0x20,0x20,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x22
    .byte 0x73,0x74,0x61,0x74,0x75,0x73,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20
    .byte 0x20,0x20,0x3c,0x70>
    
    static esp_err_t root_get_handler(httpd_req_t* req) {
        extern const char index_html_start[] asm("_binary_index_html_start");
        extern const char index_html_end[] asm("_binary_index_html_end");
    
        extern const unsigned int len2 asm("index_html_length");
    
        printf("len2=%d\n", len2);
        printf("len=%d\n", index_html_end - index_html_start);
        printf("strlen(index_html_start)=%d\n", strlen(index_html_start));
        httpd_resp_set_type(req, "text/html");
        // strlen(index_html_start) gets a length that is 2 bytes more than the actual length
        httpd_resp_send(req, index_html_start, len2);
        return ESP_OK;
    }
    

    Refer to the usage of <span>asm</span>.

    Leave a Comment