WiFi 基础与连接:ESP32 的 WiFi 模式、连接路由器、扫描与 IP 获取
做嵌入式开发,尤其是 IoT 项目,WiFi 几乎是绕不开的坎。ESP32 之所以受欢迎,很大程度是因为它把 WiFi 和蓝牙都集成在一块芯片上,省去了外挂模块的麻烦。
今天我们就来把 ESP32 的 WiFi 基础啃透。说白了,就是搞清楚三件事:ESP32 能当什么角色?怎么连上路由器?连上之后怎么拿到 IP?
ESP32 的三种 WiFi 模式
ESP32 的 WiFi 模块可以工作在三种模式下。我刚开始接触时也混淆过,后来发现其实很好理解——你把它想象成一个人际关系模型就行。
| 模式 | 枚举值 | 角色比喻 | 典型场景 |
|---|---|---|---|
| Station 模式 | WIFI_MODE_STA | 客户端/学生 | 连接家庭路由器上网 |
| SoftAP 模式 | WIFI_MODE_AP | 热点/老师 | 手机直连 ESP32 配置 |
| Station + SoftAP 模式 | WIFI_MODE_APSTA | 既是学生又是老师 | 中继器、配网桥接 |
我个人习惯把 Station 模式叫做「上网模式」,SoftAP 叫做「广播模式」。项目里最常用的是 Station 模式——让 ESP32 连上你家的路由器,然后跟服务器通信。
核心要点:初始化 WiFi 之前,必须先调用 esp_netif_init() 和 event_loop_create()。这是 ESP-IDF 的底层依赖,少了它们 WiFi 根本跑不起来。
连接路由器:Station 模式实战
好,我们直接上手写代码。下面这段是我项目里常用的模板,去掉了一些无关的日志打印,保留了核心逻辑。
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#define WIFI_SSID "Your_SSID"
#define WIFI_PASS "Your_Password"
static void wifi_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) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect(); // 自动重连
} 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("WIFI", "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
}
}
void wifi_init_sta(void)
{
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&wifi_event_handler, NULL, NULL);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&wifi_event_handler, NULL, NULL);
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
},
};
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
esp_wifi_start();
}
这段代码里有个细节——WIFI_EVENT_STA_DISCONNECTED 事件触发后我直接调了 esp_wifi_connect()。为什么?因为 WiFi 信号不稳定时,ESP32 可能会掉线。我曾经在一个工厂项目里遇到过,设备放在金属机箱里,WiFi 频繁断开,不加自动重连的话设备就「死」了。
我的习惯:重连次数不要无限循环。我会加一个计数器,重连 5 次失败后进入低功耗休眠,等 30 秒再试。这样既省电又避免死循环。
WiFi 扫描:看看周围有哪些热点
有时候你不知道 WiFi 的 SSID,或者想让用户选择网络。这时候就需要扫描功能。
ESP32 的扫描 API 很简单,但有几个坑。嗯,这里要注意:扫描是阻塞的,默认会花 2-3 秒。如果你在任务里直接调,整个任务都会被卡住。
void wifi_scan(void)
{
uint16_t ap_count = 0;
esp_wifi_scan_start(NULL, true); // true = 阻塞等待
esp_wifi_scan_get_ap_num(&ap_count);
wifi_ap_record_t *ap_records = malloc(sizeof(wifi_ap_record_t) * ap_count);
esp_wifi_scan_get_ap_records(&ap_count, ap_records);
for (int i = 0; i < ap_count; i++) {
printf("SSID: %s, RSSI: %d, Channel: %d\n",
ap_records[i].ssid,
ap_records[i].rssi,
ap_records[i].primary);
}
free(ap_records);
}
扫描结果里,rssi 是信号强度,单位 dBm。数值越大(越接近 0)信号越好。我一般用这个值做信号质量判断:
| RSSI 范围 | 信号质量 | 建议 |
|---|---|---|
| -30 ~ -50 dBm | 优秀 | 稳定连接 |
| -50 ~ -70 dBm | 良好 | 可用,偶尔重连 |
| -70 ~ -85 dBm | 一般 | 可能掉线,建议优化位置 |
| < -85 dBm | 差 | 基本不可用 |
避坑指南:扫描时如果 WiFi 已经连接,扫描会短暂断开当前连接。我曾经因为这个导致设备在扫描期间丢失了服务器的心跳包。解决方案:扫描前检查连接状态,或者使用非阻塞扫描(false 参数),扫描完成后通过事件回调处理。
获取 IP 地址:DHCP 与静态 IP
连接成功后,ESP32 默认通过 DHCP 获取 IP。你不需要手动配置,路由器会自动分配。但有些场景——比如你想让设备 IP 固定,方便远程访问——就需要设置静态 IP。
我个人更倾向于用 DHCP + 路由器 MAC 绑定。这样既保留了 DHCP 的灵活性,又保证了 IP 固定。但如果你非要代码里写死,也可以:
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
esp_netif_dhcpc_stop(sta_netif); // 停止 DHCP 客户端
esp_netif_ip_info_t ip_info;
IP4_ADDR(&ip_info.ip, 192, 168, 1, 100);
IP4_ADDR(&ip_info.gw, 192, 168, 1, 1);
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
esp_netif_set_ip_info(sta_netif, &ip_info);
注意顺序:先停止 DHCP,再设置 IP。我曾经反过来写过,结果 IP 设置完又被 DHCP 覆盖了,排查了半天才发现是顺序问题。
知识体系总览
下面这张图把本章的核心逻辑串起来了。你可以把它当作 WiFi 初始化的「路线图」。
这张图把初始化流程、模式分支和最终目标都画清楚了。你跟着箭头走一遍,基本就能理解 ESP32 的 WiFi 启动全貌。
几个实用小技巧
- WiFi 重连间隔:不要连续调用
esp_wifi_connect(),加个vTaskDelay(1000 / portTICK_PERIOD_MS)避免频繁重连。 - 保存配网信息:用 NVS 存储 SSID 和密码,下次开机直接读取,不用每次都重新配网。
- 信号强度检测:定期调用
esp_wifi_sta_get_rssi(),如果信号变差可以主动切换网络。
我的经验:在产品里,我会在 WiFi 连接成功后打印 IP 地址到串口,同时通过一个 LED 指示连接状态。这样调试时一眼就能看出设备有没有连上网,省去了反复看日志的麻烦。
好了,WiFi 基础就讲到这里。你动手试一遍代码,把 ESP32 连上你的路由器,看到串口打印出 IP 地址的那一刻,就算真正入门了。