基于 ESP32-S3 与 ESP-IDF 的网页 LED 控制系统设计与实现
本文基于一个真实 ESP-IDF 工程,复盘 ESP32-S3 网页控制 LED 的完整实现过程。项目运行后,ESP32-S3 以 SoftAP 模式创建 WiFi 热点,手机或电脑连接热点后访问,即可打开内置网页控制面板。网页通过 HTTP GET 请求访问/led/on/led/off/led/blink和等接口,ESP32 端由接收请求,再调用 LED 控制模块修改 GPIO1 电平,实现
基于 ESP32-S3 与 ESP-IDF 的网页 LED 控制系统设计与实现
一、可选标题
- 基于 ESP32-S3 与 ESP-IDF 的网页 LED 控制系统设计与实现
- ESP32 Web 控制项目实战:从 SoftAP 热点到浏览器控制 GPIO
- 用 ESP-IDF 搭建一个可复现的 ESP32-S3 网页控制面板
二、文章摘要
本文基于一个真实 ESP-IDF 工程,复盘 ESP32-S3 网页控制 LED 的完整实现过程。项目运行后,ESP32-S3 以 SoftAP 模式创建 WiFi 热点,手机或电脑连接热点后访问 192.168.4.1,即可打开内置网页控制面板。网页通过 HTTP GET 请求访问 /led/on、/led/off、/led/blink 和 /led/status 等接口,ESP32 端由 esp_http_server 接收请求,再调用 LED 控制模块修改 GPIO1 电平,实现 LED 常亮、熄灭和闪烁。项目还集成了 LCD 显示、NVS 初始化、FreeRTOS 任务、BSP 驱动组件和 16MB 自定义分区表,适合作为从 Arduino 过渡到 ESP-IDF 的 Web 控制入门工程。
文章目录
- 基于 ESP32-S3 与 ESP-IDF 的网页 LED 控制系统设计与实现
-
- 一、可选标题
- 二、文章摘要
- 三、正文
- 1. 项目简介
- 2. 项目功能概述
- 3. 项目目录结构解析
- 4. 项目整体架构设计
- 5. 核心工作流程说明
- 6. WiFi SoftAP 初始化解析
- 7. WiFi 事件回调与 LCD 显示
- 8. HTTP Web Server 路由解析
- 9. 网页前后端交互逻辑说明
- 10. 硬件控制逻辑说明
- 11. BSP、LCD 与外设初始化说明
- 12. 分区表与 sdkconfig 关键配置
- 13. 为什么选择 ESP-IDF,而不是简单 Arduino 写法
- 14. 编译、烧录与运行方法
- 15. 运行效果与调试经验
- 16. 我踩过的坑
- 17. 面向初学者的几个概念解释
- 18. 常见问题与解决方案
- 19. 项目总结与可扩展方向
三、正文
1. 项目简介
这个项目是一个基于 ESP32-S3 的网页控制硬件实验。它的核心目标并不复杂:ESP32-S3 创建 WiFi 热点,启动 HTTP Web Server,用户通过浏览器打开网页,点击按钮后控制开发板上的红色 LED。
项目使用的是 ESP-IDF,而不是 Arduino。代码中可以看到 WiFi、事件循环、HTTP Server、FreeRTOS 任务、NVS、BSP 驱动和自定义分区表等典型 ESP-IDF 工程元素。对于想从 Arduino 过渡到 ESP-IDF 的开发者来说,这个项目很适合用来理解“网页请求如何真正落到硬件 GPIO 上”。
2. 项目功能概述
当前版本已经实现的功能包括:
- ESP32-S3 创建 WiFi SoftAP 热点;
- 热点名称为
ESP32S3 WIFI; - 热点密码为
123456789; - 默认访问地址为
http://192.168.4.1/; - 启动 ESP-IDF HTTP Web Server;
- 浏览器访问
/返回网页控制面板; - 网页提供 LED 打开、关闭、闪烁和状态查询按钮;
- ESP32-S3 通过 GPIO1 控制红色 LED;
- LCD 显示 AP 启动信息、IP 地址和设备连接状态;
- LED 闪烁由独立 FreeRTOS 任务完成。
3. 项目目录结构解析
项目根目录主要内容如下:
WiFi_AP
├── CMakeLists.txt
├── README.md
├── sdkconfig
├── partitions-16MiB.csv
├── main
│ ├── CMakeLists.txt
│ ├── main.c
│ ├── led_ctrl.c
│ ├── led_ctrl.h
│ ├── web_server.c
│ └── web_server.h
└── components
├── BSP
│ ├── CMakeLists.txt
│ ├── LED
│ ├── MYIIC
│ ├── MYSPI
│ ├── SPILCD
│ └── XL9555
└── Middlewares

