4. WiFi SoftAP 模式:初始化 SoftAP、配置热点参数、管理客户端连接、IP 地址分配
好,咱们进入第四章。这一章聊的是 ESP32 的 SoftAP 模式——说白了,就是让你的 ESP32 变成一个 WiFi 热点。手机、电脑都能连上来,就像连你家路由器一样。
我刚开始做物联网项目时,总觉得 SoftAP 就是个“临时方案”。后来发现,很多场景还真离不开它。比如设备配网、本地数据展示、甚至小范围的局域网通信。你想想看,没有路由器的地方,设备之间怎么通信?SoftAP 就是答案。
4.1 SoftAP 模式概述
SoftAP,全称 Software Access Point。ESP32 通过软件模拟出一个 WiFi 热点。它不需要额外的硬件,靠的就是 WiFi 模块的 AP 模式。
和路由器比,ESP32 的 SoftAP 能力有限。但够用。我做过一个项目,在农田里用 ESP32 做数据采集,周围没网没路由器。设备启动后自动开启 SoftAP,手机连上去就能看传感器数据。嗯,这种场景下,SoftAP 就是救命稻草。
| 特性 | ESP32 SoftAP | 普通路由器 |
|---|---|---|
| 最大客户端数 | 通常 4~10 个 | 几十到上百个 |
| 发射功率 | 约 20 dBm | 可调,通常更高 |
| IP 分配 | 内置 DHCP 服务器 | 独立 DHCP 服务 |
| 并发模式 | 支持 STA + AP 共存 | 纯 AP 模式 |
4.2 初始化 SoftAP
初始化 SoftAP 的流程,其实和 STA 模式很像。只是配置的参数不同。我习惯把初始化拆成三步:
- 初始化网络接口——调用
esp_netif_init()和esp_event_loop_create_default() - 创建 AP 网络接口——
esp_netif_create_default_wifi_ap() - 配置并启动 WiFi——设置 SSID、密码、信道等参数
来看代码。这是最基础的 SoftAP 初始化:
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
void app_main(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) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 2. 初始化网络接口
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
// 3. 初始化 WiFi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 4. 配置 AP 参数
wifi_config_t wifi_config = {
.ap = {
.ssid = "ESP32_AP",
.ssid_len = strlen("ESP32_AP"),
.password = "12345678",
.max_connection = 4,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
printf("SoftAP 已启动,SSID: ESP32_AP\n");
}
WIFI_AUTH_OPEN。我曾经在项目里忘了改这个,结果设备一直连不上,排查了半天才发现是认证模式不匹配。
4.3 配置热点参数
热点参数不只是 SSID 和密码。还有几个关键字段,我列个表给你看:
| 参数 | 说明 | 取值范围 |
|---|---|---|
| ssid | 热点名称 | 最长 32 字节 |
| password | 连接密码 | 8~64 字节,或空 |
| ssid_len | SSID 长度,0 表示自动计算 | 0 或实际长度 |
| channel | WiFi 信道 | 1~13 |
| authmode | 认证模式 | WIFI_AUTH_OPEN / WPA / WPA2 / WPA3 |
| max_connection | 最大客户端数 | 1~10(推荐 ≤4) |
| beacon_interval | 信标帧间隔(毫秒) | 100~1000,默认 100 |
信道选择有讲究。2.4G 频段有 13 个信道,但 1、6、11 互不干扰。我一般选 1 或 6,因为大多数家用路由器默认用这些信道,兼容性好。
看一个更完整的配置示例:
wifi_config_t wifi_config = {
.ap = {
.ssid = "MyESP32",
.ssid_len = 0, // 自动计算
.password = "securePass123",
.channel = 6, // 手动指定信道
.authmode = WIFI_AUTH_WPA2_PSK,
.max_connection = 4,
.beacon_interval = 100, // 默认值
},
};
4.4 管理客户端连接
客户端连上来之后,你怎么知道?怎么管理?ESP32 提供了事件回调机制。
注册两个事件:
WIFI_EVENT_AP_STACONNECTED——有设备连入WIFI_EVENT_AP_STADISCONNECTED——有设备断开
事件回调里能拿到客户端的 MAC 地址。我习惯在连接时打印 MAC,断开时也打印,方便调试。
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
printf("设备连入,MAC: " MACSTR "\n", MAC2STR(event->mac));
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
printf("设备断开,MAC: " MACSTR "\n", MAC2STR(event->mac));
}
}
// 注册事件
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
你还可以主动获取已连接的客户端列表:
wifi_sta_list_t sta_list;
esp_wifi_ap_get_sta_list(&sta_list);
printf("当前连接数: %d\n", sta_list.num);
for (int i = 0; i < sta_list.num; i++) {
printf("客户端 %d MAC: " MACSTR "\n", i, MAC2STR(sta_list.sta[i].mac));
}
WIFI_EVENT_AP_STACONNECTED 事件里检查 MAC 地址,不在白名单的就调用 esp_wifi_deauth_sta() 踢掉。嗯,简单粗暴,但有效。
4.5 IP 地址分配
客户端连上 SoftAP 后,需要获取 IP 地址才能通信。ESP32 内置了 DHCP 服务器,默认自动分配 IP。
默认的 IP 池是 192.168.4.2 到 192.168.4.254,网关是 192.168.4.1。这个地址段可以改。
我经常需要自定义 IP 段,比如和现有网络不冲突。看代码:
// 获取 AP 的 netif 句柄
esp_netif_t *ap_netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
// 配置静态 IP
esp_netif_ip_info_t ip_info;
memset(&ip_info, 0, sizeof(ip_info));
esp_netif_str_to_ip4("192.168.5.1", &ip_info.ip);
esp_netif_str_to_ip4("192.168.5.1", &ip_info.gw);
esp_netif_str_to_ip4("255.255.255.0", &ip_info.netmask);
esp_netif_set_ip_info(ap_netif, &ip_info);
// 配置 DHCP 地址池
esp_netif_dhcps_start(ap_netif); // 先启动 DHCP
dhcps_lease_t lease;
lease.enable = true;
esp_netif_str_to_ip4("192.168.5.100", &lease.start_ip);
esp_netif_str_to_ip4("192.168.5.200", &lease.end_ip);
esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &lease, sizeof(lease));
这里要注意顺序:先设置 IP 信息,再启动 DHCP,最后配置地址池。我刚开始搞反了顺序,结果 DHCP 分配的还是默认网段。
255.255.0.0,地址池范围会很大,DHCP 分配可能变慢。保持 255.255.255.0 最稳妥。
4.6 知识结构总览
这一章的内容,我用一张图帮你理清脉络:
这张图把 SoftAP 的四个核心模块串起来了。初始化是基础,配置参数是定制,客户端管理是交互,IP 分配是通信的前提。四者缺一不可。
4.7 完整示例:一个可用的 SoftAP
最后,给你一个可以直接跑起来的完整示例。它包含了初始化、事件回调、IP 配置:
#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"
#include "lwip/err.h"
#include "lwip/sys.h"
static const char *TAG = "softap";
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "设备连入: " MACSTR, MAC2STR(event->mac));
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "设备断开: " MACSTR, MAC2STR(event->mac));
}
}
void app_main(void)
{
// 初始化 NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 初始化网络接口
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
// 配置静态 IP
esp_netif_ip_info_t ip_info;
IP4_ADDR(&ip_info.ip, 192, 168, 5, 1);
IP4_ADDR(&ip_info.gw, 192, 168, 5, 1);
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
esp_netif_set_ip_info(ap_netif, &ip_info);
// 初始化 WiFi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 注册事件
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
// 配置 AP
wifi_config_t wifi_config = {
.ap = {
.ssid = "ESP32_Demo",
.ssid_len = 0,
.password = "demo1234",
.channel = 6,
.authmode = WIFI_AUTH_WPA2_PSK,
.max_connection = 4,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "SoftAP 已启动,SSID: ESP32_Demo,IP: 192.168.5.1");
}
这个示例里,我把 IP 改成了 192.168.5.1 网段,避免和常见路由器冲突。你烧录后,用手机搜一下 ESP32_Demo,密码 demo1234,连上去就能拿到 192.168.5.x 的 IP。
ping 192.168.5.1 测试连通性。如果 ping 不通,先检查手机是否拿到了正确的 IP 地址,再检查 ESP32 的 WiFi 状态。
好了,这一章就到这里。SoftAP 模式其实不复杂,但细节不少。初始化顺序、参数配置、事件处理、IP 分配,每一步都有坑。我踩过的坑都写在里面了,你照着做,应该能少走弯路。