arduino-esp32 WiFiScanTime 示例精讲:用自定义驻留时间精确控制 ESP32 的 Wi-Fi 扫描行为
2026/9/14 19:57:54 网站建设 项目流程

arduino-esp32 WiFiScanTime 示例精讲:用自定义驻留时间精确控制 ESP32 的 Wi-Fi 扫描行为

【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32

本篇围绕libraries/WiFi/examples/WiFiScanTime示例展开,讲解如何为 ESP32 系列芯片的 Wi-Fi 扫描设置"每信道最小/最大驻留时间",从而在扫描速度与网络发现率之间取得平衡。读完后你将掌握WiFi.scanNetworks()的完整参数语义、setScanActiveMinTime()的作用机制,以及扫描结果(SSID、RSSI、信道、加密类型)的读取与内存释放方法,并能从源码层面理解 Arduino 封装之下 ESP-IDF 扫描流程的真实实现。

示例定位与适用目标

WiFiScanTimelibraries/WiFi库内置的一组 Wi-Fi 扫描示例之一,用于演示使用自定义扫描时序(custom scan timing)扫描可用 Wi-Fi 网络并打印结果。同目录下还有几个相关示例可作对照:WiFiScan(基础同步扫描)、WiFiScanAsync(异步扫描)、WiFiScanDualAntenna(双天线扫描),本示例的独特点在于显式控制每个信道的驻留时间。

根据 README,该示例支持的目标芯片如下:

支持的目标ESP32ESP32-S2ESP32-C3ESP32-S3ESP32-C6

从源码结构看,这一限制与构建配置直接对应。示例的 ci.yml 声明了requires_any条件:CONFIG_SOC_WIFI_SUPPORTED=yCONFIG_ESP_HOSTED_ENABLED=y,即只有 SoC 原生支持 Wi-Fi、或启用了 ESP Hosted(通过另一颗协处理芯片提供 Wi-Fi)的构建才会编译运行该示例。库源码 WiFiScan.h 中同样以#if SOC_WIFI_SUPPORTED || CONFIG_ESP_HOSTED_ENABLED包裹整个类定义,二者是一致的。

核心概念:扫描驻留时间(min / max)语义

示例文件 WiFiScanTime.ino 开头的注释给出了这套 API 最关键的行为约定——活跃扫描(active scan)下min_timemax_time四种组合的语义:

min 取值max 取值行为
min=0, max=0每个信道驻留固定的 120 ms(底层默认值)
min>0, max=0每个信道驻留固定的 120 ms
min=0, max>0每个信道驻留maxms
min>0, max>0每个信道至少驻留minms:若这段时间内未发现任何 AP,立即跳到下一信道;若发现了 AP,则驻留延长至maxms 以收集更多结果

这套规则正是 ESP-IDFwifi_scan_config_t.scan_time.active.min/max字段的语义。它解决的是一个实际工程问题:全信道扫描 13 个(2.4 GHz 频段)信道时,若每个信道都驻留较长时间,扫描总时长会成倍增加,阻塞主循环;而驻留时间太短又会漏掉响应慢、信号弱的 AP。min保证"没有收获就快速跳过",max保证"有收获就多收集"。

示例代码逐段解析

示例的完整逻辑可以拆为三步:设置每信道最小驻留时间、发起带最大驻留时间的同步扫描、格式化打印并释放结果。

1. 准备射频:进入 Station 模式

// Set WiFi to station mode and disconnect from an AP if it was previously connected. WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100);

扫描前必须让 Wi-Fi 处于 Station 模式并断开已有连接。从 WiFiScan.cpp 的实现看,scanNetworks()内部会调用WiFi.enableSTA(true),即底层已保证 STA 射频使能,但显式调用WiFi.mode(WIFI_STA)+WiFi.disconnect()可以确保从 SoftAP 或 STA+AP 模式干净地切出,避免 AP 广播与扫描相互干扰。

2. 设置最小驻留时间并发起扫描

void wifiScan(uint16_t min_time, uint16_t max_time) { Serial.println("Scan start"); // Set the minimum time per channel for active scanning. WiFi.setScanActiveMinTime(min_time); // Capture the start time of the scan. uint32_t start = millis(); // WiFi.scanNetworks will return the number of networks found. // Scan networks with those options: Synchrone mode, show hidden networks, // active scan, max scan time per channel. int n = WiFi.scanNetworks(false, true, false, max_time); Serial.printf("Scan done, elapsed time: %" PRIu32 " ms\n", millis() - start); ... }

注意minmax走的是两条不同的 API 通道,这是本示例最容易忽视的细节:

  • min通过WiFi.setScanActiveMinTime(min_time)单独设置,写入 WiFiScan.cpp 中的静态成员_scanActiveMinTime(默认值为 100 ms,见 第 49 行),在后续每次scanNetworks()构建配置时生效;
  • max则作为scanNetworks()的第 4 个位置参数max_ms_per_chan直接传入。

scanNetworks()的完整原型(见 WiFiScan.h)及默认值如下:

int16_t scanNetworks( bool async = false, // 是否异步扫描,true 时立即返回 bool show_hidden = false, // 是否包含隐藏 SSID 的网络 bool passive = false, // true=被动扫描(只监听 beacons),false=主动扫描(发送 probe) uint32_t max_ms_per_chan = 300, // 每信道最大驻留时间(被动模式下为监听时长) uint8_t channel = 0, // 0 表示扫描全部信道,非 0 只扫指定信道 const char *ssid = nullptr, // 非空则只匹配指定 SSID const uint8_t *bssid = nullptr // 非空则只匹配指定 AP 的 MAC );

示例调用WiFi.scanNetworks(false, true, false, max_time)的含义是:同步模式、显示隐藏网络、主动扫描、每信道最大驻留max_timems。返回值语义为:成功时返回发现的 AP 数量;异步模式下扫描进行中返回WIFI_SCAN_RUNNING;扫描超时或底层启动失败返回WIFI_SCAN_FAILED(详见 WiFiScan.cpp 第 67–107 行)。

3. 格式化打印扫描结果

示例按固定列宽打印序号、SSID(最长 32 字符截断)、RSSI、信道与加密类型:

for (int i = 0; i < n; ++i) { Serial.printf("%2d", i + 1); Serial.print(" | "); Serial.printf("%-32.32s", WiFi.SSID(i).c_str()); Serial.print(" | "); Serial.printf("%4" PRIi32, WiFi.RSSI(i)); Serial.print(" | "); Serial.printf("%2" PRIi32, WiFi.channel(i)); Serial.print(" | "); switch (WiFi.encryptionType(i)) { case WIFI_AUTH_OPEN: Serial.print("open"); break; case WIFI_AUTH_WEP: Serial.print("WEP"); break; case WIFI_AUTH_WPA_PSK: Serial.print("WPA"); break; case WIFI_AUTH_WPA2_PSK: Serial.print("WPA2"); break; case WIFI_AUTH_WPA_WPA2_PSK: Serial.print("WPA+WPA2"); break; case WIFI_AUTH_WPA2_ENTERPRISE: Serial.print("WPA2-EAP"); break; case WIFI_AUTH_WPA3_PSK: Serial.print("WPA3"); break; case WIFI_AUTH_WPA2_WPA3_PSK: Serial.print("WPA2+WPA3"); break; case WIFI_AUTH_WAPI_PSK: Serial.print("WAPI"); break; default: Serial.print("unknown"); } Serial.println(); delay(10); }

这些取值函数最终都落在 WiFiScan.cpp 的SSID()RSSI()channel()encryptionType()等方法上,它们按索引取出wifi_ap_record_t记录并返回对应字段(ssidrssiprimaryauthmode)。示例的注释也特别提醒:encryptionType()的返回值与老版 Arduino WiFi Shield 库不同,因为新增了 WPA3、WPA2+WPA3 等现代加密模式的枚举值。除SSID(i)外,库还提供BSSID(i)/BSSIDstr(i)获取 AP 的 MAC 地址(WiFiScan.cpp 第 255–284 行),以及一次性取出全部字段的getNetworkInfo()(第 197–208 行)。

4. 释放结果并循环演示

// Delete the scan result to free memory for code below. WiFi.scanDelete(); // Wait a bit before scanning again delay(2000);

WiFi.scanDelete()会释放上一次扫描结果的堆内存(_scanResult缓冲)、清零_scanCount并清除扫描状态位(WiFiScan.cpp 第 177–185 行)。在 RAM 有限的设备上,读完结果后及时调用它是良好习惯。

5. setup() 中的三组对照扫描

void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100); // 每信道最小 100 ms / 最大 300 ms(默认值组合) wifiScan(100, 300); // 最小 100 ms / 最大 1500 ms:有 AP 时驻留更久,发现更全,耗时更长 wifiScan(100, 1500); // 最小 0 ms / 最大 1500 ms:无 AP 时快速跳信道,有 AP 时最长驻留 1500 ms wifiScan(0, 1500); }

loop()为空,示例只在setup()中完成三轮扫描,正好覆盖"默认组合、拉大 max、min 归零"三种典型配置,方便对比不同参数下打印的elapsed time与网络数量的差异。

底层实现:Arduino 封装到 ESP-IDF 的调用链

从源码结构看,scanNetworks()(WiFiScan.cpp 第 67–107 行)的完整流程是:

  1. 防重入:若状态位WIFI_SCANNING_BIT已置位(说明已有扫描在进行),直接返回WIFI_SCAN_RUNNING
  2. 清理旧结果:调用scanDelete()释放上一次结果;
  3. 构建wifi_scan_config_t:填入ssidbssidchannelshow_hidden;按passive标志选择WIFI_SCAN_TYPE_PASSIVE(此时max_ms_per_chan写入scan_time.passive)或WIFI_SCAN_TYPE_ACTIVEmin取静态成员_scanActiveMinTimemax取参数max_ms_per_chan)——这正是上文 min/max 四种组合语义的落点
  4. 启动扫描:调用 ESP-IDF 的esp_wifi_scan_start(&config, false),同步阻塞模式;成功后记录_scanStarted = millis(),并等待WIFI_SCAN_DONE_BIT状态位,最长等待_scanTimeout(默认 60000 ms,见 第 47 行,可用WiFi.setScanTimeout()修改);
  5. 结果回调:扫描完成事件触发_scanDone()(第 115–137 行),它通过esp_wifi_scan_get_ap_num()取得 AP 数量,再calloc分配wifi_ap_record_t数组并用esp_wifi_scan_get_ap_records()填充,最后置位WIFI_SCAN_DONE_BIT唤醒等待方。

