WiFi外設配置
ESP32/8266的Wi-Fi庫支持配置及監控Wi-Fi連網功能
相關內容參考樂鑫的ESP32/8266文檔https://docs.espressif.com/projects/esp-idf/zh_CN/release-v4.1/api-reference/network/esp_wifi.html
基本模式
-
基站模式(又稱STA模式或Client模式):將ESP連接到附近的AP,此時相當於ESP在蹭網
-
AP模式(又稱Soft-AP模式或Server模式):將ESP設置為AP,可供周圍設備連接,此時相當於ESP開熱點
-
AP-STA共存模式:ESP32既是接入點,同時又作為基站連接到另外一個接入點,此時相當於ESP連着隔壁wifi開熱點給自家用
同時支持以上模式的安全模式(WPA、WPA2、WEP等),可以理解成安全蹭網
基本功能
- 主動/被動掃描附近AP,主動找別人家網蹭
- 使用混雜模式監控IEEE802.11 Wi-Fi數據包,可以理解成ESP能看到你上了什么不可描述的網站
庫函數
- 初始化與設置
esp_wifi_init(const wifi_init_config_t *config)//WiFi功能初始化,config為初始化結構體句柄
esp_wifi_set_config(wifi_interface_t interface, wifi_config_t *conf)//使能設置
esp_wifi_set_mode(wifi_mode_t mode)//模式設置
//可如下配置
WIFI_MODE_NULL=0
WIFI_MODE_STA//STA模式
WIFI_MODE_AP//軟AP模式
WIFI_MODE_APSTA//混合模式
WIFI_MODE_MAX
esp_wifi_get_mode(wifi_mode_t *mode)//獲取當前模式
esp_wifi_get_config(wifi_interface_t interface, wifi_config_t *conf)//獲取當前設置
- 關閉WiFi
esp_wifi_stop()//STA模式下斷開wifi連接,AP模式下關閉熱點並釋放內存,共用模式下斷開連接並關閉熱點
esp_wifi_deinit()//釋放曾在esp_wifi_init中申請的資源並停止WiFi工作,不需要wifi功能時可以使用
- 連接/斷開WiFi
/* 用於STA模式 */
esp_wifi_connect()//連接WiFi
esp_wifi_disconnect()//斷開WiFi
/* 用於AP模式 */
esp_wifi_deauth_sta(uint16_t aid)//停止對接入設備的授權——不讓別人蹭網
esp_wifi_ap_get_sta_aid(const uint8_t mac[6], uint16_t *aid)//獲取當前接入的設備信息
esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta)//獲取當前接入的設備列表
- 掃描附近
esp_wifi_scan_start(const wifi_scan_config_t *config, bool block)//掃描AP以蹭網
/* 推薦最大掃描時間為1500ms */
esp_wifi_scan_stop()//在途中停止掃描
esp_wifi_scan_get_ap_num(uint16_t *number)//獲得最后一次掃描得到的AP號碼
esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records)//獲取掃描記錄
esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info)//獲取當前連接wifi的相關信息
//返回如下結構體的指針
uint8_t bssid[6]//MAC地址
uint8_t ssid[33]//SSID
uint8_t primary//AP通道
wifi_second_chan_t second//AP第二通道
int8_t rssi//信號強度
wifi_auth_mode_t authmode//認證模式
wifi_cipher_type_t pairwise_cipher//PTK成對傳輸密鑰,用於單播數據幀的加密解密
wifi_cipher_type_t group_cipher//GTK組臨時密鑰,用於組播數據幀和廣播數據幀的加密和解密
wifi_ant_t ant//用於接收信號的天線引腳
/* 相關控制寄存器位 */
uint32_t phy_11b : 1//11b模式開啟標志
uint32_t phy_11g : 1//11g模式開啟標志
uint32_t phy_11n : 1//11n模式開啟標志
uint32_t phy_lr : 1//低頻模式開啟標志
uint32_t wps : 1//WPS支持情況標志
uint32_t reserved : 27//寄存器保留位
/* 相關控制寄存器位 */
wifi_country_t country//AP的國家信息
- 操作系統相關
esp_wifi_set_event_mask(uint32_t mask)//設置事件掩碼
- 其他
esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap)//設置特殊接口的協議類型
//可選WIFI_PROTOCOL_11B、WIFI_PROTOCOL_11G、WIFI_PROTOCOL_11N)
esp_wifi_get_protocol(wifi_interface_t ifx, uint8_t *protocol_bitmap)//獲取當前協議類型
sp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw)//設置帶寬
esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw)//獲取當前帶寬
sp_wifi_set_channel(uint8_t primary, wifi_second_chan_t second)//設置primary/secondary通道
esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second)//獲取當前使用的通道
esp_wifi_set_country(const wifi_country_t *country)//設置當前的國家信息
esp_wifi_get_country(wifi_country_t *country)//獲取當前的國家信息
esp_wifi_set_mac(wifi_interface_t ifx, const uint8_t mac[6])//設置當前mac地址
esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6])//獲取當前mac地址
esp_wifi_set_ant_gpio(const wifi_ant_gpio_config_t *config)//設置天線引腳
esp_wifi_get_ant_gpio(wifi_ant_gpio_config_t *config)//獲取當前天線引腳
esp_wifi_set_ant(const wifi_ant_config_t *config)//設置天線設定
esp_wifi_get_ant(wifi_ant_config_t *config)//獲取當前天線設定
esp_wifi_set_promiscuous(bool en)//使能混雜模式
esp_wifi_get_promiscuous(bool *en)//獲取混雜模式
esp_wifi_set_promiscuous_filter(const wifi_promiscuous_filter_t *filter)//設置混雜模式過濾器,默認過濾除WIFI_PKT_MISC外的包
esp_wifi_get_promiscuous_filter(wifi_promiscuous_filter_t *filter)//獲取混雜模式過濾器
esp_wifi_set_promiscuous_ctrl_filter(const wifi_promiscuous_filter_t *filter)//使能混雜類型過濾器的子類型過濾
esp_wifi_get_promiscuous_ctrl_filter(wifi_promiscuous_filter_t *filter)//獲取混雜類型過濾器的子類型過濾
esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb)//設置混雜模式監控回調函數
- 低功耗相關
esp_wifi_set_inactive_time(wifi_interface_t ifx, uint16_t sec)//設置暫時休眠時間
esp_wifi_get_ant(wifi_ant_config_t *config)//獲取暫時休眠時間
特征:大部分API都有對應的set和get兩個方向,需要回傳數據時使用get*,初始設置時使用set*
AP模式初始化
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);
}
其中主要用到了wifi_config_t這個結構體,它的內容如下所示
typedef struct {
uint8_t ssid[32];//SSID
uint8_t password[64];//密碼
uint8_t ssid_len;//SSID長度,若設為0則會自動查找到終止字符;否則會在規定長度處截斷
uint8_t channel;//AP的通道
wifi_auth_mode_t authmode;//授權模式
uint8_t ssid_hidden;//是否廣播SSID,默認為0-廣播;設為1則不廣播
uint8_t max_connection;//能連接的最大節點數量,默認為4,最大為4
uint16_t beacon_interval;//信標間隔,默認100ms,應設置在100-60000ms內
} wifi_ap_config_t;
STA模式初始化
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
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,
&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));
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) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
/* The event will not be processed after unregister */
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
}
其中主要用到了wifi_sta_config_t這個結構體,它的內容如下所示
typedef struct {
uint8_t ssid[32];//SSID
uint8_t password[64];//密碼
bool bssid_set;//是否設置目標AP的MAC地址,一般設為0;只有用戶需要查看AP的MAC地址時才設為1
uint8_t bssid[6];//目標AP的MAC地址
uint8_t channel;//目標AP的通道,如果未知設為0;范圍是1-13
} wifi_sta_config_t;
AP-STA共存模式
esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id)
{
case SYSTEM_EVENT_STA_START:
ESP_LOGI(TAG, "Connecting to AP");
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "Connected");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
//ESP_LOGI(TAG, "Wifi disconnected, try to connect again...");
esp_wifi_connect();
break;
default:
break;
}
return ESP_OK;
}
void ESP_net_init(void)
{
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_APSTA) );
wifi_config_t sta_config = {
.sta = {
.ssid = TARGET_ESP_WIFI_SSID,
.password = TARGET_ESP_WIFI_PASS,
.bssid_set = false
}
};
wifi_config_t ap_config = {
.ap = {
.ssid = AP_ESP_WIFI_SSID,
.password = AP_ESP_WIFI_PASS,
.ssid_len = 0,
.max_connection = AP_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_PSK
}
};
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config));
esp_err_t tmp=esp_wifi_set_config(WIFI_IF_AP, &ap_config);
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
}
void app_main(void)
{
//init 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);
//init wifi ap and station
ESP_net_init();
}
在這里使用了狀態機(SM)的編程思路,【開始連接】-【連接完畢】-【丟失連接】幾個狀態切換中都會調用event_handler()進行處理並打印相關信息
基本初始化方法
//設置線程
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//進行默認初始化
//設置wifi_config結構體來配置具體的wifi模式
wifi_config_t sta_wifi_config = {
.sta = {
.ssid = SSID,
.password = PASSWORD,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {//這里可省略
.capable = true,
.required = false
},
},
};
wifi_config_t ap_wifi_config = {
.ap = {
.ssid = SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = WIFI_CHANNEL,
.password = PASSWORD,
.max_connection = MAX_STA_CONN,//這里可省略
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
if (strlen(PASSWORD) == 0)//檢查密碼是否為空
{
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
//檢查錯誤並使能設置
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config));
esp_err_t tmp=esp_wifi_set_config(WIFI_IF_AP, &ap_config);
esp_wifi_connect();//連接wifi
/* 中間可加入ESP_LOGI()輸出debg消息 */
WiFi連接實際上使用的是一套異步的狀態機,所有需要調用的外設都被ESP-IDF封裝起來了,開發者只需要配置基本邏輯即可。流程如下:
-
初始化用於存儲WiFi配置數據(包括ssid和密碼)的NVS
-
配置WiFi數據並將其寫入WiFi外設(或NVS)
-
開啟WiFi
-
設備自動根據外設寄存器內的配置連接附近WiFi,並根據當前連接情況向主程序發送事件集
-
開發者編寫的狀態機負責處理WiFi外設發來的事件,主要分成以下幾種情況:
- WiFi已連接
- WiFi未連接
- 找不到指定ssid的WiFi
- 連接丟失
一般使用ESP-IDF中的默認事件循環來實現狀態機
使用其中的事件類型和事件ID區分各個不同的具體時間
關於ESP-IDF的事件集可以參考ESP32上移植的FreeRTOS相關教程
