1. 如何使用 ESP32-S3 连接 WiFi(包括手机热点)?
下图为 station 模式下的宏观场景,其中包含不同阶段的具体描述: 
1.1 ESP32-S3 有哪些 WiFi 支持?
需要了解的有以下几点:
- WiFi Station 模式 也就是 ESP32-S3 作为终端,去连接家里的 WiFi。
- Soft AP 模式(接入点) 将 ESP32-S3 打开热点,让手机来连接我们的 ESP32-S3。
- Station + Soft AP 共存模式 即既能连接 WiFi,也能自身作为热点。
- Scan 模式 用 ESP32-S3 扫描附近的 WiFi 热点,并将输出结果展现出来,这通常是连接前的辅助操作。
- 协议与频段支持 目前 ESP32-S3 支持 IEEE 802.11 b/g/n 协议,均属于 2.4GHz 频段。 需要注意的是,目前的 ESP32 芯片系列仅支持 2.4GHz WiFi 网络。如果需要连接 5GHz 网络,可以选择支持双频的 ESP32-C5 芯片。
1.2 Station、SoftAP、Scan 的区别
| 模式 | 说明 |
|---|---|
| WiFi Station (STA) | ESP32-S3 作为客户端,连接到外部路由器或手机热点,获取 IP 地址上网。 |
| SoftAP | ESP32-S3 作为热点(接入点),其他设备(手机、电脑等)可以连接到它。 |
| Scan | 扫描周围可用的 AP,获取 SSID、信号强度、加密方式等信息,通常是连接前的辅助操作。 |
1.3 连接 WiFi 的完整流程
使用 ESP-IDF 在 ESP32-S3 上连接 WiFi(Station 模式)分为以下几个阶段:
phase 1:Wi-Fi/LwIP 初始化
按顺序执行以下步骤:
- 调用
esp_netif_init()创建 LwIP 核心任务并初始化 TCP/IP 协议栈。 - 调用
esp_event_loop_create_default()创建系统事件任务,初始化事件回调。 - 调用
esp_netif_create_default_wifi_sta()创建默认的 Station 网络接口实例。 - 调用
esp_wifi_init()创建 Wi-Fi 驱动任务并初始化驱动。 - 创建应用程序任务(可选)。 注意:始终使用
WIFI_INIT_CONFIG_DEFAULT宏来初始化配置结构体。
phase 2:Wi-Fi 配置
调用 esp_wifi_set_mode(WIFI_MODE_STA) 设置为 Station 模式,再调用 esp_wifi_set_config() 配置目标 AP 的 SSID、密码等参数。配置信息会保存到 NVS Flash 中,下次上电无需重新配置。
phase 3:Wi-Fi 启动
调用 esp_wifi_start() 启动 Wi-Fi 驱动。启动成功后会触发 WIFI_EVENT_STA_START 事件。
phase 4:Wi-Fi 连接
在收到 WIFI_EVENT_STA_START 事件后,调用 esp_wifi_connect() 发起连接。连接过程包括:
- 扫描阶段:扫描目标 AP
- 认证阶段:发送认证请求
- 关联阶段:发送关联请求
- 四次握手阶段:完成密钥协商 连接成功后触发
WIFI_EVENT_STA_CONNECTED事件,随后 DHCP 客户端自动获取 IP 地址。
2. 如何使用ESP32-S3 连接 WiFi 完整流程(Station 模式)?
2.1 流程总览
连接 WiFi 分为以下几个阶段,必须按顺序执行:Station 场景🔗
- 初始化阶段:初始化 TCP/IP 协议栈、事件循环、网络接口、Wi-Fi 驱动
- 配置阶段:设置 WiFi 模式为 Station,配置 SSID 和密码
- 启动阶段:调用
esp_wifi_start()启动 Wi-Fi 驱动 - 连接阶段:驱动触发事件后,调用
esp_wifi_connect()发起连接,等待获取 IP
I (xxxx) wifi_sta: Wi-Fi 已启动,正在尝试连接...
I (xxxx) wifi_sta: 连接成功!获取到 IP 地址:192.168.x.x
I (xxxx) wifi_sta: 成功连接到 WiFi:WiFi_Name(你家 Your Home's Wifi name.)
Wi-Fi扫描成功
Wi-Fi 连接成功
2.2 ESP32 S3 连接 WiFi
C
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
/* ===== 宏定义 ===== */
#define WIFI_SSID "M" // 你家 WiFi 名称
#define WIFI_PASS "QWERasdf1234...." // 你家 WiFi 密码
#define WIFI_MAX_RETRY 5 // 最大重试次数
#define WIFI_CONNECTED_BIT BIT0 // 连接成功标志位
#define WIFI_FAIL_BIT BIT1 // 连接失败标志位
/* ===== 全局变量 ===== */
static EventGroupHandle_t s_wifi_event_group; // 事件组句柄
static int s_retry_num = 0; // 重试计数器
static const char *TAG = "wifi_sta";
static const char *WiFi_Scan_TAG = "wifi_scan";
/* ===== 事件回调函数 ===== */
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
// 情况1:Wi-Fi 驱动启动完成,立即发起连接
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
ESP_LOGI(TAG, "Wi-Fi 已启动,正在尝试连接...");
// 情况2:连接断开或失败,尝试重连
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < WIFI_MAX_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGW(TAG, "连接失败,正在重试(第 %d 次)...", s_retry_num);
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
ESP_LOGE(TAG, "已达到最大重试次数,连接失败!");
}
// 情况3:成功获取 IP 地址,连接完全成功
} 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, "连接成功!获取到 IP 地址:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
/* ===== Wi-Fi 扫描函数 ===== */
/* 注意:调用前 Wi-Fi 必须已经 start,调用后会 stop,供后续连接重新 start */
void wifi_scan(void)
{
// 配置扫描参数:扫描所有信道、所有 AP,包括隐藏网络
wifi_scan_config_t scan_config = {
.ssid = NULL,
.bssid = NULL,
.channel = 0,
.show_hidden = true
};
// 阻塞扫描:等待扫描完成后再继续执行
ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true));
// 获取扫描到的 AP 数量
uint16_t ap_count = 0;
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_LOGI(WiFi_Scan_TAG, "共扫描到 %d 个 Wi-Fi 网络", ap_count);
// 分配内存存放扫描结果
wifi_ap_record_t *ap_list = malloc(sizeof(wifi_ap_record_t) * ap_count);
if (ap_list == NULL) {
ESP_LOGE(WiFi_Scan_TAG, "内存分配失败");
return;
}
// 获取扫描结果列表
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_list));
// 打印表头
ESP_LOGI(WiFi_Scan_TAG, "%-32s | %-20s | %s | %s",
"SSID(网络名称)", "BSSID(MAC地址)", "信道", "信号强度(dBm)");
ESP_LOGI(WiFi_Scan_TAG, "----------------------------------------------------------------");
// 逐条打印每个 AP 的信息
for (int i = 0; i < ap_count; i++) {
ESP_LOGI(WiFi_Scan_TAG, "%-32s | %02x:%02x:%02x:%02x:%02x:%02x | %-4d | %d",
(char *)ap_list[i].ssid,
ap_list[i].bssid[0], ap_list[i].bssid[1], ap_list[i].bssid[2],
ap_list[i].bssid[3], ap_list[i].bssid[4], ap_list[i].bssid[5],
ap_list[i].primary, // 信道号
ap_list[i].rssi); // 信号强度(越接近 0 越强)
}
free(ap_list); // 释放内存,防止内存泄漏
// 扫描完成后停止 Wi-Fi,为后续连接做准备
ESP_ERROR_CHECK(esp_wifi_stop());
ESP_LOGI(WiFi_Scan_TAG, "扫描完成,Wi-Fi 已停止,准备发起连接...");
}
/* ===== Wi-Fi Station 连接函数 ===== */
void wifi_connect_sta(void)
{
// 第1步:创建事件组(用于等待连接结果)
s_wifi_event_group = xEventGroupCreate();
// 第2步:注册事件回调函数(必须在 esp_wifi_start() 之前注册!)
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(
WIFI_EVENT, ESP_EVENT_ANY_ID,
&event_handler, NULL, &instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(
IP_EVENT, IP_EVENT_STA_GOT_IP,
&event_handler, NULL, &instance_got_ip));
// 第3步:配置 Wi-Fi 连接参数(SSID、密码、加密方式)
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK, // 最低接受 WPA2 加密
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH, // WPA3 兼容配置
.sae_h2e_identifier = "",
},
};
// 第4步:设置 Station 模式
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
// 第5步:将连接配置写入 Wi-Fi 驱动
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
// 第6步:启动 Wi-Fi 驱动
// 启动后会触发 WIFI_EVENT_STA_START 事件
// 事件回调函数会自动调用 esp_wifi_connect() 发起连接
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_connect_sta 完成,等待连接结果...");
// 第7步:阻塞等待,直到连接成功或失败
EventBits_t bits = xEventGroupWaitBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, // 不自动清除标志位
pdFALSE, // 任意一个标志位满足即返回
portMAX_DELAY); // 永久等待
// 第8步:根据结果输出日志
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "成功连接到 WiFi:%s", WIFI_SSID);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGE(TAG, "连接 WiFi 失败:%s,已超过最大重试次数", WIFI_SSID);
} else {
ESP_LOGE(TAG, "未知错误");
}
}
/* ===== 程序入口 ===== */
void app_main(void)
{
/* 第0步:初始化 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);
/* 第1步:初始化 TCP/IP 协议栈(只执行一次)*/
ESP_ERROR_CHECK(esp_netif_init());
/* 第2步:创建默认事件循环(只执行一次!不能重复调用)*/
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* 第3步:创建默认 Station 网络接口(只执行一次)*/
esp_netif_create_default_wifi_sta();
/* 第4步:初始化 Wi-Fi 驱动(只执行一次)*/
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
/* 第5步:设置 Station 模式(只执行一次)*/
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
/* 第6步:启动 Wi-Fi 驱动,用于扫描(只执行一次)*/
ESP_ERROR_CHECK(esp_wifi_start());
/* 第7步:执行扫描,扫描完成后内部会调用 esp_wifi_stop() */
ESP_LOGI(TAG, "开始扫描 Wi-Fi...");
wifi_scan();
/* 第8步:等待 1 秒,确保日志输出完整 */
vTaskDelay(pdMS_TO_TICKS(1000));
/* 第9步:发起 Station 连接
* wifi_connect_sta() 内部会重新注册回调、配置参数、
* 调用 esp_wifi_start()(此时 Wi-Fi 已被 wifi_scan 停止,可以重新 start)
*/
ESP_LOGI(TAG, "开始连接 WiFi...");
wifi_connect_sta();
}