根目录 CMakeLists.txt 中项目名为 03_WiFi_AP:
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS components/Middlewares)
add_compile_options(-fdiagnostics-color=always)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(03_WiFi_AP)
代码说明:
这是标准 ESP-IDF CMake 工程入口。project(03_WiFi_AP) 决定最终生成的固件名称,构建产物中可以看到 03_WiFi_AP.bin 和 03_WiFi_AP.elf。
main/CMakeLists.txt 注册了业务源码:
idf_component_register(SRCS "main.c"
"led_ctrl.c"
"web_server.c"
INCLUDE_DIRS "."
REQUIRES BSP esp_wifi esp_event esp_netif nvs_flash esp_http_server lwip)
代码说明:
main.c 负责系统入口、外设初始化、WiFi AP 启动;web_server.c 负责 HTTP 服务和网页接口;led_ctrl.c 负责 LED 状态管理。依赖项里包含 esp_wifi、esp_http_server、esp_netif 和 nvs_flash,说明这个项目是典型 ESP-IDF 网络控制工程。
4. 项目整体架构设计
整个项目可以分为四层:
- 浏览器前端层:由
web_server.c中的s_index_html字符串提供 HTML、CSS、JavaScript; - HTTP 接口层:由 ESP-IDF
esp_http_server提供,注册多个 GET 路由; - 业务控制层:由
led_ctrl.c维护 LED 状态并统一控制 GPIO; - 硬件驱动层:由
components/BSP/LED/led.c配置 GPIO1 并输出电平。
Mermaid 系统架构图
这张图可以看出,浏览器并不直接操作 GPIO,而是通过 HTTP 请求进入 ESP32-S3 的 Web Server,再由服务端调用 LED 控制模块,最后落到 BSP 驱动和 GPIO 电平。
5. 核心工作流程说明
项目启动后的主流程在 main.c 的 app_main() 中:
ret = nvs_flash_init();
led_ctrl_init();
my_spi_init();
myiic_init();
xl9555_init();
spilcd_init();
wifi_init_softap(ap_ip, sizeof(ap_ip));
ESP_ERROR_CHECK(web_server_start(EXAMPLE_ESP_WIFI_SSID, ap_ip));
代码说明:
程序先初始化 NVS,再初始化 LED、SPI、IIC、XL9555 和 LCD。随后 ESP32-S3 进入 SoftAP 模式创建热点,最后启动 HTTP Web Server。主任务后续只保留 vTaskDelay() 空闲循环,不再直接翻转 LED,避免阻塞 WiFi、HTTP 和 LCD 事件处理。
Mermaid 启动流程图
6. WiFi SoftAP 初始化解析
项目中 WiFi 相关配置定义在 main.c:
#define EXAMPLE_ESP_WIFI_SSID "ESP32S3 WIFI"
#define EXAMPLE_ESP_WIFI_PASS "123456789"
#define EXAMPLE_MAX_STA_CONN 5
代码说明:
ESP32-S3 不是连接已有路由器,而是自己创建热点。手机或电脑需要先连接 ESP32S3 WIFI,再访问开发板 IP。
SoftAP 初始化函数是 wifi_init_softap(),关键步骤包括:
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(
WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL
));
代码说明:
esp_netif_init() 初始化网络接口层;esp_event_loop_create_default() 创建默认事件循环;esp_netif_create_default_wifi_ap() 创建默认 AP 网络接口;esp_wifi_init() 初始化 WiFi 驱动;esp_event_handler_register() 注册 WiFi 事件回调。
AP 配置如下:
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
代码说明:
项目最多允许 5 个设备连接热点,认证方式为 WPA/WPA2-PSK。如果密码长度为 0,代码会切换为开放热点。
启动 AP 的核心代码:
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
启动完成后,项目通过 esp_netif_get_ip_info() 获取 AP 网关 IP,并用 inet_ntoa_r() 转成字符串,传给 Web Server 显示。
7. WiFi 事件回调与 LCD 显示
项目注册了 wifi_event_handler() 处理设备连接和断开事件。
当有设备连接时:
if (event_id == WIFI_EVENT_AP_STACONNECTED)
{
wifi_event_ap_staconnected_t *event =
(wifi_event_ap_staconnected_t *)event_data;
ESP_LOGI(TAG, "station " MACSTR " join, AID=%d",
MAC2STR(event->mac), event->aid);
sprintf(lcd_buff, "MACSTR:"MACSTR, MAC2STR(event->mac));
spilcd_show_string(0, 90, 320, 16, 16, lcd_buff, BLUE);
}
代码说明:
当手机或电脑连上热点时,ESP32-S3 会在串口打印 MAC 地址,并在 LCD 上显示连接设备的 MAC。设备断开时,回调处理 WIFI_EVENT_AP_STADISCONNECTED,同样更新 LCD。
![[配图建议:LCD 显示 AP IP 与设备 MAC 的运行效果图]](https://i-blog.csdnimg.cn/direct/c18378130669495a86a74f81ae8a3b9a.jpeg)



8. HTTP Web Server 路由解析
Web 服务入口在 web_server.c:
esp_err_t web_server_start(const char *ap_ssid, const char *ap_ip)
它接收当前热点名和 IP 地址,用于网页状态显示。
服务启动使用 ESP-IDF 默认 HTTP Server 配置:
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
esp_err_t ret = httpd_start(&s_http_server, &config);
实际注册的路由如下:
GET / 返回网页控制面板
GET /led/on 打开 LED
GET /led/off 关闭 LED
GET /led/blink LED 闪烁模式
GET /led/status 查询当前 LED 状态
路由注册代码:
ESP_ERROR_CHECK(httpd_register_uri_handler(s_http_server, &index_uri));
ESP_ERROR_CHECK(httpd_register_uri_handler(s_http_server, &led_on_uri));
ESP_ERROR_CHECK(httpd_register_uri_handler(s_http_server, &led_off_uri));
ESP_ERROR_CHECK(httpd_register_uri_handler(s_http_server, &led_blink_uri));
ESP_ERROR_CHECK(httpd_register_uri_handler(s_http_server, &led_status_uri));
代码说明:
当前版本使用 GET 请求,没有使用 POST。对于 LED 实验来说,GET 足够直观,浏览器直接访问接口也能验证效果。如果后续控制继电器、门锁等更严肃的设备,更建议改成 POST 并加入权限校验。
Mermaid HTTP 路由分发流程图
9. 网页前后端交互逻辑说明
项目没有单独的 html、css、js 文件。整个网页写在 web_server.c 的 s_index_html[] 字符串中。
页面包含:
- 当前热点名称;
- 当前开发板 IP;
- 当前 LED 状态;
- 打开 LED 按钮;
- 关闭 LED 按钮;
- 闪烁模式按钮;
- 查询状态按钮。
核心 JavaScript 逻辑如下:
async function refreshStatus(){
const r = await fetch('/led/status', {cache:'no-store'});
const j = await r.json();
document.getElementById('ssid').textContent = j.ssid;
document.getElementById('ip').textContent = j.ip;
document.getElementById('state').textContent = j.state;
}
async function setLed(uri){
await fetch(uri, {cache:'no-store'});
refreshStatus();
}
refreshStatus();
setInterval(refreshStatus, 2000);
代码说明:
refreshStatus() 请求 /led/status,拿到 JSON 后刷新页面显示。setLed(uri) 根据按钮传入的 URI 请求 /led/on、/led/off 或 /led/blink。页面加载后立即查询一次状态,并且每 2 秒自动刷新一次。
状态接口返回 JSON:
snprintf(json, sizeof(json),
"{\"ssid\":\"%s\",\"ip\":\"%s\",\"state\":\"%s\"}",
s_ap_ssid, s_ap_ip, state);
代码说明:
返回内容包含热点名称、IP 地址和 LED 状态。这里还设置了响应类型和缓存控制:
httpd_resp_set_type(req, "application/json; charset=utf-8");
httpd_resp_set_hdr(req, "Cache-Control", "no-store");
这可以减少浏览器缓存导致状态显示不更新的问题。
Mermaid 前端点击到硬件响应时序图
10. 硬件控制逻辑说明
LED 状态定义在 led_ctrl.h:
typedef enum
{
LED_OFF = 0,
LED_ON,
LED_BLINK
} led_ctrl_state_t;
代码说明:
项目支持三种状态:关闭、打开、闪烁。
硬件上,红色 LED 接在 GPIO1:
#define LED0_GPIO_PIN GPIO_NUM_1
components/BSP/LED/led.h 中定义了控制宏:
#define LED0(x) do { x ? \
gpio_set_level(LED0_GPIO_PIN, 1): \
gpio_set_level(LED0_GPIO_PIN, 0); \
} while(0)
代码说明:
本项目 LED 是低电平点亮。LED0(0) 输出低电平,LED 点亮;LED0(1) 输出高电平,LED 熄灭。这是很多开发板上常见的“低电平有效”设计,初学者很容易把电平逻辑写反。
led_ctrl.c 中真正执行状态切换的是:
static void led_ctrl_apply_level(led_ctrl_state_t state)
{
switch (state)
{
case LED_ON:
LED0(0);
break;
case LED_OFF:
LED0(1);
break;
case LED_BLINK:
default:
LED0(0);
break;
}
}
代码说明:
网页并不直接操作 GPIO,而是通过状态枚举控制 LED。这样做的好处是业务层和底层 GPIO 解耦,后续把 LED 换成继电器或风扇时,只需要扩展控制模块。
闪烁任务如下:
static void led_blink_task(void *arg)
{
while (1)
{
portENTER_CRITICAL(&s_led_mux);
if (s_led_state == LED_BLINK)
{
LED0_TOGGLE();
}
portEXIT_CRITICAL(&s_led_mux);
vTaskDelay(pdMS_TO_TICKS(LED_BLINK_PERIOD_MS));
}
}
代码说明:
LED 闪烁由独立 FreeRTOS 任务负责,周期为 500ms。使用 vTaskDelay() 只会挂起当前 LED 任务,不会阻塞 HTTP Server 或 WiFi 事件循环。
项目还使用临界区保护 LED 状态:
static volatile led_ctrl_state_t s_led_state = LED_BLINK;
static portMUX_TYPE s_led_mux = portMUX_INITIALIZER_UNLOCKED;
代码说明:
HTTP 请求处理函数和 LED 闪烁任务可能同时访问 s_led_state,所以用 portENTER_CRITICAL() 和 portEXIT_CRITICAL() 保护状态和 GPIO 更新,避免切换瞬间多翻转一次 LED。
Mermaid GPIO 控制执行流程图

