ESP32_IDF學習2【WiFi】


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

基本模式

  1. 基站模式(又稱STA模式或Client模式):將ESP連接到附近的AP,此時相當於ESP在蹭網

  2. AP模式(又稱Soft-AP模式或Server模式):將ESP設置為AP,可供周圍設備連接,此時相當於ESP開熱點

  3. AP-STA共存模式:ESP32既是接入點,同時又作為基站連接到另外一個接入點,此時相當於ESP連着隔壁wifi開熱點給自家用

同時支持以上模式的安全模式(WPA、WPA2、WEP等),可以理解成安全蹭網

基本功能

  1. 主動/被動掃描附近AP,主動找別人家網蹭
  2. 使用混雜模式監控IEEE802.11 Wi-Fi數據包,可以理解成ESP能看到你上了什么不可描述的網站

庫函數

  1. 初始化與設置
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)//獲取當前設置
  1. 關閉WiFi
esp_wifi_stop()//STA模式下斷開wifi連接,AP模式下關閉熱點並釋放內存,共用模式下斷開連接並關閉熱點
esp_wifi_deinit()//釋放曾在esp_wifi_init中申請的資源並停止WiFi工作,不需要wifi功能時可以使用
  1. 連接/斷開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)//獲取當前接入的設備列表
  1. 掃描附近
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的國家信息
  1. 操作系統相關
esp_wifi_set_event_mask(uint32_t mask)//設置事件掩碼
  1. 其他
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)//設置混雜模式監控回調函數  
  1. 低功耗相關
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封裝起來了,開發者只需要配置基本邏輯即可。流程如下:

  1. 初始化用於存儲WiFi配置數據(包括ssid和密碼)的NVS

  2. 配置WiFi數據並將其寫入WiFi外設(或NVS)

  3. 開啟WiFi

  4. 設備自動根據外設寄存器內的配置連接附近WiFi,並根據當前連接情況向主程序發送事件集

  5. 開發者編寫的狀態機負責處理WiFi外設發來的事件,主要分成以下幾種情況:

    • WiFi已連接
    • WiFi未連接
    • 找不到指定ssid的WiFi
    • 連接丟失

    一般使用ESP-IDF中的默認事件循環來實現狀態機

    使用其中的事件類型事件ID區分各個不同的具體時間

關於ESP-IDF的事件集可以參考ESP32上移植的FreeRTOS相關教程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM