天天看點

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

一、概述

Wi-Fi 庫支援配置及監控 ESP32 Wi-Fi 連網功能。

支援配置:

  • 基站模式(即 STA 模式或 Wi-Fi 用戶端模式),此時 ESP32 連接配接到接入點 (AP)。
    ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描
  • AP 模式(即 Soft-AP 模式或接入點模式),此時基站連接配接到 ESP32。
    ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描
  • AP-STA 共存模式(ESP32 既是接入點,同時又作為基站連接配接到另外一個接入點)。
  • 上述模式的各種安全模式(WPA、WPA2 及 WEP 等)。
  • 掃描接入點(包括主動掃描及被動掃描)。
  • 使用混雜模式監控 IEEE802.11 Wi-Fi 資料包。

ESP-IDF 程式設計指南——Wi-Fi

二、API說明

以下 WIFI 接口位于 esp_wifi/include/esp_wifi.h。

2.1 esp_wifi_init

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.2 esp_wifi_set_mode

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.3 esp_wifi_get_mode

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.4 esp_wifi_start

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.5 esp_wifi_connect

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.6 esp_wifi_disconnect

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.7 esp_wifi_scan_start

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.8 esp_wifi_get_mac

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.9 esp_wifi_set_config

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

2.10 esp_wifi_get_config

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

三、AP模式

3.1 一般場景

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

3.2 Wi-Fi/LwIP初始階段

如上圖中 1.1\1.2\1.3\1.4 所示,分别

  • 初始化LwIP

    建立LwIP核心任務并初始化與LwIP相關的工作。

  • 初始化Wi-Fi事件處理

    Wi-Fi事件處理基于esp_event庫。Wi-Fi驅動程式會将事件發送到預設事件循環。應用程式可以在使用進行注冊的回調中處理這些事件

    esp_event_handler_register()

    。esp_netif元件還處理Wi-Fi事件,以提供一組預設行為。例如,當Wi-Fi站連接配接到AP時,esp_netif将自動啟動DHCP用戶端(預設情況下)。
// 建立系統事件任務并初始化應用程式事件的回調函數。
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 建立具有TCP / IP堆棧的預設網絡接口執行個體綁定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啟動階段

3.5 Wi-Fi連接配接階段

當有終端接入或斷開連接配接時,産生

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 一般場景

ESP32學習筆記(5)——WiFi接口使用(STA和AP模式)一、概述二、API說明三、AP模式四、STA模式五、掃描

4.2 Wi-Fi/LwIP初始階段

如上圖中 1.1\1.2\1.3\1.4 所示,分别

  • 初始化LwIP

    建立LwIP核心任務并初始化與LwIP相關的工作。

  • 初始化Wi-Fi事件處理

    Wi-Fi事件處理基于esp_event庫。Wi-Fi驅動程式會将事件發送到預設事件循環。應用程式可以在使用進行注冊的回調中處理這些事件

    esp_event_handler_register()

    。esp_netif元件還處理Wi-Fi事件,以提供一組預設行為。例如,當Wi-Fi站連接配接到AP時,esp_netif将自動啟動DHCP用戶端(預設情況下)。
// 建立系統事件任務并初始化應用程式事件的回調函數。
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 建立具有TCP / IP堆棧的預設網絡接口執行個體綁定基站。
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啟動階段

調用

esp_wifi_start()

以啟動Wi-Fi驅動程式。

Wi-Fi驅動程式将WIFI_EVENT_STA_START釋出到事件任務;然後,事件任務将執行一些正常操作,并将調用應用程式事件回調函數。

應用程式事件回調函數将WIFI_EVENT_STA_START中繼到應用程式任務。此時調用

esp_wifi_connect()

4.5 Wi-Fi連接配接階段

一旦

esp_wifi_connect()

被調用,Wi-Fi驅動程式将開始内部掃描/連接配接過程。

如果内部掃描/連接配接過程成功,将生成WIFI_EVENT_STA_CONNECTED。在事件任務中,它将啟動DHCP用戶端,該用戶端最終将觸發DHCP程序。

由于例如密碼錯誤,找不到AP等原因,Wi-Fi連接配接可能會失敗。在這種情況下,會出現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伺服器成功接收到IP位址,則将出現IP_EVENT_STA_GOT_IP,并且事件任務将執行正常處理。

在應用程式事件回調中,IP_EVENT_STA_GOT_IP被中繼到應用程式任務。對于基于LwIP的應用程式,此事件非常特殊,這意味着該應用程式已準備就緒,可以開始其任務,例如建立TCP / UDP套接字等。一個非常常見的錯誤是在收到IP_EVENT_STA_GOT_IP之前初始化套接字。接收IP之前,請勿開始與套接字相關的工作。

五、掃描

目前,

esp_wifi_scan_start()

僅在Station或Station + AP模式下支援該API。

掃描所有頻道後,将出現WIFI_EVENT_SCAN_DONE。

應用程式的事件回調函數通知應用程式任務已接收到WIFI_EVENT_SCAN_DONE。

esp_wifi_scan_get_ap_num()

調用以擷取在此掃描中找到的AP的數量。然後,它配置設定足夠的條目和調用

esp_wifi_scan_get_ap_records()

以擷取AP記錄。請注意,一旦

esp_wifi_scan_get_ap_records()

被調用,Wi-Fi驅動程式中的AP記錄将被釋放。

esp_wifi_scan_get_ap_records()

一次掃描完成事件請勿調用兩次。如果

esp_wifi_scan_get_ap_records()

在發生掃描完成事件時未調用,則不會釋放由Wi-Fi驅動程式配置設定的AP記錄。是以,請確定調用

esp_wifi_scan_get_ap_records()

,但隻能調用一次。

/* 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驅動程式

    ESP32 開發筆記(三)源碼示例 19_WIFI_STA 建立STA站模式連接配接路由器

    ESP32 開發筆記(三)源碼示例 15_WIFI_AP 建立軟AP示例

    ESP32 開發筆記(三)源碼示例 14_WIFI_Scan 附近WIFI信号掃描示例

繼續閱讀