目录
[1.1 基础术语](#1.1 基础术语)
[1.2 标准与协议](#1.2 标准与协议)
[1.3 安全相关](#1.3 安全相关)
[1.4 信号与性能](#1.4 信号与性能)
[1.5 组网与拓扑](#1.5 组网与拓扑)
[1.6 网络功能](#1.6 网络功能)
[1.7 实用场景](#1.7 实用场景)
[2.1 项目结构全览](#2.1 项目结构全览)
[2.2 WIFI 组件深度解析](#2.2 WIFI 组件深度解析)
[2.2.1 模块架构](#2.2.1 模块架构)
[2.2.2 初始化管线](#2.2.2 初始化管线)
[2.2.3 事件处理流程](#2.2.3 事件处理流程)
[2.2.4 关键函数详解](#2.2.4 关键函数详解)
[2.3 系统构建](#2.3 系统构建)
[2.4 WIFI组件代码展览](#2.4 WIFI组件代码展览)
前言
ESP32 作为面向物联网应用的芯片,具备较强的无线联网能力。为便于系统介绍其联网功能,本文单独开设联网篇,首先讲解 Wi‑Fi 扫描的实现方法。
本文使用微雪 ESP32-C6-DEV-KIT-N16 开发板,开发环境为 ESP-IDF 6.0.1。工程模板与前文 ESP32-P4 示例模板基本一致,相关代码可从我的 Gitee 仓库获取。
《https://gitee.com/pai-schoolmate/esp32_project.git》
一、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 项目结构全览

本工程采用 ESP-IDF 组件化架构(模式 A) 。顶层 CMakeLists.txt 通过 EXTRA_COMPONENT_DIRS 引入了 IDF 自带的 console 示例组件(cmd_system、cmd_nvs、cmd_wifi),免去手写系统命令的重复劳动。
2.2 WIFI 组件深度解析
2.2.1 模块架构
文件:components/WIFI/scan.c(300 行)------这是本工程代码量最大、逻辑最密集的模块,完成从 WiFi 子系统初始化到扫描结果解析输出的全部工作。模块架构如下
- 公开 API:
wifi_scan_init()------ 唯一的对外接口,由app_main()调用 - 事件回调:
wifi_event_handler()------ 注册到 ESP-IDF 事件循环,处理WIFI_EVENT_SCAN_DONE - 控制台命令:
scan_cmd_handler()+register_scan_cmd()------ 让用户在 REPL 中手动触发重新扫描 - 工具函数:
cipher_type_to_str()------ 将枚举转为可读字符串 - 静态缓冲区:
ap_records[100]------ 文件作用域,避免每次扫描动态分配内存
2.2.2 初始化管线

⚠ 顺序不可打乱
ESP-IDF WiFi 初始化有严格的前置依赖。例如,若先调用 esp_wifi_init() 再初始化 NVS,驱动会因读不到 RF 校准数据而打印 warning 并使用默认射频参数。事件循环若未创建就注册回调,esp_event_handler_instance_register() 会返回 ESP_ERR_INVALID_STATE。
2.2.3 事件处理流程

事件回调在扫描完成后由事件循环任务调用(非 ISR 上下文),因此可以安全地调用日志输出和内存操作。回调末尾的 esp_wifi_clear_ap_list() 是关键------不调用它,扫描结果会一直占用 WiFi 驱动的内部缓冲区,导致下一次扫描无法获取新结果。
2.2.4 关键函数详解
⚡ 初始化 wifi_scan_init(void)
完成 WiFi 子系统从零到可扫描的全部初始化,并注册控制台命令。是 WIFI 组件唯一的公开 API。
返回值 :无(内部通过 ESP_ERROR_CHECK 断言,失败直接 abort)。
注意 :NVS 初始化包含「损坏恢复」逻辑------若检测到 ESP_ERR_NVS_NO_FREE_PAGES 或 ESP_ERR_NVS_NEW_VERSION_FOUND,会自动擦除 NVS 分区后重试。这意味着首次烧录或 OTA 升级后不会因 NVS 格式变化而卡死。
⚡ 初始化 esp_event_loop_create_default(void)
创建系统默认事件循环。ESP-IDF 的 WiFi、IP、蓝牙等子系统都通过该事件循环分发事件到用户注册的回调。
返回值 :ESP_OK 成功;ESP_ERR_INVALID_STATE 已存在(重复调用安全);ESP_ERR_NO_MEM 内存不足。
注意 :必须在 esp_event_handler_instance_register() 之前调用,否则注册回调会返回 ESP_ERR_INVALID_STATE。默认事件循环会创建一个独立的 FreeRTOS 任务(栈大小由 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE 决定,默认 3584 字节)。
⚡ 初始化 esp_wifi_init(const wifi_init_config_t *config)
初始化 WiFi 驱动程序,分配内部缓冲区、加载射频校准数据、初始化硬件寄存器。
config:指向 wifi_init_config_t 结构体的指针。通常使用 WIFI_INIT_CONFIG_DEFAULT() 宏生成默认配置,包含 TX/RX 缓冲区数量、AMSDU 阈值等参数。
返回值 :ESP_OK 成功;ESP_ERR_NO_MEM 内存不足;ESP_ERR_INVALID_ARG 参数非法。
注意 :依赖 NVS 已初始化(射频校准数据从 NVS 读取)和 esp_netif_init() 已完成。该函数只能调用一次,重复调用返回 ESP_ERR_INVALID_STATE。
🔄 事件注册 esp_event_handler_instance_register(base, id, cb, arg, instance)
向事件循环注册一个事件处理回调。当指定类型的事件被 post 到事件循环时,回调函数将在事件循环任务上下文中被调用。
event_base:事件基类型,如 WIFI_EVENT、IP_EVENT。
event_id:具体事件 ID,ESP_EVENT_ANY_ID 表示订阅该 base 下的所有事件。
event_handler:回调函数指针,签名为 void (*)(void*, esp_event_base_t, int32_t, void*)。
event_handler_arg:传递给回调的自定义参数(透传),本工程传 NULL。
instance:输出参数,返回注册实例句柄,可用于后续注销。本工程传 NULL 表示不保存。
返回值 :ESP_OK 成功;ESP_ERR_INVALID_ARG 参数非法;ESP_ERR_NO_MEM 内存不足。
注意:回调不在 ISR 中执行,而是在事件循环任务中串行执行,因此回调内可以调用日志、内存分配等非实时安全函数。但回调执行时间不宜过长,否则会阻塞后续事件的处理。
🔧 配置 esp_wifi_set_mode(wifi_mode_t mode)
设置 WiFi 工作模式。必须在 esp_wifi_start() 之前调用。
mode:可选值:WIFI_MODE_STA(站点模式,用于扫描/连接 AP)、WIFI_MODE_AP(软 AP 模式)、WIFI_MODE_APSTA(混合模式)、WIFI_MODE_NULL(关闭射频)。
返回值 :ESP_OK 成功;ESP_ERR_INVALID_ARG 模式值非法;ESP_ERR_WIFI_NOT_INIT WiFi 未初始化。
注意 :本工程使用 WIFI_MODE_STA------扫描功能要求 STA 模式。若设为 WIFI_MODE_NULL,射频关闭,扫描将返回错误。模式切换会触发 WIFI_EVENT_STA_STOP / WIFI_EVENT_STA_START 等事件。
🔧 控制 esp_wifi_start(void)
启动 WiFi 硬件,开启射频,并根据当前模式触发对应事件(STA 模式触发 WIFI_EVENT_STA_START)。
返回值 :ESP_OK 成功;ESP_ERR_WIFI_NOT_INIT 未初始化;ESP_ERR_WIFI_IF 内部错误。
注意 :必须在 esp_wifi_set_mode() 之后调用。启动后 WiFi 驱动开始占用 CPU 和内存资源。只有 start 之后才能调用 esp_wifi_scan_start()、esp_wifi_connect() 等操作函数。对应的停止函数为 esp_wifi_stop()。
📦 扫描 esp_wifi_scan_start(const wifi_scan_config_t *config, bool block)
触发一次 WiFi 扫描。扫描完成后产生 WIFI_EVENT_SCAN_DONE 事件,结果通过 esp_wifi_scan_get_ap_records() 获取。
config:扫描配置指针。NULL 表示使用默认配置(全信道、全 SSID、主动扫描)。可指定特定 SSID、信道列表、扫描类型(主动/被动)、各信道停留时间等。
block:true = 阻塞直到扫描完成再返回;false = 立即返回,结果通过事件异步通知。本工程使用 false 以避免阻塞主任务。
返回值 :ESP_OK 成功;ESP_ERR_WIFI_NOT_INIT 未初始化;ESP_ERR_WIFI_NOT_STARTED 未 start;ESP_ERR_WIFI_IF 正在扫描中或内部错误。
注意 :全信道主动扫描在 2.4 GHz 下约耗时 1.5~2 秒(13 个信道 × 120 ms/信道)。扫描期间 STA 无法收发数据。若上一次扫描结果未通过 esp_wifi_clear_ap_list() 清除,可能导致新扫描结果获取异常。
2.3 系统构建
顶层 CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ESP32C6_v6_temaplat)
关键点:EXTRA_COMPONENT_DIRS 将 IDF 示例中的 cmd_system、cmd_nvs、cmd_wifi 组件引入本工程,避免重复实现。
组件依赖关系
| 组件 | REQUIRES |
|---|---|
| main | (隐式依赖 basic、WIFI) |
| WIFI | basic · esp_event · esp_netif · esp_wifi |
| basic | console · cmd_system · cmd_nvs · cmd_wifi · nvs_flash · esp_partition |
2.4 WIFI组件代码展览
CMakeLists.txt
cpp
idf_component_register(SRCS "scan.c"
INCLUDE_DIRS "include"
REQUIRES basic esp_event esp_netif esp_wifi)
scan.c
cpp
/* ==========================================================================
* WiFi 扫描模块 --- 实现文件
*
* 功能:
* 1. 初始化 NVS、TCP/IP 协议栈、事件循环、WiFi 子系统
* 2. 将 WiFi 设为 STA 模式并触发主动扫描
* 3. 在 SCAN_DONE 事件中打印前若干 AP 的详细信息
*
* 依赖:
* - ESP-IDF: nvs_flash, esp_netif, esp_wifi, esp_event, esp_log
* - 本组件: scan.h(函数声明)、basic 组件
* ========================================================================== */
/* ---------- 头文件 ---------- */
#include <stdio.h> /* 标准 I/O */
#include <inttypes.h> /* PRI 格式化宏(如 PRIu16) */
#include "scan.h" /* 本模块接口声明 */
#include "esp_log.h" /* ESP 日志系统 */
#include "nvs_flash.h" /* 非易失性存储(NVS) */
#include "esp_netif.h" /* TCP/IP 网络栈初始化 */
#include "esp_event.h" /* 事件循环框架 */
#include "esp_wifi.h" /* WiFi 驱动 API */
/* ---------- 宏定义 ---------- */
/** 模块日志标签,用于 ESP_LOGx 宏 */
static const char *TAG = "wifi_scan";
/** 单次扫描最多缓存的 AP 数量 */
#define MAX_AP_NUM 100
/**
* 打印 AP 详情的个数上限。
*
* 注意:这里设为 5 仅用于演示,实际扫描到的 AP 可能远超这个数量。
* 如需全部输出,请将 `DISPLAY_AP_COUNT` 改为 `ap_num`。
*/
#define DISPLAY_AP_COUNT 5
/* ---------- 模块级(文件作用域)变量 ---------- */
/** 存储扫描结果的全局缓冲区(文件内 static,外部不可见) */
static wifi_ap_record_t ap_records[MAX_AP_NUM];
/* ======================================================================
* 内部辅助函数
* ====================================================================== */
/**
* @brief 将 wifi_cipher_type_t 枚举转为可读字符串
*
* 覆盖 ESP-IDF 定义的全部 13 种密码类型(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 "未知(?)";
}
}
/* ======================================================================
* 内部函数
* ====================================================================== */
/**
* @brief WiFi 事件处理回调
*
* 当前仅处理 `WIFI_EVENT_SCAN_DONE` 事件:
* - 获取扫描到的 AP 数量
* - 读取前 `DISPLAY_AP_COUNT` 个 AP 记录
* - 逐条打印 AP 的 MAC、RSSI、信道、SSID 等信息
*
* @note 扫描结果存储在文件级静态数组 `ap_records` 中。
*/
static void wifi_event_handler(void *event_handler_arg,
esp_event_base_t event_base,
int32_t event_id,
void *event_data)
{
/* 仅处理扫描完成事件 */
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) {
/* ---- 1. 获取扫描到的 AP 总数 ---- */
uint16_t ap_num = 0;
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_num));
ESP_LOGI(TAG, "扫描完成,共发现 %" PRIu16 " 个 WiFi 网络", ap_num);
/* ---- 2. 边界保护,防止数组越界 ---- */
if (ap_num > MAX_AP_NUM) {
ap_num = MAX_AP_NUM;
}
/* ---- 3. 读取 AP 记录到缓冲区 ---- */
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_records));
/* ---- 4. 打印前 DISPLAY_AP_COUNT 个 AP 的详细信息 ---- */
ESP_LOGI(TAG, "========== 扫描结果(前 %d 个) ==========",
(ap_num < DISPLAY_AP_COUNT) ? ap_num : DISPLAY_AP_COUNT);
for (int i = 0; (i < DISPLAY_AP_COUNT) && (i < ap_num); i++) {
const wifi_ap_record_t *ap = &ap_records[i];
/* 4.1 基本信息:MAC、RSSI、信道、SSID */
ESP_LOGI(TAG,
"[%d] MAC: %02x:%02x:%02x:%02x:%02x:%02x "
"RSSI: %d dBm 信道: %d SSID: %s",
i + 1,
ap->bssid[0], ap->bssid[1], ap->bssid[2],
ap->bssid[3], ap->bssid[4], ap->bssid[5],
ap->rssi,
ap->primary,
ap->ssid);
/* 4.2 安全信息:认证模式、加密类型 */
ESP_LOGI(TAG,
" 认证模式: %s 配对密码: %s 组密码: %s",
ap->authmode == WIFI_AUTH_OPEN ? "Open" :
ap->authmode == WIFI_AUTH_WEP ? "WEP" :
ap->authmode == WIFI_AUTH_WPA_PSK ? "WPA-PSK" :
ap->authmode == WIFI_AUTH_WPA2_PSK ? "WPA2-PSK" :
ap->authmode == WIFI_AUTH_WPA3_PSK ? "WPA3-PSK" : "其他",
cipher_type_to_str(ap->pairwise_cipher),
cipher_type_to_str(ap->group_cipher));
/* 4.3 频段与功能特性
* 优先用 PHY 标志位判断,无法确定时用 primary 信道号兜底
* 802.11b/g → 2.4 GHz(确定) 802.11a/ac → 5 GHz(确定)
* 802.11n/ax → 多频段(需结合其他标志或信道号) */
const char *band_str = "未知";
int has_24g = ap->phy_11b || ap->phy_11g; /* b/g 是 2.4G 独有 */
int has_5g = ap->phy_11a || ap->phy_11ac; /* a/ac 是 5G 独有 */
if (has_24g && has_5g) {
band_str = "2.4 / 5 GHz (双频)";
} else if (has_24g) {
band_str = "2.4 GHz";
} else if (has_5g) {
band_str = "5 GHz";
} else if (ap->phy_11ax) {
/* 仅 11ax 标志:Wi-Fi 6/6E,具体频段依赖信道号 */
if (ap->primary >= 1 && ap->primary <= 14) {
band_str = "2.4 GHz (Wi-Fi 6)";
} else if (ap->primary >= 36 && ap->primary <= 177) {
band_str = "5 GHz (Wi-Fi 6)";
} else {
band_str = "6 GHz (Wi-Fi 6E)";
}
} else if (ap->phy_11n) {
/* 仅 11n 标志:大概率 2.4G,用信道号确认 */
if (ap->primary >= 36 && ap->primary <= 177) {
band_str = "5 GHz (802.11n)";
} else {
band_str = "2.4 GHz (802.11n)";
}
} else {
/* PHY 标志均无,完全依赖信道号兜底 */
if (ap->primary >= 1 && ap->primary <= 14) {
band_str = "2.4 GHz";
} else if (ap->primary >= 36 && ap->primary <= 177) {
band_str = "5 GHz";
}
}
ESP_LOGI(TAG,
" 频段: %s WPS: %s FTM: %s",
band_str,
ap->wps ? "支持" : "不支持",
ap->ftm_responder ? "支持" : "不支持");
/* 4.4 带宽与中心频率 */
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: bw_str = "未知"; break;
}
/* 从主信道号计算中心频率(MHz) */
uint16_t center_freq = 0;
if (ap->primary >= 1 && ap->primary <= 14) {
center_freq = 2407 + ap->primary * 5; /* 2.4 GHz */
} else if (ap->primary >= 36 && ap->primary <= 177) {
center_freq = 5000 + ap->primary * 5; /* 5 GHz */
}
/* 4.5 GHz VHT 宽信道:使用 AP 上报的 VHT 频率 */
if (ap->bandwidth >= 2 && ap->vht_ch_freq1 != 0) {
center_freq = ap->vht_ch_freq1;
}
ESP_LOGI(TAG,
" 带宽: %s 中心频率: %d MHz",
bw_str,
center_freq);
}
ESP_LOGI(TAG, "==========================================");
esp_wifi_clear_ap_list(); /* 清空扫描结果,释放内存 */
}
}
/* ======================================================================
* 控制台命令
* ====================================================================== */
/** scan 命令的回调,转发到 wifi_scan_trigger() */
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;
}
/** 注册 scan 控制台命令 */
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)
* 3. 创建默认事件循环
* 4. 初始化 WiFi 子系统(默认配置)
* 5. 注册 WIFI_EVENT 回调
* 6. 设置 WiFi 为 STA 模式并启动
* 7. 发起主动全信道扫描(阻塞时间极短,结果通过事件异步返回)
*/
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 事件回调 ---- */
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 网络...");
register_scan_cmd(); // 注册scan控制台命令
}
scan.h
cpp
#ifndef SCAN_H
#define SCAN_H
#include <string.h> /* C 标准库:字符串处理 */
#include "esp_console.h" /* ESP-IDF:控制台框架 */
#include "linenoise/linenoise.h" /* ESP-IDF:行编辑库 */
#include "nvs_flash.h" /* ESP-IDF:非易失性存储(NVS) */
#include "esp_partition.h" /* ESP-IDF:分区表查询 */
#include "esp_log.h" /* ESP-IDF:日志系统 */
#include "FreeRTOS/FreeRTOS.h" /* FreeRTOS:内核 */
#include "FreeRTOS/task.h" /* FreeRTOS:任务管理 */
#include "FreeRTOS/semphr.h" /* FreeRTOS:信号量管理 */
/**
* @brief 初始化 WiFi 扫描功能
*
* 初始化 WiFi 扫描相关的数据结构和资源,并执行首次扫描。
*/
void wifi_scan_init(void);
#endif /* SCAN_H */
三、输出展示

配对密码(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 信息解析与日志输出等关键细节,为后续拓展更多联网功能提供了可复用的基础框架和实践参考。