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
-
Prepare Development Environment
# Create project directory structure mkdir -p my_web_server/main components/web_files -
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 -
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") -
Compile and Flash
idf.py set-target esp32 idf.py build idf.py -p PORT flash monitor # Replace PORT with the actual serial port -
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
- Hardware Control: Control the onboard LED via GPIO2
- 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
- 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.
5. Additional Notes on the Web Page
After compiling the web page, it generates <span>index.html.S</span>.

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>.