一舔示、概述
Wi-Fi 庫支持配置及監(jiān)控 ESP32 Wi-Fi 連網(wǎng)功能借杰。
支持配置:
-
基站模式(即 STA 模式或 Wi-Fi 客戶端模式)过吻,此時 ESP32 連接到接入點 (AP)。
-
AP 模式(即 Soft-AP 模式或接入點模式),此時基站連接到 ESP32纤虽。
AP-STA 共存模式(ESP32 既是接入點乳绕,同時又作為基站連接到另外一個接入點)。
上述模式的各種安全模式(WPA逼纸、WPA2 及 WEP 等)洋措。
掃描接入點(包括主動掃描及被動掃描)。
使用混雜模式監(jiān)控 IEEE802.11 Wi-Fi 數(shù)據(jù)包杰刽。
二菠发、API說明
以下 WIFI 接口位于 esp_wifi/include/esp_wifi.h。
2.1 esp_wifi_init
2.2 esp_wifi_set_mode
2.3 esp_wifi_get_mode
2.4 esp_wifi_start
2.5 esp_wifi_connect
2.6 esp_wifi_disconnect
2.7 esp_wifi_scan_start
2.8 esp_wifi_get_mac
2.9 esp_wifi_set_config
2.10 esp_wifi_get_config
三贺嫂、AP模式
3.1 一般場景
3.2 Wi-Fi/LwIP初始階段
如上圖中 1.1\1.2\1.3\1.4 所示滓鸠,分別
-
初始化LwIP
創(chuàng)建LwIP核心任務(wù)并初始化與LwIP相關(guān)的工作。
ESP_ERROR_CHECK(esp_netif_init());
-
初始化Wi-Fi事件處理
Wi-Fi事件處理基于esp_event庫第喳。Wi-Fi驅(qū)動程序會將事件發(fā)送到默認事件循環(huán)糜俗。應(yīng)用程序可以在使用進行注冊的回調(diào)中處理這些事件esp_event_handler_register()
。esp_netif組件還處理Wi-Fi事件曲饱,以提供一組默認行為悠抹。例如,當(dāng)Wi-Fi站連接到AP時渔工,esp_netif將自動啟動DHCP客戶端(默認情況下)锌钮。
// 創(chuàng)建系統(tǒng)事件任務(wù)并初始化應(yīng)用程序事件的回調(diào)函數(shù)。
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 創(chuàng)建具有TCP / IP堆棧的默認網(wǎng)絡(luò)接口實例綁定AP引矩。
esp_netif_create_default_wifi_ap();
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
- 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
3.3 Wi-Fi配置階段
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 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(ESP_IF_WIFI_AP, &wifi_config));
3.4 Wi-Fi啟動階段
ESP_ERROR_CHECK(esp_wifi_start());
3.5 Wi-Fi連接階段
當(dāng)有終端接入或斷開連接時,產(chǎn)生 WIFI_EVENT_AP_STACONNECTED
或 WIFI_EVENT_AP_STADISCONNECTED
事件侵浸。
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, "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);
}
}
3.6 完整代碼
/* WiFi softAP Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#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"
/* The examples use WiFi configuration that you can set via project configuration menu.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN
static const char *TAG = "wifi 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, "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);
}
}
void wifi_init_softap(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 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(ESP_IF_WIFI_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
void app_main(void)
{
//Initialize 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_LOGI(TAG, "ESP_WIFI_MODE_AP");
wifi_init_softap();
}
四旺韭、STA模式
4.1 一般場景
4.2 Wi-Fi/LwIP初始階段
如上圖中 1.1\1.2\1.3\1.4 所示,分別
-
初始化LwIP
創(chuàng)建LwIP核心任務(wù)并初始化與LwIP相關(guān)的工作掏觉。
ESP_ERROR_CHECK(esp_netif_init());
-
初始化Wi-Fi事件處理
Wi-Fi事件處理基于esp_event庫区端。Wi-Fi驅(qū)動程序會將事件發(fā)送到默認事件循環(huán)。應(yīng)用程序可以在使用進行注冊的回調(diào)中處理這些事件esp_event_handler_register()
澳腹。esp_netif組件還處理Wi-Fi事件织盼,以提供一組默認行為。例如酱塔,當(dāng)Wi-Fi站連接到AP時沥邻,esp_netif將自動啟動DHCP客戶端(默認情況下)。
// 創(chuàng)建系統(tǒng)事件任務(wù)并初始化應(yīng)用程序事件的回調(diào)函數(shù)羊娃。
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 創(chuàng)建具有TCP / IP堆棧的默認網(wǎng)絡(luò)接口實例綁定基站唐全。
esp_netif_create_default_wifi_sta();
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,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
instance_got_ip));
- 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
4.3 Wi-Fi配置階段
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Setting a password implies station will connect to all security modes including WEP/WPA.
* However these modes are deprecated and not advisable to be used. Incase your Access point
* doesn't support WPA2, these mode can be enabled by commenting below line */
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.required = false
},
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
4.4 Wi-Fi啟動階段
調(diào)用esp_wifi_start()
以啟動Wi-Fi驅(qū)動程序排苍。
Wi-Fi驅(qū)動程序?qū)?a target="_blank">WIFI_EVENT_STA_START發(fā)布到事件任務(wù);然后珊泳,事件任務(wù)將執(zhí)行一些常規(guī)操作缺猛,并將調(diào)用應(yīng)用程序事件回調(diào)函數(shù)。
應(yīng)用程序事件回調(diào)函數(shù)將WIFI_EVENT_STA_START中繼到應(yīng)用程序任務(wù)延届。此時調(diào)用esp_wifi_connect()
剪勿。
ESP_ERROR_CHECK(esp_wifi_start());
4.5 Wi-Fi連接階段
一旦esp_wifi_connect()
被調(diào)用,Wi-Fi驅(qū)動程序?qū)㈤_始內(nèi)部掃描/連接過程方庭。
如果內(nèi)部掃描/連接過程成功厕吉,將生成WIFI_EVENT_STA_CONNECTED。在事件任務(wù)中二鳄,它將啟動DHCP客戶端赴涵,該客戶端最終將觸發(fā)DHCP進程。
由于例如密碼錯誤订讼,找不到AP等原因髓窜,Wi-Fi連接可能會失敗。在這種情況下欺殿,會出現(xiàn)WIFI_EVENT_STA_DISCONNECTED并提供這種失敗的原因寄纵。
static void 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) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} 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(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
4.6 Wi-Fi“Got IP”階段
初始化DHCP客戶端后,將開始IP階段脖苏。如果從DHCP服務(wù)器成功接收到IP地址程拭,則將出現(xiàn)IP_EVENT_STA_GOT_IP,并且事件任務(wù)將執(zhí)行常規(guī)處理棍潘。
在應(yīng)用程序事件回調(diào)中恃鞋,IP_EVENT_STA_GOT_IP被中繼到應(yīng)用程序任務(wù)。對于基于LwIP的應(yīng)用程序亦歉,此事件非常特殊恤浪,這意味著該應(yīng)用程序已準備就緒,可以開始其任務(wù)肴楷,例如創(chuàng)建TCP / UDP套接字等水由。一個非常常見的錯誤是在收到IP_EVENT_STA_GOT_IP之前初始化套接字。接收IP之前赛蔫,請勿開始與套接字相關(guān)的工作砂客。
五、掃描
當(dāng)前呵恢,esp_wifi_scan_start()
僅在Station或Station + AP模式下支持該API鞠值。
掃描所有頻道后,將出現(xiàn)WIFI_EVENT_SCAN_DONE瑰剃。
應(yīng)用程序的事件回調(diào)函數(shù)通知應(yīng)用程序任務(wù)已接收到WIFI_EVENT_SCAN_DONE齿诉。esp_wifi_scan_get_ap_num()
調(diào)用以獲取在此掃描中找到的AP的數(shù)量。然后,它分配足夠的條目和調(diào)用esp_wifi_scan_get_ap_records()
以獲取AP記錄粤剧。請注意歇竟,一旦esp_wifi_scan_get_ap_records()
被調(diào)用,Wi-Fi驅(qū)動程序中的AP記錄將被釋放抵恋。esp_wifi_scan_get_ap_records()
一次掃描完成事件請勿調(diào)用兩次焕议。如果esp_wifi_scan_get_ap_records()
在發(fā)生掃描完成事件時未調(diào)用,則不會釋放由Wi-Fi驅(qū)動程序分配的AP記錄弧关。因此盅安,請確保調(diào)用esp_wifi_scan_get_ap_records()
,但只能調(diào)用一次世囊。
/* Scan Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
This example shows how to scan for available set of APs.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"
#define DEFAULT_SCAN_LIST_SIZE CONFIG_EXAMPLE_SCAN_LIST_SIZE
static const char *TAG = "scan";
static void print_auth_mode(int authmode)
{
switch (authmode) {
case WIFI_AUTH_OPEN:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OPEN");
break;
case WIFI_AUTH_WEP:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WEP");
break;
case WIFI_AUTH_WPA_PSK:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_PSK");
break;
case WIFI_AUTH_WPA2_PSK:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_PSK");
break;
case WIFI_AUTH_WPA_WPA2_PSK:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_WPA2_PSK");
break;
case WIFI_AUTH_WPA2_ENTERPRISE:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_ENTERPRISE");
break;
case WIFI_AUTH_WPA3_PSK:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_PSK");
break;
case WIFI_AUTH_WPA2_WPA3_PSK:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_PSK");
break;
default:
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_UNKNOWN");
break;
}
}
static void print_cipher_type(int pairwise_cipher, int group_cipher)
{
switch (pairwise_cipher) {
case WIFI_CIPHER_TYPE_NONE:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_NONE");
break;
case WIFI_CIPHER_TYPE_WEP40:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP40");
break;
case WIFI_CIPHER_TYPE_WEP104:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP104");
break;
case WIFI_CIPHER_TYPE_TKIP:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP");
break;
case WIFI_CIPHER_TYPE_CCMP:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_CCMP");
break;
case WIFI_CIPHER_TYPE_TKIP_CCMP:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
break;
default:
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
break;
}
switch (group_cipher) {
case WIFI_CIPHER_TYPE_NONE:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_NONE");
break;
case WIFI_CIPHER_TYPE_WEP40:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP40");
break;
case WIFI_CIPHER_TYPE_WEP104:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP104");
break;
case WIFI_CIPHER_TYPE_TKIP:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP");
break;
case WIFI_CIPHER_TYPE_CCMP:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_CCMP");
break;
case WIFI_CIPHER_TYPE_TKIP_CCMP:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
break;
default:
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
break;
}
}
/* Initialize Wi-Fi as sta and set scan method */
static void wifi_scan(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
uint16_t number = DEFAULT_SCAN_LIST_SIZE;
wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
uint16_t ap_count = 0;
memset(ap_info, 0, sizeof(ap_info));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);
for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
print_auth_mode(ap_info[i].authmode);
if (ap_info[i].authmode != WIFI_AUTH_WEP) {
print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
}
ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
}
}
void app_main(void)
{
// Initialize 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 );
wifi_scan();
}
? 由 Leung 寫于 2021 年 4 月 19 日
? 參考:Wi-Fi驅(qū)動程序
ESP32 開發(fā)筆記(三)源碼示例 19_WIFI_STA 創(chuàng)建STA站模式連接路由器
ESP32 開發(fā)筆記(三)源碼示例 15_WIFI_AP 創(chuàng)建軟AP示例
ESP32 開發(fā)筆記(三)源碼示例 14_WIFI_Scan 附近WIFI信號掃描示例