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 初始化与连接流程 ESP32 WiFi 初始化与连接流程 NVS 初始化 esp_netif_init + Event Loop 选择 WiFi 模式 Station SoftAP AP+STA 配置 SSID/密码 连接路由器 配置 AP 参数 开启热点 同时配置两者 双模式运行 获取 IP 地址(DHCP/静态) 扫描可在任意阶段调用

这张图把初始化流程、模式分支和最终目标都画清楚了。你跟着箭头走一遍,基本就能理解 ESP32 的 WiFi 启动全貌。

几个实用小技巧

  • WiFi 重连间隔:不要连续调用 esp_wifi_connect(),加个 vTaskDelay(1000 / portTICK_PERIOD_MS) 避免频繁重连。
  • 保存配网信息:用 NVS 存储 SSID 和密码,下次开机直接读取,不用每次都重新配网。
  • 信号强度检测:定期调用 esp_wifi_sta_get_rssi(),如果信号变差可以主动切换网络。

我的经验:在产品里,我会在 WiFi 连接成功后打印 IP 地址到串口,同时通过一个 LED 指示连接状态。这样调试时一眼就能看出设备有没有连上网,省去了反复看日志的麻烦。

好了,WiFi 基础就讲到这里。你动手试一遍代码,把 ESP32 连上你的路由器,看到串口打印出 IP 地址的那一刻,就算真正入门了。