目录
前言
ESP32 作为面向物联网应用的芯片,具备较强的无线联网能力。为便于系统介绍其联网功能,本文单独开设联网篇,首先讲解 Wi‑Fi 扫描的实现方法。
本文使用微雪 ESP32-C6-DEV-KIT-N16 开发板,开发环境为 ESP-IDF 6.0.1。工程模板与前文 ESP32-P4 示例模板基本一致,相关代码可从我的 Gitee 仓库获取。
一、WIFI的相关术语
关于WIFI相信同学们已经很熟悉了,但是关于WIFI的一些专业知识肯定有一些薄弱,那么这里来进行一个复习。
1.1 基础术语
AP(Access Point,接入点)
无线接入点,发出 WiFi 信号的设备。家用无线路由器本身就集成了 AP 功能。
STA(Station,站点)
连接到无线网络的终端设备,也就是你的手机、笔记本电脑、智能电视、智能音箱等。AP 和 STA 是 WiFi 中最核心的一对角色关系:AP 发信号,STA 连上来。
SSID(Service Set Identifier)
WiFi 网络的名字。你在设备上看到的 WiFi 列表里的每一个名称就是一个 SSID。
BSSID(Basic Service Set Identifier)
AP 的 MAC 地址,是物理层面唯一标识。同一个 SSID 下可能有多个 BSSID(比如 Mesh 网络的多个节点)。
频段(Band)
WiFi 使用的无线电频率范围,目前主要有三个:
-
2.4GHz:穿墙能力强、覆盖远,但干扰多(蓝牙、微波炉、无线鼠标都用这个频段),速度相对慢
-
5GHz:速度快、干扰少,但穿墙能力弱、覆盖距离短
-
6GHz:WiFi 6E 和 WiFi 7 新增的频段,更宽的频道、更高的速度、更少的干扰
1.2 标准与协议
WiFi 4 / 5 / 6 / 7
IEEE 802.11 协议族的商业命名:
| 商业名 | 技术标准 | 推出年份 | 主要提升 | | --- | --- | --- | --- | | WiFi 4 | 802.11n | 2009 | 引入 MIMO,速度突破百兆 | | WiFi 5 | 802.11ac | 2014 | 主攻 5GHz,速度达 Gbps 级别 | | WiFi 6 | 802.11ax | 2019 | 高密场景优化,更低延迟,更省电 | | WiFi 7 | 802.11be | 2024 | 320MHz 频宽,速度可达 30Gbps+ |
MIMO(Multiple-Input Multiple-Output)
多入多出技术。路由器用多根天线同时收发数据,就像多条车道同时跑车。常见标注如 2x2 MIMO、4x4 MIMO。
MU-MIMO(Multi-User MIMO)
多用户 MIMO,允许路由器同时和多个 STA 通信。WiFi 5 只支持下行 MU-MIMO,WiFi 6 支持上下行都支持。
OFDMA(Orthogonal Frequency Division Multiple Access)
正交频分多址,WiFi 6 引入的技术。把一个信道分成更小的子信道,可以在同一时刻给多个设备发送小数据包,大幅降低延迟、提升多设备场景下的效率。
1.3 安全相关
WEP
最早的加密协议,极其脆弱,几分钟就能破解,早已被淘汰。
WPA / WPA2 / WPA3
-
WPA:WEP 的替代品,现在也已不安全
-
WPA2:目前最广泛使用的标准,采用 AES 加密,对普通家庭足够安全
-
WPA3:最新标准,引入 SAE 握手(替代 WPA2 的 PSK),防暴力破解,即使密码弱也不易被离线破解
WPS(WiFi Protected Setup)
通过按路由器上的按钮或输入 PIN 码快速连接 WiFi。但 PIN 方式存在严重安全漏洞,建议在路由器后台关闭 WPS。
MAC 地址过滤
路由器设置只允许特定 MAC 地址的设备连接。看起来能增强安全,但实际上 MAC 地址可以伪造,防御能力有限。
1.4 信号与性能
dBm(分贝毫瓦)
信号强度的单位,通常为负值,越接近 0 越强:
- -30 dBm:极强(紧贴路由器)
- -50 dBm:良好
- -67 dBm:可靠(高清视频流的最低要求)
- -80 dBm:弱,可能不稳定
- -90 dBm:几乎不可用
RSSI(Received Signal Strength Indicator)
接收信号强度指示,与 dBm 类似,用来衡量 STA 收到的 AP 信号强弱。经常直接用dBm做单位。
信道(Channel)
WiFi 工作的具体频率划分。2.4GHz 国内有 13 个信道(1-13),其中 1、6、11 互不重叠。5GHz 信道更多,且支持更宽的频宽(20/40/80/160MHz)。
信道干扰(Co-Channel Interference / Adjacent-Channel Interference)
邻居的 WiFi 用相同或相邻信道时产生的互相干扰,是导致网速变慢甚至不稳定的常见原因。
频宽(Channel Bandwidth)
信道的宽度,越宽速度越快但越容易被干扰。常见的:20MHz、40MHz、80MHz、160MHz。
1.5 组网与拓扑
Mesh(网状网络)
多个 AP 节点组成一个统一的 WiFi 网络,设备在不同节点间自动切换,全屋只有一个 SSID,实现无缝覆盖。三频 Mesh 会用单独一个 5GHz 频段做节点间回传,性能更好。
漫游(Roaming)
STA 从一个 AP 的覆盖范围移动到另一个 AP 时自动切换连接的过程。
802.11k / 802.11v / 802.11r(快速漫游协议)
- 802.11k:让 STA 提前知道附近有哪些 AP 可选
- 802.11v:让 AP 主动建议 STA 切换到哪个 AP
- 802.11r:简化重认证流程,切换更快
这三者合称 快速漫游,对视频通话、在线游戏等实时业务体验提升明显。
WDS(Wireless Distribution System)
无线分布式系统,允许 AP 之间通过无线方式互联,扩展覆盖范围。相比 Mesh 配置复杂,性能也差一些。
中继(Repeater / Extender)
接收主路由的信号再转发出去,会损失一半带宽(半双工),而且会增加延迟。Mesh 在很大程度上已经取代了传统的中继模式。
1.6 网络功能
DHCP(Dynamic Host Configuration Protocol)
自动给接入的设备分配 IP 地址。路由器默认会开启 DHCP 服务,你连上 WiFi 时自动获取 IP 就是它在工作。
NAT(Network Address Translation)
网络地址转换,把局域网内多个设备的私有 IP 映射到一个公网 IP 上网。这是家用路由器最核心的功能之一。
LAN / WAN 端口
- WAN 口:接光猫或入户宽带
- LAN 口:接家里的有线设备
VLAN(Virtual Local Area Network)
虚拟局域网,把一个物理网络划分成多个逻辑隔离的网络。常用于区分访客网络和家庭网络,或者 IPTV 和普通上网走不同的 VLAN。
QoS(Quality of Service)
服务质量,路由器可以优先处理某些流量。比如设置游戏和视频通话的优先级高于下载,保证关键业务不卡顿。
端口转发(Port Forwarding)
把从外网访问特定端口的流量转发到内网某台设备。搭建 NAS、远程访问摄像头、自建网站等场景常用。
UPnP(Universal Plug and Play)
设备自动在路由器上开端口,简化配置。虽然方便但有安全隐患,建议在不必要时关闭。
1.7 实用场景
双频 / 三频
- 双频:同时支持 2.4GHz 和 5GHz
- 三频:一个 2.4GHz + 两个 5GHz,多见于高端 Mesh 路由器,其中一个 5GHz 专门用于节点间回传
天线增益(dBi)
天线具有将信号集中方向的能力。增益越高信号传得越远,但覆盖角度越小。家用路由器通常用 3-5dBi 的天线。
PoE(Power over Ethernet)
通过网线同时传输数据和电力。企业级 AP 常用 PoE 供电,只需要一根网线既上网又取电。
双频合一
路由器把 2.4GHz 和 5GHz 使用同一个 SSID,让设备自己选择连哪个频段。但有些设备会固执地连在 2.4GHz 上不切回 5GHz,所以很多人会分开设置两个 SSID。
Beamforming(波束成形)
路由器将信号定向发射到特定设备的方向,而不是全向广播,可以提升特定设备的信号质量和速度。
WAN 聚合 / LAN 聚合
把两个网口绑定成一个,实现双倍带宽。适合 NAS 等需要大带宽的场景。
这份算是比较完整的常用 WiFi 术语清单了。涉及到的专业名词较多,但都进行了非常通俗的解释,结合现实实例还是比较好理解的。
二、工程实现
2.1 WIFI组件代码展览
CMakeLists.txt
idf_component_register(SRCS "wifi_event.c" "scan.c"
INCLUDE_DIRS "include"
REQUIRES basic esp_event esp_netif esp_wifi)
scan.c
/* ==========================================================================
* WiFi 扫描模块 — 实现文件
*
* 职责:
* 1. 初始化 NVS、TCP/IP 协议栈、事件循环及 WiFi 子系统
* 2. 将 WiFi 设为 STA 模式并触发主动全信道扫描
* 3. 注册 "scan" 控制台命令,供用户手动触发重新扫描
*
* 依赖:
* - ESP-IDF:nvs_flash、esp_netif、esp_wifi、esp_event、esp_console、esp_log
* - 本组件:scan.h(函数声明)、wifi_event.h(事件处理回调)
* ========================================================================== */
/* ---------- 头文件 ---------- */
#include "scan.h" /* 本模块接口声明 */
#include "wifi_event.h" /* WiFi 事件处理回调 */
/* ---------- 模块常量 ---------- */
/** 模块日志标签,用于 ESP_LOGx 输出 */
static const char *TAG = "wifi_scan";
/* ======================================================================
* 控制台命令(内部函数)
* ====================================================================== */
/**
* @brief "scan" 命令回调 —— 触发一次全信道主动扫描
*
* @param[in] argc 参数个数(未使用)
* @param[in] argv 参数列表(未使用)
* @return 始终返回 0
*/
static int scan_cmd_handler(int argc, char **argv)
{
ESP_LOGI(TAG, "触发重新扫描...");
esp_err_t ret = esp_wifi_scan_start(NULL, false);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "扫描启动失败: %s", esp_err_to_name(ret));
}
return 0;
}
/**
* @brief 向控制台框架注册 "scan" 命令
*/
static void register_scan_cmd(void)
{
const esp_console_cmd_t cmd = {
.command = "scan",
.help = "触发一次 WiFi 全信道扫描,结果通过日志输出",
.hint = NULL,
.func = &scan_cmd_handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
/* ======================================================================
* 公开 API
* ====================================================================== */
/**
* @brief 初始化 WiFi 子系统并触发首次全信道扫描
*
* 初始化流程(按序执行):
* 1. 初始化 NVS —— WiFi 校准数据存储依赖
* 2. 初始化 TCP/IP 协议栈 —— esp_netif_init()
* 3. 创建默认事件循环 —— 供后续事件注册使用
* 4. 初始化 WiFi 子系统 —— 使用默认配置 WIFI_INIT_CONFIG_DEFAULT()
* 5. 注册 WiFi 事件回调 —— 监听 WIFI_EVENT 下全部事件
* 6. 设置为 STA 模式并启动 WiFi
* 7. 发起主动全信道扫描
* 8. 注册 "scan" 控制台命令
*
* @note 本函数依赖 NVS、esp_netif、WiFi 驱动的正确初始化,
* 任一子步骤失败将通过 ESP_ERROR_CHECK 直接 abort。
*/
void wifi_scan_init(void)
{
/* ---- 1. 初始化 NVS ---- */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
/* NVS 分区已满或版本不兼容,擦除后重新初始化 */
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
/* ---- 2. 初始化 TCP/IP 协议栈 ---- */
ESP_ERROR_CHECK(esp_netif_init());
/* ---- 3. 创建默认事件循环 ---- */
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* ---- 4. 初始化 WiFi 子系统(默认配置) ---- */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
/* ---- 5. 注册 WiFi 事件回调(监听 WIFI_EVENT 下全部事件) ---- */
ESP_ERROR_CHECK(
esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL, /* arg: 未使用 */
NULL)); /* instance: 未保存句柄 */
/* ---- 6. 设为 STA 模式并启动 WiFi ---- */
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
/* ---- 7. 发起主动全信道扫描 ---- */
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, false));
ESP_LOGI(TAG, "WiFi 初始化完成,开始扫描 WiFi 网络...");
/* ---- 8. 注册 scan 控制台命令 ---- */
register_scan_cmd();
}
wifi_event.c
/* ==========================================================================
* WiFi 事件处理模块 — 实现文件
*
* 功能:
* 1. 统一处理 WiFi 及 IP 事件(扫描完成、连接/断开、获取 IP 等)
* 2. 管理扫描结果(AP 记录缓冲区)
* 3. 断开连接时按条件自动重连
*
* 依赖:
* - ESP-IDF: esp_wifi, esp_event, esp_log, esp_netif
* - FreeRTOS: vTaskDelay(重连延时)
* ========================================================================== */
/* ---------- 头文件 ---------- */
#include <stdio.h> /* printf */
#include <string.h> /* strcmp, strncpy */
#include <inttypes.h> /* PRIu16 */
#include "wifi_event.h" /* 本模块接口声明及 wifi_event */
/* ---------- 宏定义 ---------- */
/** 模块日志标签 */
static const char *TAG = "wifi_event";
/* ---------- 全局实例定义 ---------- */
/** WiFi 事件模块全局状态(其他模块通过 wifi_event 直接访问) */
struct wifi_event_flags wifi_event = {
.max_retry = 20,
.retry_delay_ms = 500,
.retry_enabled = true,
};
/* ======================================================================
* 内部辅助函数
* ====================================================================== */
/**
* @brief 将密码类型枚举转为可读字符串
*
* 覆盖 ESP-IDF 定义的全部密码类型(wifi_cipher_type_t)。
*/
static const char *cipher_type_to_str(wifi_cipher_type_t type)
{
switch (type) {
case WIFI_CIPHER_TYPE_NONE: return "无";
case WIFI_CIPHER_TYPE_WEP40: return "WEP-40";
case WIFI_CIPHER_TYPE_WEP104: return "WEP-104";
case WIFI_CIPHER_TYPE_TKIP: return "TKIP";
case WIFI_CIPHER_TYPE_CCMP: return "CCMP (AES)";
case WIFI_CIPHER_TYPE_TKIP_CCMP: return "TKIP+CCMP";
case WIFI_CIPHER_TYPE_AES_CMAC128: return "AES-CMAC-128";
case WIFI_CIPHER_TYPE_SMS4: return "SMS4 (WAPI)";
case WIFI_CIPHER_TYPE_GCMP: return "GCMP";
case WIFI_CIPHER_TYPE_GCMP256: return "GCMP-256";
case WIFI_CIPHER_TYPE_AES_GMAC128: return "AES-GMAC-128";
case WIFI_CIPHER_TYPE_AES_GMAC256: return "AES-GMAC-256";
case WIFI_CIPHER_TYPE_UNKNOWN: return "未知";
default: return "未知(?)";
}
}
/* ======================================================================
* 公开 API
* ====================================================================== */
/**
* @brief 将认证模式枚举转为简短可读字符串
*
* 供 scan 打印列表和 station 显示连接信息共同使用。
*/
const char *authmode_to_short_str(wifi_auth_mode_t mode)
{
switch (mode) {
case WIFI_AUTH_OPEN: return "Open";
case WIFI_AUTH_WEP: return "WEP";
case WIFI_AUTH_WPA_PSK: return "WPA";
case WIFI_AUTH_WPA2_PSK: return "WPA2";
case WIFI_AUTH_WPA_WPA2_PSK: return "WPA/WPA2";
case WIFI_AUTH_WPA3_PSK: return "WPA3";
case WIFI_AUTH_WPA2_WPA3_PSK: return "WPA2/WPA3";
case WIFI_AUTH_WAPI_PSK: return "WAPI";
default: return "Other";
}
}
/* ======================================================================
* 事件处理回调
* ====================================================================== */
/**
* @brief WiFi 及 IP 事件处理回调
*
* 处理的事件:
* - WIFI_EVENT_SCAN_DONE : 扫描完成,打印 AP 列表
* - WIFI_EVENT_STA_START : STA 启动
* - WIFI_EVENT_STA_CONNECTED : 成功关联到 AP
* - WIFI_EVENT_STA_DISCONNECTED: 与 AP 断开,按条件自动重连
* - IP_EVENT_STA_GOT_IP : 获取到 IP 地址,连接完成
*
* @note 扫描结果存储在 wifi_event.ap_records 中。
*/
void wifi_event_handler(void *event_handler_arg,
esp_event_base_t event_base,
int32_t event_id,
void *event_data)
{
/* ==================== WiFi 事件 ==================== */
if (event_base == WIFI_EVENT) {
switch (event_id) {
/* ---- 扫描完成 ---- */
case WIFI_EVENT_SCAN_DONE: {
uint16_t ap_num = 0;
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_num));
ESP_LOGI(TAG, "扫描完成,共发现 %" PRIu16 " 个 WiFi 网络", ap_num);
/* 边界保护,防止超出缓冲区 */
if (ap_num > WIFI_MAX_AP_NUM) {
ap_num = WIFI_MAX_AP_NUM;
}
/* 读取 AP 记录到缓冲区 */
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num,
wifi_event.ap_records));
wifi_event.ap_count = ap_num;
/* 打印全部 AP 详情 */
printf("\r\n");
printf("========== WiFi 扫描结果(共 %d 个) ==========\r\n",
wifi_event.ap_count);
for (int i = 0; i < wifi_event.ap_count; i++) {
const wifi_ap_record_t *ap = &wifi_event.ap_records[i];
/* 频段字符串(ESP32-C6 仅 2.4 GHz) */
const char *band_str = "2.4 GHz";
if (ap->phy_11ax) {
band_str = "2.4 GHz (Wi-Fi 6)";
} else if (ap->phy_11n) {
band_str = "2.4 GHz (802.11n)";
} else if (ap->phy_11g) {
band_str = "2.4 GHz (802.11g)";
} else if (ap->phy_11b) {
band_str = "2.4 GHz (802.11b)";
}
/* 带宽字符串 */
const char *bw_str = "未知";
switch (ap->bandwidth) {
case 1: bw_str = "20 MHz"; break;
case 2: bw_str = "40 MHz"; break;
case 3: bw_str = "80 MHz"; break;
case 4: bw_str = "160 MHz"; break;
default: break;
}
/* 中心频率(2.4 GHz: 信道 1~14) */
uint16_t center_freq = 0;
if (ap->primary >= 1 && ap->primary <= 14) {
center_freq = 2407 + ap->primary * 5;
}
/* 第一行:编号 MAC RSSI 信道 SSID */
printf("\r\n");
printf("[%2d] MAC:%02x:%02x:%02x:%02x:%02x:%02x"
" \tRSSI:%ddBm \tCh:%d \tSSID:%s\r\n",
i + 1,
ap->bssid[0], ap->bssid[1], ap->bssid[2],
ap->bssid[3], ap->bssid[4], ap->bssid[5],
ap->rssi, ap->primary, (char *)ap->ssid);
/* 第二行:安全 配对密码 频段 带宽 中心频率 WPS FTM */
printf(" Authmode:%s \tCipher:%s \tBand:%s "
"\tBW:%s \tFreq:%dMHz \tWPS:%s \tFTM:%s\r\n",
authmode_to_short_str(ap->authmode),
cipher_type_to_str(ap->pairwise_cipher),
band_str, bw_str, center_freq,
ap->wps ? "支持" : "不支持",
ap->ftm_responder ? "支持" : "不支持");
}
printf("================================================\r\n");
printf("提示: connect <编号> <密码> 连接网络 "
"(开放网络: connect <编号> \"\")\r\n\r\n");
break;
}
/* ---- STA 启动 ---- */
case WIFI_EVENT_STA_START:
ESP_LOGI(TAG, "WiFi STA 模式已启动");
break;
/* ---- 成功关联到 AP ---- */
case WIFI_EVENT_STA_CONNECTED: {
wifi_event_sta_connected_t *event =
(wifi_event_sta_connected_t *)event_data;
wifi_event.is_connected = true;
wifi_event.retry_count = 0;
wifi_event.retry_enabled = true;
ESP_LOGI(TAG, "已连接到 AP: %s (信道 %d)",
event->ssid, event->channel);
break;
}
/* ---- 与 AP 断开连接 ---- */
case WIFI_EVENT_STA_DISCONNECTED: {
wifi_event_sta_disconnected_t *event =(wifi_event_sta_disconnected_t *)event_data;
wifi_event.is_connected = false;
/* 认证/密码错误 — 不重试 */
if (event->reason == WIFI_REASON_AUTH_FAIL ||
event->reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT ||
event->reason == WIFI_REASON_AUTH_EXPIRE) {
wifi_event.retry_enabled = false;
printf("\r\n错误: 密码错误或认证失败,请检查后重试\r\n");
break;
}
else if (wifi_event.retry_enabled == false) {
break;
}
/* 意外断开 — 尝试自动重连 */
if (wifi_event.retry_count < wifi_event.max_retry) {
wifi_event.retry_count++;
ESP_LOGW(TAG, "意外断开 (原因码: %d),%d 毫秒后第 %d/%d 次重连...",
event->reason,
wifi_event.retry_delay_ms,
wifi_event.retry_count,
wifi_event.max_retry);
vTaskDelay(pdMS_TO_TICKS(wifi_event.retry_delay_ms));
esp_wifi_connect();
} else {
wifi_event.retry_enabled = false;
ESP_LOGE(TAG, "已达最大重连次数 (%d),停止重连",
wifi_event.max_retry);
printf("\r\n错误: 连接失败,已达最大重试次数 (%d),"
"请检查网络后重试\r\n",
wifi_event.max_retry);
}
break;
}
default:
break;
}
}
/* ==================== IP 事件 ==================== */
else if (event_base == IP_EVENT) {
if (event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, "WiFi 连接成功!已获取 IP 地址");
ESP_LOGI(TAG, " IP 地址 : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, " 子网掩码: " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, " 网关 : " IPSTR, IP2STR(&event->ip_info.gw));
ESP_LOGI(TAG, "========================================");
}
}
}
scan.h
#ifndef SCAN_H
#define SCAN_H
#include <stdint.h>
#include "esp_wifi.h"
/**
* @brief 初始化 WiFi 子系统并触发首次全信道扫描
*
* 依次完成 NVS、TCP/IP 协议栈、事件循环、WiFi 驱动的初始化,
* 将 WiFi 设为 STA 模式并发起主动扫描。同时注册 "scan" 控制台命令。
*
* @note 必须在 wifi_station_init() 之前调用。
* @note 任一步骤失败将通过 ESP_ERROR_CHECK 直接 abort。
* @note 本函数不创建默认 STA 网络接口,如有 DHCP 需求需额外调用
* esp_netif_create_default_wifi_sta()。
*/
void wifi_scan_init(void);
#endif /* SCAN_H */
wifi_event.h
#ifndef WIFI_EVENT_H
#define WIFI_EVENT_H
/* ---------- C 标准库 ---------- */
#include <stdbool.h> /* bool */
#include <stdint.h> /* uint16_t */
#include <string.h> /* 字符串处理 */
/* ---------- ESP-IDF ---------- */
#include "esp_console.h" /* 控制台框架 */
#include "esp_event.h" /* 事件循环 */
#include "esp_log.h" /* 日志系统 */
#include "esp_netif.h" /* 网络接口(IP 地址获取) */
#include "esp_partition.h" /* 分区表查询 */
#include "esp_wifi.h" /* WiFi API */
#include "linenoise/linenoise.h" /* 行编辑库 */
#include "nvs_flash.h" /* 非易失性存储(NVS) */
/* ---------- FreeRTOS ---------- */
#include "FreeRTOS/FreeRTOS.h" /* 内核 */
#include "FreeRTOS/task.h" /* 任务管理 */
#include "FreeRTOS/semphr.h" /* 信号量管理 */
/* ======================================================================
* 宏定义
* ====================================================================== */
/** 单次扫描最多缓存的 AP 数量(编译时常量,用于数组定长) */
#define WIFI_MAX_AP_NUM 100
/* ======================================================================
* WiFi 事件状态结构体
* ====================================================================== */
/**
* @brief WiFi 事件模块全部运行时状态
*
* 其他模块通过全局实例 wifi_event 直接访问成员,不使用 accessor 函数。
*/
struct wifi_event_flags {
/* ---- 扫描结果 ---- */
uint16_t ap_count; /**< 实际缓存的 AP 数量 */
wifi_ap_record_t ap_records[WIFI_MAX_AP_NUM]; /**< AP 记录缓冲区 */
/* ---- 连接状态 ---- */
bool is_connected; /**< 当前是否已连接到 AP */
bool retry_enabled; /**< 重连机制开关 */
int retry_count; /**< 当前重连尝试计数 */
int max_retry; /**< 最大重连次数(默认 3) */
int retry_delay_ms; /**< 重连间隔毫秒(默认 3000) */
char last_ssid[33]; /**< 最近连接的 SSID */
char last_password[65]; /**< 最近连接的密码 */
};
/** WiFi 事件模块全局实例(定义在 wifi_event.c) */
extern struct wifi_event_flags wifi_event;
/* ======================================================================
* 函数声明
* ====================================================================== */
/**
* @brief WiFi 及 IP 事件处理回调
*
* 统一处理 WiFi 事件(扫描完成、STA 启动/连接/断开)和 IP 事件(获取 IP 地址)。
* 内部通过 esp_event_handler_instance_register() 注册,不由用户直接调用。
*/
void wifi_event_handler(void *event_handler_arg,
esp_event_base_t event_base,
int32_t event_id,
void *event_data);
#endif /* WIFI_EVENT_H */
2.2 代码逻辑讲解
首先我们需要初始化NVS,因为ESP-IDF 的 WiFi 子系统在底层会把 校准数据(PHY/RF calibration data) 持久化到 NVS 分区中。这些数据包括:
- 射频校准参数 — 芯片在不同温度、电压下的 TX 功率、IQ 偏移等校准值
- WiFi 配置信息 — 通过
esp_wifi_set_config()保存的 SSID、密码等(虽然本项目只做扫描,不涉及连接) - 信道校准缓存 — 各信道的扫描校准结果,避免每次开机都重新校准
跳过 nvs_flash_init() 直接调用 esp_wifi_init(),会导致esp_wifi_init() 返回错误,因为底层尝试读写 NVS 时会失败。
擦除整个 NVS 分区再重新初始化是没关系的,WiFi 校准数据会在下次启动时自动重新生成并写入。
接着依次执行esp_netif_init、esp_event_loop_create_default、esp_wifi_init。顺序不要乱,到这一步,wifi初始化完成了,接下来需要注册 WiFi 事件回调,用来处理各种WiFi事件。
这里使用默认的注册回调函数esp_event_handler_instance_register,WIFI_EVENT是官方WIFI事件声明,直接调用。ID就全选ESP_EVENT_ANY_ID。
接着依次使用esp_wifi_set_mode、esp_wifi_start和esp_wifi_scan_start开启wifi扫描。
对WIFI_EVENT这是总共进行了以下四个判断
- WIFI_EVENT_SCAN_DONE : 扫描完成,打印 AP 列表
- WIFI_EVENT_STA_START : STA 模式已启动
- WIFI_EVENT_STA_CONNECTED : 成功关联到 AP
- WIFI_EVENT_STA_DISCONNECTED : 与 AP 断开,按条件自动重连
其中只有WIFI_EVENT_SCAN_DONE是本次工程需要的,其他判断可以忽略,是为了后续实验准备的。
一旦触发了WIFI_EVENT_SCAN_DONE,便会将wifi信号的多种信息打印出来和记录在结构体wifi_event_flags中。
三、输出展示
配对密码(Pairwise Cipher)
单播加密 — AP 与单个客户端之间通信使用的加密算法。
当你的设备连接到 WiFi 后,你和 AP 之间的数据包就是用这个算法加密的,每个客户端拥有一把独立的密钥。
组密码(Group Cipher)
广播/组播加密 — AP 向所有已连接客户端广播数据时使用的加密算法。
AP 有时需要向所有客户端发送同样的信息(如 ARP 广播),这时用的就是组密码。所有连接到同一个 AP 的设备共享同一把组密钥。取值与配对密码相同(同一个枚举)。
为什么需要两种?
- 单播密钥每人一把 → 更安全,互不窃听
- 组播密钥大家共用 → AP 只需加密一次,所有客户端都能解密,效率更高
中心频率(Center Frequency)
WiFi 信道不是单一频率,而是一段频率范围。中心频率就是这个范围的正中间位置。
ESP32-C6 的 WiFi 射频模块 仅支持 2.4 GHz 频段,不支持 5 GHz。
注意
乐鑫官网明确说明ESP32-C6 的 WiFi 射频模块 仅支持 2.4 GHz 频段,不支持 5 GHz。ESP32-C6 的射频硬件根本无法接收5 GHz频率的信号,所以无论怎么扫都扫不到。
总结
本文基于 ESP32-C6 与 ESP-IDF 6.0.1,系统梳理了 WiFi 基础术语、协议标准、安全机制、信号与组网等概念,并结合组件化工程示例,完整实现并解析了一个可通过控制台命令触发的全信道 WiFi 扫描功能,包含初始化流程、事件回调、AP 信息解析与日志输出等关键细节,为后续拓展更多联网功能提供了可复用的基础框架和实践参考。