3. WiFi Station 模式:从零开始连接网络
这一章我们正式进入 ESP32 的 WiFi 编程。Station 模式,说白了就是让 ESP32 像你的手机一样,去连接一个已有的路由器。这是最基础、最常用的模式。我个人做过的物联网项目里,90% 以上都是用的这个模式。
3.1 初始化 WiFi:让协议栈准备好
写代码之前,得先让 ESP32 的 WiFi 硬件和协议栈「醒过来」。我刚开始学的时候,总忘记初始化,结果回调函数死活不触发。你想想看,硬件都没上电,软件怎么跑?
初始化分两步:
- 初始化 netif——网络接口层,负责 IP 协议栈
- 初始化 WiFi 驱动——让射频硬件进入工作状态
// 第一步:初始化 netif
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
// 第二步:初始化 WiFi 驱动
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
这里有个细节:nvs_flash_init() 是用来读取非易失性存储的。WiFi 的 MAC 地址、校准参数都存在里面。我曾经遇到过一上电就崩溃,查了半天发现是 NVS 分区没烧录。嗯,这个坑你们别踩。
3.2 配置 SSID 和密码:告诉 ESP32 连谁
初始化完了,就该告诉 ESP32 要连哪个路由器了。配置信息放在一个结构体里:
wifi_config_t wifi_config = {
.sta = {
.ssid = "MyHomeWiFi",
.password = "password123",
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
| 参数 | 说明 | 我的建议 |
|---|---|---|
| ssid | 路由器名称,最长 32 字节 | 注意中文字符串编码问题 |
| password | WiFi 密码,WPA2 至少 8 位 | 不要硬编码在正式产品里 |
| threshold.authmode | 最低认证模式 | 设成 WPA2_PSK 兼容性最好 |
3.3 连接事件处理:监听 WiFi 的「心跳」
WiFi 连接不是瞬间完成的。ESP32 会经历扫描、认证、关联、获取 IP 四个阶段。每个阶段都有对应的事件。我习惯用事件处理函数来跟踪状态:
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();
printf("WiFi 开始连接...\n");
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
printf("已连接到 AP,正在获取 IP...\n");
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
printf("连接断开,5 秒后重试...\n");
esp_wifi_connect();
}
}
为什么要监听 WIFI_EVENT_STA_DISCONNECTED?因为 WiFi 随时可能掉线。我做过一个户外温湿度监测项目,设备挂在树上,信号不稳定。如果不做重连逻辑,设备断网后就成「孤儿」了。
3.4 获取 IP 地址:真正「上网」的标志
连接上 AP 不等于能上网。只有拿到 IP 地址,才算真正接入网络。这个事件来自 IP 协议栈,不是 WiFi 驱动:
static void ip_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
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;
printf("获取到 IP:" IPSTR "\n", IP2STR(&event->ip_info.ip));
}
}
这里有个容易忽略的点:IP_EVENT_STA_GOT_IP 只触发一次。如果 DHCP 租约到期续约,不会再次触发。所以不要在事件处理函数里做一次性初始化,除非你确定只执行一次。
获取到的 IP 信息包含三个值:
- ip —— 本机 IP 地址
- netmask —— 子网掩码,通常是 255.255.255.0
- gw —— 网关地址,也就是路由器的 IP
3.5 完整流程:把碎片拼起来
下面这张图是我自己画的工作流程,帮你把上面的步骤串起来:
3.6 完整代码示例
把上面所有代码拼在一起,就是一个可运行的 Station 模式程序:
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#define WIFI_SSID "MyHomeWiFi"
#define WIFI_PASS "password123"
static void wifi_event_handler(void* arg, esp_event_base_t base,
int32_t id, void* data)
{
if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
} else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) data;
ESP_LOGI("wifi", "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
}
}
void wifi_init_sta(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
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,
&wifi_event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
}
3.7 调试技巧:怎么知道连没连上?
代码写完了,怎么验证?我一般用三个方法:
- 看串口日志——打开 ESP32 的串口输出,观察事件日志。看到
GOT_IP就说明成功了。 - ping 测试——在电脑上 ping ESP32 的 IP 地址,能通就说明网络层正常。
- 路由器管理页面——登录路由器,查看已连接设备列表。看到 ESP32 的 MAC 地址就对了。
有一次我在客户现场调试,串口日志显示 GOT_IP,但设备就是不上报数据。后来发现是 DNS 解析有问题。所以拿到 IP 只是第一步,应用层通信还得单独验证。
esp_wifi_set_ps(WIFI_PS_NONE) 关闭省电。我做过一个智能灯控项目,就是因为省电模式导致开关指令延迟,用户体验极差。
好了,这一章的内容就这些。Station 模式是 ESP32 WiFi 开发的基石,后面的 TCP、HTTP、MQTT 都建立在它之上。把初始化、事件处理、IP 获取这三个环节吃透,后面的路就好走了。