如果改用异步模式(scanNetworks(true, ...)),函数会立即返回WIFI_SCAN_RUNNING,此时需要轮询WiFi.scanComplete():未完成返回WIFI_SCAN_RUNNING,完成返回网络数量,超过_scanTimeout超时则清位并返回WIFI_SCAN_FAILED(第 157–172 行)。这正是同目录下WiFiScanAsync示例采用的模式,适合需要扫描期间保持 CPU 响应性的场景。

期望的串口输出

按 README 给出的样例,烧录后串口(115200 波特率,示例中Serial.begin(115200))输出形如:

Setup done Scan start Scan done, elapsed time: 4960 ms 17 networks found Nr | SSID | RSSI | CH | Encryption 1 | IoTNetwork | -62 | 1 | WPA2 2 | WiFiSSID | -62 | 1 | WPA2-EAP 3 | B3A7992 | -63 | 6 | WPA+WPA2 4 | WiFi | -63 | 6 | WPA3 5 | IoTNetwork2 | -64 | 11 | WPA2+WPA3 ...

其中Scan done, elapsed time一行即示例中millis() - start的实测值:它随max参数增大、以及环境中 AP 密度变化而变化,是判断参数调优效果的直接依据。

使用方法与排错

README 中给出的操作步骤:在 Arduino IDE 中编译/验证前,通过Tools -> Board选择正确的板卡,再通过Tools -> Port: xxx选择已识别的 COM 口,然后编译烧录libraries/WiFi/examples/WiFiScanTime/WiFiScanTime.ino即可。

README 的 Troubleshooting 一节同样值得保留在实操清单里:

  • 首要前提:使用质量可靠的 USB 数据线,并确保供电充足;
  • 编程失败(Programming Fail):尝试降低串口连接速度;
  • COM 口未被识别:检查 USB 线连接与 USB 转串口驱动安装情况。

结合源码还可以补充一点排错视角:若扫描"完成"但返回 0 个网络,可检查是否误用了passive=true且驻留时间过短(被动扫描依赖 AP 主动发 beacon,响应慢的 AP 容易漏检);若长时间阻塞后返回失败值,则受setScanTimeout()的超时上限(默认 60 s)约束。

小结

WiFiScanTime示例用不到百行代码覆盖了 ESP32 Wi-Fi 扫描时序控制的全部关键点:setScanActiveMinTime()管理每信道最小驻留(静态状态、默认 100 ms),scanNetworks()的第 4 个参数管理最大驻留(默认 300 ms),两者共同决定每个信道"快跳还是久留";结果通过SSID/RSSI/channel/encryptionType/BSSIDstr等索引接口读取,用scanDelete()释放;底层则是wifi_scan_config_tesp_wifi_scan_start()_scanDone()回调这条清晰的 ESP-IDF 调用链。对于需要在"扫描延迟"与"AP 发现完整度"之间做取舍的应用(如自动选网、信号测量、Mesh 选父节点),这组参数是最直接的调优入口。

【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询