11. BSP、LCD 与外设初始化说明
项目的 BSP 组件位于 components/BSP,包含:
LED:板载红色 LED 驱动;MYIIC:IIC 初始化;MYSPI:SPI 初始化;SPILCD:SPI LCD 显示驱动;XL9555:IO 扩展芯片驱动。
BSP 组件的 CMakeLists.txt 注册了这些目录:
set(src_dirs
LED
MYIIC
XL9555
MYSPI
SPILCD)
idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
项目中实际用到的外设初始化顺序为:
led_ctrl_init();
my_spi_init();
myiic_init();
xl9555_init();
spilcd_init();
关键引脚信息:
LED0: GPIO1
IIC SDA: GPIO41
IIC SCL: GPIO42
SPI SCLK: GPIO12
SPI MOSI: GPIO11
SPI MISO: GPIO13
LCD DC: GPIO40
LCD CS: GPIO21
XL9555 地址: 0x20
README 中也说明了实验平台为正点原子 ESP32-S3 开发板,红色 LED 连接 IO1,XL9555 使用 IO41/IO42 作为 IIC 引脚。
图1:打开LED
图2:关闭LED
图3:闪烁模式
12. 分区表与 sdkconfig 关键配置
项目使用自定义分区表 partitions-16MiB.csv:
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x9000,0x6000,,
phy_init,data,phy,0xf000,0x1000,,
factory,app,factory,0x10000,0x1F0000,,
vfs,data,fat,0x200000,0xA00000,,
storage,data,spiffs,0xc00000,0x400000,,
说明:
nvs用于存储 WiFi、PHY 等非易失数据;phy_init用于 RF 校准相关数据;factory是当前应用固件分区,大小约 1984KB;vfs是 FAT 分区,大小 10MB;storage是 SPIFFS 分区,大小 4MB。
当前代码没有挂载 FATFS 或 SPIFFS,也没有从 Flash 文件系统加载网页。网页是编译进固件的字符串。保留 FAT 和 SPIFFS 分区,说明后续可以扩展为“外部网页资源文件”方案。
sdkconfig 关键配置包括:
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE="16MB"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_FILENAME="partitions-16MiB.csv"
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y
CONFIG_LWIP_DHCPS=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240
从 build 目录可以反推出:
- 目标芯片:ESP32-S3;
- ESP-IDF 版本:v5.1.2;
- 构建工具:Ninja;
- 编译器:
xtensa-esp32s3-elf-gcc; - 串口监视波特率:115200;
- Flash:16MB,80MHz;
- App 固件大小约 839KB;
- Bootloader 大小约 21KB;
- 应用烧录地址:
0x10000; - 分区表烧录地址:
0x8000; - Bootloader 烧录地址:
0x0。
13. 为什么选择 ESP-IDF,而不是简单 Arduino 写法
如果只是点亮 LED,Arduino 的确更快。但这个项目的目标不是“点灯”,而是打通完整网络控制链路。
ESP-IDF 的优势体现在:
- WiFi AP、事件循环、HTTP Server 都是组件化接口;
- FreeRTOS 任务可以把 LED 闪烁、网络服务、主任务拆开;
esp_http_server可以清晰注册路由,适合扩展更多接口;sdkconfig和分区表可控,适合后续加入 SPIFFS、OTA、NVS 配网;- BSP 驱动组件可以把 LED、LCD、IIC、SPI、XL9555 与业务逻辑分离。
简单说,Arduino 适合快速验证,ESP-IDF 更适合把原型变成可维护工程。
14. 编译、烧录与运行方法
进入项目目录:
cd E:\esp32_tool\WiFi_AP
设置目标芯片:
idf.py set-target esp32s3
编译:
idf.py build
烧录并打开串口监视器:
idf.py flash monitor
运行后,串口会打印类似信息:
AP 启动成功,IP: 192.168.4.1
热点名称: ESP32S3 WIFI,访问地址: http://192.168.4.1/
HTTP 服务器启动成功
手机或电脑连接热点:
SSID: ESP32S3 WIFI
Password: 123456789
然后浏览器访问:
http://192.168.4.1/
![[配图建议:ESP32-S3 串口日志截图]](https://i-blog.csdnimg.cn/direct/214aa0a1ffa447e6b450aa0c251c188f.png)
乱码的翻译如下:
15. 运行效果与调试经验
运行效果:
- LCD 显示
ESP32-S3、WiFi AP Test; - AP 启动后 LCD 显示 IP;
- 设备连接热点后,LCD 显示连接设备 MAC;
- 浏览器打开控制面板;
- 点击“打开 LED”,GPIO1 输出低电平,LED 点亮;
- 点击“关闭 LED”,GPIO1 输出高电平,LED 熄灭;
- 点击“闪烁模式”,LED 以 500ms 周期翻转;
- 页面每 2 秒刷新一次状态。
调试建议:
- 看串口日志
重点观察 AP 和 WEB_SERVER 标签下的日志。如果看不到 HTTP 服务器启动成功,说明 Web Server 没有正常启动。
- 用浏览器直接访问接口
可以直接访问:
http://192.168.4.1/led/on
http://192.168.4.1/led/off
http://192.168.4.1/led/status
如果接口能返回 JSON,说明 HTTP 路由正常。
- 验证 GPIO 是否真的变化
LED 不亮时,不要只看网页。可以用万用表测 GPIO1 电平,或者在 led_ctrl_set_state() 中加日志确认请求是否到达。
- 排查连接问题
手机必须先连接 ESP32S3 WIFI 热点。没有连接热点时,访问 192.168.4.1 通常打不开。
16. 我踩过的坑
16.1 网页打不开
最常见原因是手机没有连接 ESP32 热点,或者手机自动切回了有互联网的 WiFi。解决方法是确认当前 WiFi 名称为 ESP32S3 WIFI。
16.2 路由不匹配
项目注册的是 /led/on、/led/off、/led/blink、/led/status。如果写成 /led/open 或 /status,会进入 404 处理函数。
16.3 GPIO 电平逻辑反了
本项目 LED 是低电平点亮。LED0(0) 才是亮,LED0(1) 是灭。控制继电器时也要先确认模块是高电平触发还是低电平触发。
16.4 WiFi 连接失败
热点密码是 123456789。如果修改密码,要注意 WPA/WPA2 密码长度不能太短。代码中如果密码长度为 0,会切换为开放热点。
16.5 浏览器缓存导致页面没更新
项目已经在 HTML 和 JSON 响应里设置 Cache-Control: no-store,前端 fetch 也使用 {cache:'no-store'}。如果修改了网页但浏览器仍显示旧页面,可以强制刷新或换无痕窗口。
16.6 分区表设置不当
当前固件约 839KB,factory 分区约 1984KB,够用。但如果后续加入大网页资源、图片、OTA 双分区,现有分区表需要重新规划。
17. 面向初学者的几个概念解释
HTTP 路由:
可以理解为“浏览器访问的地址路径”。例如 /led/on 对应 C 端的 led_on_get_handler()。
GET 请求:
浏览器请求资源或触发简单操作的一种方式。本项目用 GET 控制 LED,便于测试。工程化场景中,控制类操作更推荐 POST。
GPIO:
ESP32 的通用输入输出引脚。这里 GPIO1 被配置为输出,用高低电平控制 LED。
电平控制:
GPIO 输出 0 是低电平,输出 1 是高电平。本项目 LED 低电平亮,所以打开 LED 要输出 0。
事件驱动:
WiFi 连接、断开不是在主循环里一直查,而是由 ESP-IDF 在事件发生时调用回调函数,比如 wifi_event_handler()。
18. 常见问题与解决方案
Q1:为什么连接热点后仍然打不开网页?
先确认手机当前连接的是 ESP32S3 WIFI。部分手机检测到热点不能上网后,会自动切换到其他 WiFi 或移动网络。可以临时关闭移动数据再访问 http://192.168.4.1/。
Q2:为什么 LED 状态显示 ON,但灯没有亮?
检查开发板 LED 是否确实接在 GPIO1,并确认 LED 是否为低电平点亮。本项目的 LED0(0) 表示点亮。
Q3:为什么访问接口返回 404?
检查 URI 是否完全匹配。当前代码只注册了 /、/led/on、/led/off、/led/blink、/led/status。
Q4:能不能把网页放到 SPIFFS?
可以。当前分区表已经保留了 storage SPIFFS 分区,但代码还没有挂载 SPIFFS,也没有实现静态文件读取。后续可以把 HTML、CSS、JS 拆成文件放到 SPIFFS,再由 HTTP Server 按文件路径返回。
Q5:能不能控制继电器或风扇?
可以,但当前版本未实现。建议新增类似 device_ctrl.c 的模块,将 GPIO 编号、电平有效逻辑和设备状态封装起来,再新增 /relay/on、/fan/off 等接口。
19. 项目总结与可扩展方向
这个项目的工程价值在于:它把“浏览器请求 -> HTTP 路由 -> 状态解析 -> GPIO 控制 -> 硬件响应”的链路完整打通了。
当前版本控制对象是 LED,但架构已经适合继续扩展:
- 远程互联网控制:把 SoftAP 改为 STA 模式,连接路由器,再接入云服务器或 MQTT;
- 传感器数据上报:增加温湿度、光照、电流等传感器接口,并通过
/sensor/status返回 JSON; - WebSocket 实时状态刷新:当前
sdkconfig未启用 HTTPD WebSocket,可后续打开CONFIG_HTTPD_WS_SUPPORT; - 账号登录与权限控制:增加登录页面、Token 或简单 Basic Auth;
- OTA 在线升级:当前分区表没有 OTA 双分区,需要重新规划;
- 多设备联动:把
led_ctrl抽象为device_ctrl,支持继电器、风扇、电机等多路 GPIO。
对于初学者来说,这个项目最值得学习的不是某一个 API,而是它的分层思路:网络请求交给 HTTP Server,业务状态交给 led_ctrl.c,底层电平交给 BSP 驱动。这样代码后续才容易继续长大,而不是所有逻辑都塞进 app_main()。
需要源码的,请在评论区下留言。制作不易,请各位观众老爷点个赞和收藏
更多推荐



所有评论(0)