ESP32-WIFI-WebUI控制LED
ESP32-WIFI-WebUI控制LED
逻辑流程(A = ESP32,B = 客户端)
- A:将 ESP32 配置为 AP 模式,开启 Wi-Fi 热点并作为 HTTP 服务器运行。
- B:电脑或手机连接到 ESP32 热点,建立局域网通信。
- B → A:客户端在浏览器访问
192.168.4.1,向 ESP32 发送 HTTP GET 请求。 - A → B:ESP32 响应请求,返回 HTML 网页文件。
- B:浏览器接收并解析 HTML 文件,显示网页内容。
- B → A:用户在网页上点击按钮,浏览器向 ESP32 发送带有控制参数的 HTTP GET 请求(如
led_on/led_off)。 - A:ESP32 解析 URL 参数,根据指令触发对应的回调函数执行操作(如控制 LED 开关)。
Web界面


通过手机抓包软件分析
第一次进入192.168.4.1网址

可以看到响应内容格式为
text/html
对应代码段中处理根目录请求函数构造的数据响应格式(text/html)

| 按下按键(触发发送led_on事件) | 按下按键(触发发送led_off事件) |
|---|---|
![]() |
![]() |
可以分析看到请求路径为/led

分析代码可知触发路径为/led的请求格式后,处理函数为led_handler
后面源码中有详细解释led_handler()作用
在此简略说明处理过程:获取URL,查询键值对,判断值,做出处理
重要代码段分析
初始化NVS
// 初始化NVS(非易失性存储)闪存,用于存储WiFi配置等数据
ESP_ERROR_CHECK(nvs_flash_init());
初始化 Wi-Fi AP 模式
void wifi_init_softap(void)
{
// 初始化网络接口
ESP_ERROR_CHECK(esp_netif_init());
// 创建默认事件循环
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 创建默认的Wi-Fi AP网络接口
esp_netif_create_default_wifi_ap();
// 初始化Wi-Fi配置
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 配置Wi-Fi AP参数
wifi_config_t wifi_config = {
.ap = {
.ssid = "ESP32_WEB", // 设置SSID名称
.ssid_len = strlen("ESP32_WEB"), // SSID长度
.password = "12345678", // 设置密码
.max_connection = 2, // 最大连接数
.authmode = WIFI_AUTH_WPA_WPA2_PSK // 设置认证模式为WPA/WPA2
},
};
// 如果密码为空,则将认证模式设置为开放模式
if (strlen((char *)wifi_config.ap.password) == 0)
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
// 设置Wi-Fi模式为AP模式
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
// 设置Wi-Fi配置
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
// 启动Wi-Fi
ESP_ERROR_CHECK(esp_wifi_start());
// 打印日志,显示Wi-Fi AP已启动的信息
ESP_LOGI(TAG, "Wi-Fi AP started. SSID:%s password:%s", "ESP32_WEB", "12345678");
}
初始化外设led(略)
启动HTTP服务器(注册服务器响应函数)
// 启动 HTTP 服务器
httpd_handle_t start_webserver(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
//注册根路径(/)的处理函数:
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,// 设置处理函数为index_handler
.user_ctx = NULL //回传html
};
httpd_register_uri_handler(server, &index_uri);//注册到服务器中
//注册 /led 路径的处理函数
// 定义一个httpd_uri_t类型的变量led_uri// 设置uri为"/led"
httpd_uri_t led_uri = {
.uri = "/led",
.method = HTTP_GET,
.handler = led_handler,// 设置处理函数为led_handler
.user_ctx = NULL //设置led
};
httpd_register_uri_handler(server, &led_uri);
}
return server;
}
处理/根目录请求,返回 HTML
static esp_err_t index_handler(httpd_req_t *req)——
{
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)_binary_index_html_start,
_binary_index_html_end - _binary_index_html_start);
}
处理 /led 控制请求
// 静态函数,用于处理LED控制请求
static esp_err_t led_handler(httpd_req_t *req)
{
// 定义一个字符数组,用于存储URL查询字符串
char buf[100];
// 获取URL查询字符串的长度,并加1
size_t len = httpd_req_get_url_query_len(req) + 1;
// 如果URL查询字符串的长度大于1
if (len > 1) {
// 获取URL查询字符串
httpd_req_get_url_query_str(req, buf, len);
// 定义一个字符数组,用于存储查询参数
char param[10];
// 如果查询参数为"state"
if (httpd_query_key_value(buf, "state", param, sizeof(param)) == ESP_OK) {
// 打印查询参数
ESP_LOGI(TAG, "LED state param: %s", param);
// 如果查询参数为"on"
if (strcmp(param, "on") == 0) {
// 设置LED引脚为高电平
gpio_set_level(LED_GPIO, 1);
// 设置LED状态为打开
led_on = true;
// 发送响应,LED已打开
httpd_resp_sendstr(req, "LED turned ON");
// 如果查询参数为"off"
} else if (strcmp(param, "off") == 0) {
// 设置LED引脚为低电平
gpio_set_level(LED_GPIO, 0);
// 设置LED状态为关闭
led_on = false;
// 发送响应,LED已关闭
httpd_resp_sendstr(req, "LED turned OFF");
// 如果查询参数不为"on"或"off"
} else {
// 发送响应,LED状态无效
httpd_resp_sendstr(req, "Invalid LED state");
}
// 如果查询参数不为"state"
} else {
// 发送响应,缺少"state"参数
httpd_resp_sendstr(req, "Missing 'state' param");
}
// 如果URL查询字符串的长度不大于1
} else {
// 发送响应,未提供查询参数
httpd_resp_sendstr(req, "No query provided");
}
// 返回ESP_OK
return ESP_OK;
}
Web网页端(HTML)
触发部分
<script>
let ledOn = false; //定义变量,用于存储LED状态
function toggleLED() {
// 切换LED状态函数
ledOn = !ledOn;
//更新网页上显示的LED状态:
document.getElementById("led-status").innerText =
"当前状态: " + (ledOn ? "开启" : "关闭");
// 向ESP32发送HTTP GET请求(根据固件的URL接口设计修改)
//请求的URL为 /led?state=on 或 /led?state=off,根据 ledOn 的值决定:
fetch("/led?state=" + (ledOn ? "on" : "off"))
// then 方法处理ESP32的响应:
// 检查响应是否成功(response.ok)。
// 如果响应失败,抛出一个错误,提示“网络错误”。
// 如果成功,调用 response.text() 将响应内容转换为文本。
.then((response) => {
if (!response.ok) {
throw new Error("网络错误");
}
return response.text();
})
.then((data) => {
console.log("ESP响应:", data);
//打印ESP32返回的响应数据到浏览器的控制台,便于调试。
})
.catch((error) => {
alert("请求失败: " + error);
//捕获错误并弹出一个警告框,显示错误信息
});
}
</script>
源码:
main.c
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_http_server.h"
#include "driver/gpio.h"
#define LED_GPIO GPIO_NUM_48 // 可根据板子修改,比如GPIO2或GPIO5
static const char *TAG = "WEB_LED";
// 嵌入的 HTML 网页内容
extern const uint8_t _binary_index_html_start[];
extern const uint8_t _binary_index_html_end[];
// LED 控制状态
static bool led_on = false;
// 处理根目录请求,返回 HTML
static esp_err_t index_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)_binary_index_html_start,
_binary_index_html_end - _binary_index_html_start);
}
// 处理 /led 控制请求
// 静态函数,用于处理LED控制请求
static esp_err_t led_handler(httpd_req_t *req)
{
// 定义一个字符数组,用于存储URL查询字符串
char buf[100];
// 获取URL查询字符串的长度,并加1
size_t len = httpd_req_get_url_query_len(req) + 1;
// 如果URL查询字符串的长度大于1
if (len > 1) {
// 获取URL查询字符串
httpd_req_get_url_query_str(req, buf, len);
// 定义一个字符数组,用于存储查询参数
char param[10];
// 如果查询参数为"state"
if (httpd_query_key_value(buf, "state", param, sizeof(param)) == ESP_OK) {
// 打印查询参数
ESP_LOGI(TAG, "LED state param: %s", param);
// 如果查询参数为"on"
if (strcmp(param, "on") == 0) {
// 设置LED引脚为高电平
gpio_set_level(LED_GPIO, 1);
// 设置LED状态为打开
led_on = true;
// 发送响应,LED已打开
httpd_resp_sendstr(req, "LED turned ON");
// 如果查询参数为"off"
} else if (strcmp(param, "off") == 0) {
// 设置LED引脚为低电平
gpio_set_level(LED_GPIO, 0);
// 设置LED状态为关闭
led_on = false;
// 发送响应,LED已关闭
httpd_resp_sendstr(req, "LED turned OFF");
// 如果查询参数不为"on"或"off"
} else {
// 发送响应,LED状态无效
httpd_resp_sendstr(req, "Invalid LED state");
}
// 如果查询参数不为"state"
} else {
// 发送响应,缺少"state"参数
httpd_resp_sendstr(req, "Missing 'state' param");
}
// 如果URL查询字符串的长度不大于1
} else {
// 发送响应,未提供查询参数
httpd_resp_sendstr(req, "No query provided");
}
// 返回ESP_OK
return ESP_OK;
}
// 初始化 GPIO
void led_gpio_init(void)
{
gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << LED_GPIO,
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = 0,
.pull_up_en = 0,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
gpio_set_level(LED_GPIO, 0); // 默认关闭
}
// 启动 HTTP 服务器
httpd_handle_t start_webserver(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &index_uri);
// 定义一个httpd_uri_t类型的变量led_uri// 设置uri为"/led"
httpd_uri_t led_uri = {
.uri = "/led",
.method = HTTP_GET,
.handler = led_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &led_uri);
}
return server;
}
// 初始化 Wi-Fi AP 模式
void wifi_init_softap(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = {
.ap = {
.ssid = "ESP32_WEB",
.ssid_len = strlen("ESP32_WEB"),
.password = "12345678",
.max_connection = 2,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
if (strlen((char *)wifi_config.ap.password) == 0)
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wi-Fi AP started. SSID:%s password:%s", "ESP32_WEB", "12345678");
}
// 主函数
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
wifi_init_softap();
led_gpio_init();
start_webserver();
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ESP32 Web Server</title>
<style>
body {
background-color: #202020;
color: #fff;
text-align: center;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
padding: 20px;
box-sizing: border-box;
font-family: Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
}
h1 {
font-size: 5vw;
margin-bottom: 5vh;
}
#led-status {
font-size: 4vw;
margin-bottom: 2vh;
}
button {
padding: 10px 20px;
font-size: 4vw;
background-color: #0cc79e;
color: #202020;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0aa386;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #444;
display: flex;
flex-direction: column;
align-items: flex-end;
font-size: 3vw;
}
footer small {
color: #aaa;
line-height: 1.5em;
word-break: break-word;
}
@media (max-width: 480px) {
footer {
align-items: flex-start;
}
}
</style>
</head>
<body>
<h1>ESP32 LED 控制</h1>
<div id="led-status">当前状态: 关闭</div>
<button onclick="toggleLED()">切换 LED 开关</button>
<footer>
天雾雨涟水
<small>简介:ESP32静态Web项目练习</small>
<small>本页面为静态网站,部署在ESP32S3(服务器):</small>
<small style="color: #0cc79e"
>ESP32s3做服务器(AP模式),手机连接esp热点,浏览器进入192.168.4.1</small
>
<small style="color: #595d5c">html的内容很多,之后再慢慢优化界面</small>
<small style="color: #595d5c">将就着先用^V^</small>
<small style="color: #595d5c">给自己引流~</small>
<small style="color: #97a19e">个人博客链接:</small>
<small style="color: #25c099"
>https://www.cnblogs.com/tianwuyvlianshui/</small
>
<small style="color: #97a19e">Gitee链接:</small>
<small style="color: #25c099">https://gitee.com/sword-level_0/</small>
<small style="color: #97a19e">Github链接:</small>
<small style="color: #25c099">https://github.com/jianzhiji/</small>
</footer>
<script>
let ledOn = false;
function toggleLED() {
ledOn = !ledOn;
document.getElementById("led-status").innerText =
"当前状态: " + (ledOn ? "开启" : "关闭");
// 向ESP32发送GET请求(根据你固件的URL接口设计修改)
fetch("/led?state=" + (ledOn ? "on" : "off"))
.then((response) => {
if (!response.ok) {
throw new Error("网络错误");
}
return response.text();
})
.then((data) => {
console.log("ESP响应:", data);
})
.catch((error) => {
alert("请求失败: " + error);
});
}
</script>
</body>
</html>
CMakeLists.txt
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
# 嵌入 HTML 和 PNG 到可执行文件(将源文件转化成bin,二进制文件)
target_add_binary_data(${COMPONENT_TARGET} "index.html" TEXT)
ESP32-WIFI-WebUI控制LED的更多相关文章
- C#与Arduino通过串口通信来控制LED灯的状态
一.引言 最近摆弄了一段时间的Arduino,发现Arduino做一些电子类项目.监控.机器人.电子玩具比较容易,并且Arduino与.NET程序集成也不难.接下来介绍一个简单的小程序,C#做的一个W ...
- 利用DoHome APP和音箱控制LED灯实验参考步骤
准备材料: Arduino Uno 一块 Arduino 扩展板 购买链接 DT-06模块一个 购买链接 安卓手机一个 小度音箱一个 小灯珠一个 杜邦线若干 1.DT-06固 ...
- 18-ESP8266 SDK开发基础入门篇--TCP 服务器 RTOS版,串口透传,TCP客户端控制LED
https://www.cnblogs.com/yangfengwu/p/11112015.html 先规定一下协议 aa 55 02 01 F1 4C 控制LED点亮 F1 4C为CRC高位和低位 ...
- 基于arduino UNO R3+ESP8266控制LED灯的开关(无USB转TTL工具实现)
最近由于项目要求,需要开发物联网云平台,而本人对硬件和通信技术一窍不通,故而选择arduino这一简单单片机来实现学习掌握基础的硬件和通信技术. 下面就是本人通过查阅大佬资料做的一个整合版本的通过手机 ...
- ESP32:蓝牙BLE控制M3508电机
ESP32:蓝牙BLE控制M3508电机 先给各位朋友拜个年,祝大家新春快乐,事事顺利,身体健康啊! 还是熟悉的3508,内容概述: ESP32主控 蓝牙BLE通信 使用实时系统(FreeRTOS) ...
- 嵌入式Linux学习入门:控制LED灯
记录自己linux学习过程,让自己能够一直坚持下去 1.原理图分析: nLED_1, nLED_2, nLED_4, 给低电平则对应LED灯亮,高电平则对应LED灯灭, S3C2440芯片GPF4-G ...
- [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)
说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...
- [ZigBee] 13、ZigBee基础阶段性回顾与加深理解——用定时器1产生PWM来控制LED亮度(七色灯)
引言:PWM对于很多软件工程师可能又熟悉又陌生,以PWM调节LED亮度为例,其本质是在每个周期都偷工减料一些,整体表现出LED欠压亮度不同的效果.像大家看到的七色彩灯其原理也类似,只是用3路PWM分别 ...
- 树莓派 LED+蜂鸣+声音传感器+红外模块组合打造声控/红外控制LED
昨天搞了控制LED,玩了第一个,剩下的就感觉很简单了,这里记录一下 先来几张照片 玩了蜂蜜模块才发现规律,一般这种模块,都会有三个针脚,VCC(3.3V或5V供电输出针脚).GNC(对应GPIO针脚的 ...
- (三)开关检测来控制LED灯的亮灭
开关检测案例一: 具体电路图如下: K1--K4闭合,控制 D1—D4 亮灭 产生的问题: 1.关于 R8 R9 R7 R10 的阻值选择问题,倘若太大的话, 比如10K 不管开关断开还是闭合,好像 ...
随机推荐
- C#实现SSE通信方式的MCP Server
前面的课程,我们使用MCP Server,用的是网络上魔搭提供的. 下面我们一起来实现,用C#实现自己的MCP Server. MCP Server通信方式支持SSE.Stdio. 下面我们先实现SS ...
- 基于StringUtils实现List和String字符串互转
将以逗号分割的字符串转换成List类型: String ids= "1,2,32,59,96"; List<Long> idsList = Arrays.asList( ...
- Synchronized是怎么实现的?
回答重点 synchronized 实现原理依赖于JVM 的 Monitor(监视器锁)和对象头(Object Header) synchronized 修饰代码块:会在代码块的前后插入 monito ...
- Maui 实践:为控件动态扩展 DragDrop 能力
作者:夏群林 原创 2025.6.9 拖放的实现,和其他的 GestureRecognizer 不同,需要 DragGestureRecognizer 与 DropGestureRecognizer ...
- ArkUI-X与Android桥接通信之方法回调
平台桥接用于客户端(ArkUI)和平台(Android或iOS)之间传递消息,即用于ArkUI与平台双向数据传递.ArkUI侧调用平台的方法.平台调用ArkUI侧的方法.本文主要介绍Android平台 ...
- Golang的格式化输出
一.格式化说明符 通用占位符: %v 值的默认格式表示 %+v 类似%v,但输出结构体时会添加字段名 %#v 值的Go语法表示 %T 打印值的类型 %% 输出百分号 布尔型占位符: %t 接收bool ...
- ChatMoney是你创业自由副业的plan B!
本文由 ChatMoney团队出品 人生永远要有Plan B,在当下的市场经济环境中,工作收入和日常支出完全不能平衡,导致生活质量越来越不理想.如果觉得实在撑不下去,也许可以换个思路.我在工作之余,也 ...
- 【前端AI实践】泛谈AI实践:技术大牛们早就在用的AI在前端领域的场景
写代码有时候就像点外卖 -- 你得选对工具.用好方法,才能又快又好地解决问题.AI 就像是你编程路上的"厨房助手",帮你搞定重复劳动.理清逻辑.甚至还能给你提建议. 下面我们就从几 ...
- ArkUI-X平台桥接Bridge说明
简介 平台桥接用于客户端(ArkUI)和平台(Android或iOS)之间传递消息,即用于ArkUI与平台双向数据传递.ArkUI侧调用平台的方法.平台调用ArkUI侧的方法. 以Android平台为 ...
- 指标管理+AI大模型深度融合,开启智能数据分析管理新时代
随着企业数字化转型的加速,数据管理和分析变得越来越重要.传统的指标管理平台虽然已经能够帮助企业有效地收集.计算.管理和展示关键指标,但在业务分析层面,面对日益复杂的数据环境和业务需求,单纯依靠人工分析 ...

