ESP32 使用ESP-IDF WiFi一键配网 源码分享
- 一、源码分享
- [二、主流 WIFI 配网方式简介](#二、主流 WIFI 配网方式简介)
-
- [1、SoftAP 配网](#1、SoftAP 配网)
- [2、Smartconfig 配网](#2、Smartconfig 配网)
- [3、Airkiss 配网](#3、Airkiss 配网)
- 三、ESP-IDF详解
-
- [1、ESP32 概述](#1、ESP32 概述)
- [2、 ESP-IDF 详解](#2、 ESP-IDF 详解)
-
- [2.1 、ESP-IDF 的核心组件与架构](#2.1 、ESP-IDF 的核心组件与架构)
- [2.2 、ESP-IDF 开发环境与工具链](#2.2 、ESP-IDF 开发环境与工具链)
- [2.3 、ESP-IDF 开发流程简述](#2.3 、ESP-IDF 开发流程简述)
- [2.4、 示例代码结构 (最简单的 Hello World)](#2.4、 示例代码结构 (最简单的 Hello World))
- [2.5、 优势与特点](#2.5、 优势与特点)
- [2.6 、适用场景](#2.6 、适用场景)
- 3、总结

一、源码分享
1、效果展示


2、开发环境搭建
参考我这篇博文:VS Code 在线安装ESP-IDF,ESP32开发环境搭建详细教程
3、源码分享
3.1、 main.c
cpp
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_eap_client.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"
#include "esp_mac.h"
static EventGroupHandle_t s_wifi_event_group;
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "smartconfig_example";
static void smartconfig_example_task(void * parm);
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) {
xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
ESP_LOGI(TAG, "Scan done");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
ESP_LOGI(TAG, "Found channel");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
ESP_LOGI(TAG, "Got SSID and password");
smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
wifi_config_t wifi_config;
uint8_t ssid[33] = { 0 };
uint8_t password[65] = { 0 };
uint8_t rvd_data[33] = { 0 };
bzero(&wifi_config, sizeof(wifi_config_t));
memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
#ifdef CONFIG_SET_MAC_ADDRESS_OF_TARGET_AP
wifi_config.sta.bssid_set = evt->bssid_set;
if (wifi_config.sta.bssid_set == true) {
ESP_LOGI(TAG, "Set MAC address of target AP: "MACSTR" ", MAC2STR(evt->bssid));
memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
}
#endif
memcpy(ssid, evt->ssid, sizeof(evt->ssid));
memcpy(password, evt->password, sizeof(evt->password));
ESP_LOGI(TAG, "SSID:%s", ssid);
ESP_LOGI(TAG, "PASSWORD:%s", password);
if (evt->type == SC_TYPE_ESPTOUCH_V2) {
ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
ESP_LOGI(TAG, "RVD_DATA:");
for (int i=0; i<33; i++) {
printf("%02x ", rvd_data[i]);
}
printf("\n");
}
ESP_ERROR_CHECK( esp_wifi_disconnect() );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
esp_wifi_connect();
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
}
}
static void initialise_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
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, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void smartconfig_example_task(void * parm)
{
EventBits_t uxBits;
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
while (1) {
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
if(uxBits & CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi Connected to ap");
}
if(uxBits & ESPTOUCH_DONE_BIT) {
ESP_LOGI(TAG, "smartconfig over");
esp_smartconfig_stop();
vTaskDelete(NULL);
}
}
}
void app_main(void)
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
}
3.2、 运行日志
c
I (478) wifi:Init static rx mgmt buffer num: 5
I (482) wifi:Init management short buffer num: 32
I (486) wifi:Init dynamic tx buffer num: 32
I (490) wifi:Init static rx buffer size: 1600
I (494) wifi:Init static rx buffer num: 10
I (498) wifi:Init dynamic rx buffer num: 32
I (502) wifi_init: rx ba win: 6
I (505) wifi_init: accept mbox: 6
I (508) wifi_init: tcpip mbox: 32
I (511) wifi_init: udp mbox: 6
I (513) wifi_init: tcp mbox: 6
I (516) wifi_init: tcp tx win: 5760
I (519) wifi_init: tcp rx win: 5760
I (523) wifi_init: tcp mss: 1440
I (526) wifi_init: WiFi IRAM OP enabled
I (529) wifi_init: WiFi RX IRAM OP enabled
I (534) phy_init: phy_version 4863,a3a4459,Oct 28 2025,14:30:06
I (609) wifi:mode : sta (e8:6b:ea:c4:46:e4)
I (610) wifi:enable tsf
I (611) main_task: Returned from app_main()
I (662) smartconfig: SC version: V3.0.3
I (5477) wifi:ic_enable_sniffer
I (5477) smartconfig: Start to find channel...
I (5478) smartconfig_example: Scan done
I (36916) smartconfig: TYPE: ESPTOUCH
I (36917) smartconfig: T|AP MAC: 18:f2:2c:df:fc:aa
I (36917) smartconfig: Found channel on 11-2. Start to get ssid and password...
I (36920) smartconfig_example: Found channel
I (39362) smartconfig: T|pswd: nimingzi
I (39362) smartconfig: T|ssid: VIP
I (39362) smartconfig: T|bssid: 18:f2:2c:df:fc:aa
I (39362) wifi:ic_disable_sniffer
I (39365) smartconfig_example: Got SSID and password
I (39369) smartconfig_example: Set MAC address of target AP: 18:f2:2c:df:fc:aa
I (39376) smartconfig_example: SSID:VIP
I (39380) smartconfig_example: PASSWORD:nimingzi
W (39385) wifi:Password length matches WPA2 standards, authmode threshold changes from OPEN to WPA2
I (39408) wifi:new:<11,2>, old:<11,2>, ap:<255,255>, sta:<11,2>, prof:1, snd_ch_cfg:0x0
I (39408) wifi:state: init -> auth (0xb0)
I (39423) wifi:state: auth -> assoc (0x0)
I (39439) wifi:state: assoc -> run (0x10)
I (39533) wifi:connected with VIP, aid = 1, channel 11, 40D, bssid = 18:f2:2c:df:fc:aa
I (39533) wifi:security: WPA2-PSK, phy: bgn, rssi: -54
I (39537) wifi:pm start, type: 1
I (39538) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
I (39553) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (39581) wifi:<ba-add>idx:0 (ifx:0, 18:f2:2c:df:fc:aa), tid:6, ssn:2, winSize:64
I (41084) esp_netif_handlers: sta ip: 192.168.11.233, mask: 255.255.255.0, gw: 192.168.11.1
I (41084) smartconfig_example: WiFi Connected to ap
I (41240) wifi:<ba-add>idx:1 (ifx:0, 18:f2:2c:df:fc:aa), tid:0, ssn:0, winSize:64
I (44110) smartconfig_example: smartconfig over
3.3、APP下载
二、主流 WIFI 配网方式简介
1、SoftAP 配网
ESP32的SoftAP(软件接入点)模式允许设备自身作为WiFi热点,使其他设备(如手机或电脑)能够直接连接。这一功能常用于设备初次配网,用户可通过连接ESP32的热点,在网页界面或专用APP中输入家庭WiFi的账号密码,完成网络配置。
工作原理
-
启动SoftAP模式
ESP32启动时开启热点,生成独立网络:
此时设备广播热点(如
ESP32_AP),用户设备可搜索并连接。 -
提供配置接口
通过HTTP服务器提供配网页面:
用户通过浏览器访问
192.168.4.1(默认AP网关)提交WiFi凭证。 -
保存并切换网络
接收到凭证后,ESP32保存至非易失存储(NVS),并切换至STA模式连接路由器:
2、Smartconfig 配网
SmartConfig 是一种由乐鑫(Espressif)开发并推广的 Wi-Fi 配网技术,主要用于解决物联网设备(如 ESP32)在首次启动时,如何便捷地连接到目标 Wi-Fi 网络的问题。其核心思想是让设备在未预先知道 Wi-Fi 名称(SSID)和密码的情况下,通过监听手机 App 发送的特殊数据包来获取这些网络凭证。
工作原理
-
设备进入监听模式:
- 当 ESP32 首次启动或需要重新配网时,它会进入一个特殊的"监听"状态(通常是混杂模式)。
- 在此状态下,ESP32 会扫描并捕获周围的 Wi-Fi 数据包。
-
手机 App 发送配置信息:
- 用户需要在同一 Wi-Fi 网络环境下(通常指手机已连接到目标路由器),打开专用的配网 App(如乐鑫的
Espressif EsptouchApp 或其开源实现ESP-Touch)。 - 用户在 App 界面输入目标 Wi-Fi 的密码。
- App 不会直接连接到 ESP32(因为 ESP32 此时还未联网),而是通过手机,向路由器发送一系列特殊格式的 UDP 广播包 或利用数据包长度编码等方式。这些数据包中隐含着目标 Wi-Fi 的 SSID 和密码信息。
- 用户需要在同一 Wi-Fi 网络环境下(通常指手机已连接到目标路由器),打开专用的配网 App(如乐鑫的
-
ESP32 捕获并解析信息:
- ESP32 在监听状态下捕获这些特殊的数据包。
- 设备内置的 SmartConfig 协议栈会解析这些数据包,从中提取出加密的 SSID 和密码。
-
尝试连接网络:
- 获取到网络凭证后,ESP32 会尝试使用这些信息连接到指定的 Wi-Fi 路由器。
- 连接成功后,ESP32 通常会通过 UDP 或 TCP 向手机 App(或指定的服务器)发送一个反馈,告知配网成功。此时 App 会显示成功提示。
3、Airkiss 配网
Airkiss 是微信平台推出的一种物联网设备无线网络配置协议,主要用于为 ESP32 等物联网设备提供免手动输入密码的 Wi-Fi 联网功能。其核心原理如下:
-
工作原理
- 广播包传输:用户通过微信小程序向设备发送加密的 Wi-Fi 配置信息(SSID 和密码),数据以 UDP 广播包形式在局域网内传输。
- 设备监听:ESP32 在混杂模式下监听广播包,通过解析特定格式的数据包获取网络凭证。
- 加密信道:配置信息通过微信服务器加密传输,确保安全性。
-
优势
- 免输入:用户无需在设备界面手动输入 Wi-Fi 密码。
- 跨平台支持:兼容 Android/iOS 设备。
- 低功耗:ESP32 可在低功耗模式下完成配网。
三、ESP-IDF详解
1、ESP32 概述
ESP32 是由乐鑫科技(Espressif Systems)推出的一款高性能、低功耗、高集成度的 Wi-Fi & 蓝牙双模系统级芯片(SoC)。它广泛应用于物联网(IoT)、智能家居、工业控制等领域。其关键特性包括:
- 双核处理器(通常为 Xtensa LX6,某些型号为 RISC-V)
- 集成 Wi-Fi (802.11 b/g/n) 和蓝牙 (包括经典蓝牙和低功耗蓝牙 BLE)
- 丰富的外设接口: S P I SPI SPI, I 2 C I2C I2C, I 2 S I2S I2S, U A R T UART UART, A D C ADC ADC, D A C DAC DAC, P W M PWM PWM, 触摸传感器等
- 充足的内存(RAM 和 Flash 选项多样)
- 强大的安全特性(如加密加速器)
- 超低功耗设计,支持多种休眠模式
2、 ESP-IDF 详解
ESP-IDF (Espressif IoT Development Framework) 是乐鑫官方为 ESP32、ESP32-S 系列、ESP32-C 系列等芯片提供的开发框架和 SDK(软件开发工具包)。它是开发基于 ESP32 芯片应用程序的首选和官方推荐工具。
2.1 、ESP-IDF 的核心组件与架构
ESP-IDF 是一个分层、模块化的框架:
- 硬件抽象层 (HAL): 提供对芯片硬件资源(如 GPIO、UART、SPI、I2C、定时器、ADC、DAC、RTC、Wi-Fi、蓝牙等)进行操作的统一接口,屏蔽底层硬件细节。
- 驱动 (Drivers): 在 HAL 之上,提供更易用、功能更丰富的设备驱动(如 SPI Flash 驱动、SD 卡驱动、以太网驱动等)。
- FreeRTOS: ESP-IDF 深度集成了开源的 FreeRTOS 实时操作系统内核。它提供了任务管理、队列、信号量、软件定时器、中断管理等机制,充分利用 ESP32 的多核特性(任务可以在不同核心上运行)。
- 中间件:
- Wi-Fi 协议栈: 实现 Wi-Fi 的 Station(客户端)、SoftAP(接入点)、Promiscuous(监听)模式。
- 蓝牙协议栈: 实现经典蓝牙和低功耗蓝牙 (BLE) 的各种角色(如 GATT Client/Server, GAP Central/Peripheral)。
- TCP/IP 协议栈 (lwIP): 轻量级的 TCP/IP 协议栈,支持 IPv4/IPv6 (部分型号)、DHCP、DNS、Socket API 等。
- 文件系统 (FATFS/VFS): 支持在 SPI Flash 或外部存储设备上使用 FAT 文件系统。
- 加密库: 提供 AES、SHA、RSA 等加密算法的软件和硬件加速实现。
- HTTP/WebSocket/MQTT 等协议库: 方便构建网络应用。
- 应用程序: 用户编写的业务逻辑代码,运行在 FreeRTOS 的任务中,通过调用下层 API 实现功能。
2.2 、ESP-IDF 开发环境与工具链
- 工具链: 基于 GNU GCC 编译器(针对 Xtensa 或 RISC-V 架构)。
- 构建系统: 使用 CMake (早期版本使用 GNU Make)。开发者编写
CMakeLists.txt文件来定义项目结构、源文件、依赖组件等。 - 配置工具:
menuconfig。这是一个基于文本的图形化配置工具(类似于 Linux Kernel 的make menuconfig),用于配置 ESP-IDF 的众多选项,如:- 选择目标芯片型号
- 配置 Wi-Fi/BT 参数
- 调整 FreeRTOS 设置(任务栈大小、优先级等)
- 配置日志输出级别和方式
- 配置内存布局
- 启用/禁用特定功能和外设驱动
- 核心工具 -
idf.py: 这是 ESP-IDF 提供的命令行工具,用于执行构建、烧录、调试、监视串口输出等几乎所有开发任务。常用命令如:idf.py set-target esp32(设置目标芯片)idf.py menuconfig(启动配置工具)idf.py build(编译项目)idf.py -p PORT flash(烧录固件到设备,PORT 为串口,如 COM3 或 /dev/ttyUSB0)idf.py -p PORT monitor(启动串口监视器,查看设备日志输出)idf.py fullclean(彻底清理构建目录)
2.3 、ESP-IDF 开发流程简述
- 环境搭建: 在 Windows、Linux 或 macOS 上安装 ESP-IDF 开发环境(包括工具链、Python、Git 等)。
- 创建项目: 使用
idf.py create-project或复制示例项目模板。 - 编写代码: 在
main目录下编写应用程序代码 (通常是main.c或app_main()函数)。 - 配置项目: 运行
idf.py menuconfig根据需求进行配置。 - 编译项目: 运行
idf.py build生成可执行固件 (.bin 文件)。 - 烧录固件: 将 ESP32 开发板连接到电脑,运行
idf.py -p PORT flash将固件烧录到设备 Flash 中。 - 监视输出: 运行
idf.py -p PORT monitor查看设备运行日志,进行调试。可以使用ESP_LOGx(如ESP_LOGI,ESP_LOGE) 函数打印不同级别的日志。
2.4、 示例代码结构 (最简单的 Hello World)
c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "MAIN";
void app_main(void)
{
while (1) {
ESP_LOGI(TAG, "Hello World!"); // 打印 Info 级别日志
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1000 毫秒 (FreeRTOS 延时)
}
}
2.5、 优势与特点
- 官方支持: 由芯片原厂维护,更新及时,与新芯片特性同步快。
- 功能全面: 提供对 ESP32 所有硬件特性和外设的底层访问。
- 性能优化: 针对 ESP32 硬件进行了深度优化(如 Wi-Fi/BT 共存)。
- 稳定性高: 经过大量商业产品验证。
- 社区活跃: 用户多,社区支持好,问题容易找到解决方案。
- 文档完善: 官方提供详尽的 API 参考指南、编程指南和示例代码。
- 模块化: 易于扩展和复用代码,方便管理大型项目。
- 开源免费: 基于 Apache 2.0 许可证。
2.6 、适用场景
ESP-IDF 适用于需要深度控制硬件、追求高性能、高稳定性或需要使用 ESP32 特有功能(如超低功耗协处理器 ULP)的应用开发。对于快速原型开发,也可以考虑基于 ESP-IDF 构建的更高级框架(如 Arduino for ESP32、MicroPython),但这些框架最终都依赖于 ESP-IDF 的底层功能。
3、总结
ESP-IDF 是开发 ESP32 系列芯片的强大、灵活且功能完备的官方框架。它提供了从硬件操作到网络协议栈的全套解决方案,结合 FreeRTOS 实现了高效的多任务处理。虽然其学习曲线相对陡峭,尤其是对于不熟悉嵌入式开发和 RTOS 的开发者,但它提供了最直接、最强大的方式来充分利用 ESP32 的潜力,是开发复杂或高性能 ESP32 应用的基石。掌握 idf.py 工具链和 menuconfig 配置是使用 ESP-IDF 的关键。
