最近使用 ESP32-C3 跑了下 softAP demo,阅读了 demo 源码及跑过 demo 之后,脑海中不禁有以下几个疑问:
- ESP32-C3 作为 SoftAP 时,默认的 IP 地址为 192.168.4.1,如何修改默认的 IP 地址?
- ESP32-C3 作为 SoftAP 时,有 Station 连接上了之后,如何获取 SoftAP 分配给这个 Station 的 IP 地址 ?
笔者认为 Espressif 虽然提供了较为丰富的文档和示例,但是值得改进的地方还是有很多的。就以 softAP demo 为例,这个 demo 仅仅只是做了一个简单的示例,而对于我以上的 2 个问题,却没有很好的展现出来。
在网上看了一大堆所谓的技术文章,全都是千篇一律的跑了一下 softAP demo,然后对 softAP demo 源码进行一堆所谓的分析,然后就没有然后了,没有深入的对这个 demo 进行发掘。所以笔者阅读文档,结合 softAP demo 源码,写了一份自己的 demo,对以上 2 个问题以代码的形式做了示例。
ESP32-C3 作为 SoftAP 时,默认的 IP 地址为 192.168.4.1,如何修改默认的 IP 地址?
要修改 SoftAP 的默认 IP 地址,核心的逻辑就是要先停止 DHCP,然后设置 IP 地址,子网掩码,网关,然后在启用 DHCP 即可。
void set_softap_ip(esp_netif_t *softap)
{
esp_netif_ip_info_t ip = { 0 };
ip.ip.addr = ipaddr_addr(CONFIG_ESP_SOFTAP_IP);
ip.netmask.addr = ipaddr_addr(CONFIG_ESP_SOFTAP_NETMASK);
ip.gw.addr = ipaddr_addr(CONFIG_ESP_SOFTAP_GATEWAY);
esp_netif_dhcps_stop(softap);
esp_netif_set_ip_info(softap, &ip);
esp_netif_dhcps_start(softap);
return;
}
void wifi_init_softap(void)
{
esp_log_level_set("wifi", ESP_LOG_ERROR);
esp_netif_t *softap = esp_netif_create_default_wifi_ap();
set_softap_ip(softap);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = {
.ap = {
.ssid = CONFIG_ESP_WIFI_SSID,
.ssid_len = strlen(CONFIG_ESP_WIFI_SSID),
.channel = CONFIG_ESP_WIFI_CHANNEL,
.password = CONFIG_ESP_WIFI_PASSWORD,
.max_connection = CONFIG_ESP_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.pmf_cfg = {
.required = false,
},
},
};
if (strlen(CONFIG_ESP_WIFI_PASSWORD) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
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, "wifi_init_softap finished. SSID:%s password:%s channel:%d", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD, CONFIG_ESP_WIFI_CHANNEL);
}
ESP32-C3 作为 SoftAP 时,有 Station 连接上了之后,如何获取 SoftAP 分配给这个 Station 的 IP 地址 ?
Station 在连接 SoftAP 的过程中,其实可以分为两个阶段,在这两个阶段分别可以获取到 Station 的不同信息。
- Station 连接上 SoftAP,但还没有获取到 SoftAP 分配的 IP 地址,也就是在 DHCP 过程之前。此时只能获取到 Station 的 MAC 地址。
- Station 连接上 SoftAP,获取到 SoftAP 分配的 IP 地址,也就是在 DHCP 过程之后。此时能获取到 Station 的 IP 地址。
第一阶段属于 WiFi 驱动层,原生的 softAP demo 其实写的已经很好了,主要就是通过注册 WIFI_EVENT,在 event 回调中来获取到 Station 的 MAC 地址。
第二阶段属于 TCP/IP 协议栈层,也就是 LWIP。但原生的 softAP demo 没有对这一部分进行说明。以下代码是笔者根据官方文档自己写的。读者可以参考这部分代码来获取连接上的 Station 的 IP 地址。
-
首先注册 IP_EVENT
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
-
在 event 回调中对 IP_EVENT_AP_STAIPASSIGNED 这个
event_id
进行处理。static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == IP_EVENT) { ip_event_handler(arg, event_id, event_data); } else { wifi_event_handler(arg, event_id, event_data); } }
static void ip_event_handler(void* arg, int32_t event_id, void* event_data) { if (event_id == IP_EVENT_AP_STAIPASSIGNED) { ip_event_ap_staipassigned_t *event = (ip_event_ap_staipassigned_t *) event_data; ESP_LOGI(TAG, "the IP address assigned by the SoftAP is" IPSTR, IP2STR(&event->ip)); } }
之所以说 Espressif 值的改进的地方还有很多,一是因为 softAP demo 展示的不够详细,另一个就是因为文档更新的不及时。在阅读 Espressif Event Handling 时,文档中写明了
event_id
为 IP_EVENT_AP_STAIPASSIGNED,event_data
是一个n/a
结构:
但是在阅读 esp-idf 源码的时候突然看到了ip_event_ap_staipassigned_t
的定义:
所以在 IP_EVENT_AP_STAIPASSIGNED 的处理中抱着试一试的想法将event_data
强制转换成了ip_event_ap_staipassigned_t
指针,没想到还真的可以获取到连接上的 Station 的 IP 地址。
最后附上笔者自己的 SoftAP 代码,编译后可以直接在 ESP32-C3 上运行,esp-idf 使用 master 分支,测试 commit 为 fde4afc67a。
完整工程代码已经上传到我的 GitHub 仓库 esp-examples ,有需要的读者可以直接下载。
#include <string.h>
#include <netdb.h>
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "nvs_flash.h"
#include "esp_netif.h"
static const char *TAG = "example";
static void wifi_event_handler(void* arg, 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, "station "MACSTR" join, AID=%d", MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *) event_data;
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid);
}
}
static void ip_event_handler(void* arg, int32_t event_id, void* event_data)
{
if (event_id == IP_EVENT_AP_STAIPASSIGNED) {
ip_event_ap_staipassigned_t *event = (ip_event_ap_staipassigned_t *) event_data;
ESP_LOGI(TAG, "the IP address assigned by the SoftAP is" IPSTR, IP2STR(&event->ip));
}
}
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
if (event_base == IP_EVENT) {
ip_event_handler(arg, event_id, event_data);
} else {
wifi_event_handler(arg, event_id, event_data);
}
}
void set_softap_ip(esp_netif_t *softap)
{
esp_netif_ip_info_t ip = { 0 };
ip.ip.addr = ipaddr_addr(CONFIG_ESP_SOFTAP_IP);
ip.netmask.addr = ipaddr_addr(CONFIG_ESP_SOFTAP_NETMASK);
ip.gw.addr = ipaddr_addr(CONFIG_ESP_SOFTAP_GATEWAY);
esp_netif_dhcps_stop(softap);
esp_netif_set_ip_info(softap, &ip);
esp_netif_dhcps_start(softap);
return;
}
void wifi_init_softap(void)
{
esp_log_level_set("wifi", ESP_LOG_ERROR);
esp_netif_t *softap = esp_netif_create_default_wifi_ap();
set_softap_ip(softap);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = {
.ap = {
.ssid = CONFIG_ESP_WIFI_SSID,
.ssid_len = strlen(CONFIG_ESP_WIFI_SSID),
.channel = CONFIG_ESP_WIFI_CHANNEL,
.password = CONFIG_ESP_WIFI_PASSWORD,
.max_connection = CONFIG_ESP_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.pmf_cfg = {
.required = false,
},
},
};
if (strlen(CONFIG_ESP_WIFI_PASSWORD) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
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, "wifi_init_softap finished. SSID:%s password:%s channel:%d", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD, CONFIG_ESP_WIFI_CHANNEL);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
wifi_init_softap();
}