ESP32 之 ESP-IDF 教学(十一)WiFi篇—— WiFi两种模式

本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》,讲解如何使用 ESP-IDF 构建 ESP32 程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨!

↓↓↓通过下方对话框进入专栏目录页↓↓↓
CSDN 请求进入目录       _ O x

是否进入ESP32教学导航(基于ESP-IDF)?

       确定

一、ESP32 WIFI介绍

Wi-Fi 库支持配置及监控 ESP32 Wi-Fi 连网功能。

支持配置:

  • 基站模式(即 STA 模式或 Wi-Fi 客户端模式),此时 ESP32 连接到接入点 (AP)。

  • AP 模式(即 Soft-AP 模式或接入点模式),此时基站连接到 ESP32。

  • AP-STA 共存模式(ESP32 既是接入点,同时又作为基站连接到另外一个接入点)。

  • 上述模式的各种安全模式(WPA、WPA2 及 WEP 等)。

  • 扫描接入点(包括主动扫描及被动扫描)。

  • 使用混杂模式监控 IEEE802.11 Wi-Fi 数据包。

二、WiFi 的启动(STA 及 AP 模式)

1. WiFi STA 模式(连接到其他设备的热点)

(1) 步骤及API简介

① 初始化nvs_flash
② 初始化esp_netif
③ 创建事件循环event_loop —— event_loop是esp32库的一种事件处理模式,中文曰“事件循环”
④ 初始化、配置WiFi并启动
⑤ 事件处理(使用第③步创建的事件循环)

(2) 代码示例

① 初始化nvs_flash

#include <nvs_flash.h>

...
	...
	/*尝试初始化一次nvs_flash*/
    esp_err_t err = nvs_flash_init();
    if(err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND){
    /*第一次初始化失败, 检查错误, 处理错误, 重新初始化*/
        nvs_flash_erase();
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
    ...
...

② 初始化esp_netif

#include "esp_netif.h"
____________

esp_netif_t *pEsp_wifi_netif;

esp_netif_init();

/*创建wifi sta模式的默认netif, 返回一个指针*/
pEsp_wifi_netif = esp_netif_create_default_wifi_sta();

/* (非必须) 这行代码是修改ESP32的主机名, 即WiFi设备名, 连接其他WiFi时显示的名称 */
esp_netif_set_hostname(esp_wifi_netif, "Augtons");// 不建议使用汉字

上述代码第 6 行函数esp_netif_create_default_wifi_sta()是必要的,只不过函数返回值可以在不需要的时候忽略(本文我们借助这个创建得到的esp_netif对象来修改了主机名为Augtons,因此保留了这个返回值)

③ 初始化event_loop

#include "esp_event.h"
_____________________

esp_event_loop_create_default();

esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
									想要用于处理事件的函数, NULL, &wifi_handler);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
									想要用于处理时间的函数, NULL, &ip_handler));

代码解析:

  • esp_event_loop_create_default(); 函数用于创建一个默认的事件循环(同一个esp32程序中可以有多个event_loop,这里使用默认的事件循环)
  • esp_event_handler_instance_register() 函数解析见下:
函数名 esp_event_handler_instance_register()
函数原型 esp_err_t esp_event_handler_instance_register(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg, esp_event_handler_instance_t *instance)
含义 将事件处理程序的实例注册到默认循环。
返回值 esp_err_t
参数 event_base类型为:esp_event_base_t;表示 事件基,代表事件的大类(如WiFi事件,IP事件等)
event_id类型为:int32_t;表示事件ID,即事件基下的一个具体事件(如WiFi连接丢失,IP成功获取)
event_handler类型为:esp_event_handler_t;表示一个handler函数(模板请见第⑤步
*event_handler_arg类型为:void;表示需要传递给handler函数的参数
*instance类型为:esp_event_handler_instance_t指针;**[输出]**表示此函数注册的事件实例对象,用于生命周期管理(如删除unrigister这个事件handler)

④ 初始化、配置WiFi并启动

注意:下文的代码实例只展示了最简单的wifi连接代码,实际上第4行配置的结构体下的sta还有很多成员,这里不再细讲

#include "driver/wifi.h"
/* 第一步, WiFi初始化 */
/*
	这个宏 WIFI_INIT_CONFIG_DEFAULT() 可以初始化一 			
	个wifi_init_config(wifi初始化配置)结构体为默认值
*/
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&wifi_init_config);

/* 第二步, WiFi配置 */
wifi_config_t wifi_config = {
    .sta = {
        .ssid = "WiFi_SSID",   // wifi名(ssid)
        .password = "password",// wifi密码
    }
};
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_set_mode(WIFI_MODE_STA);  // 设置工作模式

/* 第三步, 启动WiFi */
esp_wifi_start();

⑤ 事件处理(使用第③步创建的事件循环)

这里这个函数就是第 ③ 步注册的事件循环handler函数。
handler函数模板如下:

void xxx(void *arg, esp_event_base_t eventBase, int32_t eventID, void *eventData) {
/*
	函数参数一:arg。表示传递给handler函数的参数,在第三步register函数里声明
	函数参数二,eventBase,表示事件基,详见第三步函数esp_event_handler_instance_register()解析
	函数参数三:eventID,表示事件ID,详见第三步函数esp_event_handler_instance_register()解析
	函数参数四,表示传递给这个事件的数据。例如:
		例如事件基 IP_EVENT 下的 IP_EVENT_STA_GOT_IP 事件会把获取到的IP地址传递过来,见下方示例。
*/
}

下面为WiFi事件处理的一个简单的handler函数。
处理的事件:

事件基 事件 含义
WiFi_EVENT WIFI_EVENT_STA_START WiFi启动成功
WiFi_EVENT WIFI_EVENT_STA_DISCONNECTED WiFi连接断开/连接失败
IP_EVENT IP_EVENT_STA_GOT_IP WiFi连接成功并获取到IP地址
void wifi_event_handler(void *arg, esp_event_base_t eventBase, int32_t eventID, void *eventData){
    ip_event_got_ip_t *ip = eventData;
    if(eventBase == WIFI_EVENT){
        switch (eventID) {
            default:    break;
            case WIFI_EVENT_STA_START:
                printf("WIFI_STARTED!\n");
                esp_wifi_connect(); break;
            case WIFI_EVENT_STA_DISCONNECTED:
                ESP_LOGI("user", "Connect failed\n"); 
                esp_wifi_connect();	break;

        }
    }else if(eventBase == IP_EVENT){
        if(eventID == IP_EVENT_STA_GOT_IP){
            printf("connected, got ip: "IPSTR"\n", IP2STR(&ip->ip_info.ip));
            
            //dosomething
            //例如:使用任务通知xTaskNotifyGive(任务句柄);来唤醒一个任务等
            
        }
    }
}

WIFI_EVENT_STA_START发生即WiFi启动成功之后,我们就连接WiFi。
WIFI_EVENT_STA_DISCONNECT发生即WiFi断开连接/连接失败后,我们就尝试重新连接
IP_EVENT_STA_GOT_IP发生即WiFi连接成功并获取到IP地址之后,我们就在屏幕上打印出获取到的IP地址
printf("connected, got ip: "IPSTR"\n", IP2STR(&ip->ip_info.ip));
然后做其他的事情

2. WiFi AP 模式(ESP32作为热点供其他设备链接)

AP模式的WiFi与STA模式极其类似:
不同的是,在进行WiFi配置时wifi_config_t结构体用法会改变。

wifi_config_t wifi_config = {
    .ap = {
        .ssid = "your_ssid",
        .ssid_len = strlen("your_ssid"),
        .channel = EXAMPLE_ESP_WIFI_CHANNEL,
        .password = "password",
        .max_connection = EXAMPLE_MAX_STA_CONN,
        .authmode = WIFI_AUTH_WPA_WPA2_PSK
    },
};

剩余的代码,读者可以参考 ESP-IDF 的官方示例

三、STA模式下扫描外部WiFi

1. 步骤

在上一步WiFi启动之后,我们就能进行扫描附近的WiFi了
这里需要了解 3 个相关的API函数
esp_wifi_scan_start()
esp_wifi_scan_get_ap_num()
esp_wifi_scan_get_ap_records()

函数名 esp_wifi_scan_start()
函数原型 esp_err_t esp_wifi_scan_start(const wifi_scan_config_t *config, bool block)
含义 开始扫描WiFi,扫描附近所有可用的AP。
返回值 esp_err_t
参数 *config类型为:const wifi_scan_config_t指针;表示一个用于配置扫描参数的结构体的指针
   【此参数可为NULL】NULL表示扫描全部WiFi

block类型为:bool;表示是否要阻塞。为true表示此函数将阻塞直到扫描完成。为false表示此函数将要立即返回(扫描结束通过事件循环来传递结果)

函数名 esp_wifi_scan_get_ap_num()
函数原型 esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number)
含义 获取扫描到的AP个数
返回值 esp_err_t
参数 *number类型为:uint16_t指针;表示用于储存扫描个数的变量的指针
函数名 esp_wifi_scan_get_ap_records()
函数原型 esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records)
含义 获取扫描到的AP
返回值 esp_err_t
参数 *number类型为:uint16_t【既可输入又可输出】输入读取扫描记录的期望数量,例如预先分配的数组长度。输出实际扫描到的AP个数
*ap_records类型为:wifi_ap_record_t指针;表示存放扫描结果的数组

2. 代码示例


TaskHandle_t wifi_scan_handle;	
// 上边的这个变量储存了创建的wifi扫描任务句柄
// 对应下边示例的函数 task_wifi_scan(void *arg)


void wifi_event_handler(void *arg, esp_event_base_t eventBase, int32_t eventID, void *eventData){
    
    ...
    
    if(eventBase == WIFI_EVENT){
        if(eventID == WIFI_EVENT_STA_START){
            // printf("WiFi 已启动");

			// 向WiFi扫描任务发送任务通知,通知WiFi启动成功,可以开始扫描了 (这里的任务通知相当于一个二值信号量)
            xTaskNotifyGive(wifi_scan_handle);	
        }
    }
}


void task_wifi_scan(void *arg){
	// 等待来自event_handler发来的任务通知,在等到通知之前,此任务进入阻塞状态。知道接收到WiFi启动成功的通知之后才开始往下执行。
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    uint16_t num;
    wifi_ap_record_t records[8];

    esp_wifi_scan_start(NULL, true);
    esp_wifi_scan_get_ap_num(&num);
    if(num > 8){
        num = 8;	// if 的目的是如果扫描到了8个以上的WiFi,则只保留前8个
    }
    esp_wifi_scan_get_ap_records(&num, records);
    if(num > 8){
        num = 8;	// if 的目的同上
    }

	// 这个for循环是用来将RSSI信号强度,转换成信号等级。
	// 如 rssi_level == 5 时,表示WiFi满格
    for(int i = 0; i < num; i++){
        uint8_t rssi_level = 0;
        switch (records[i].rssi) {
            case -100 ... -88:
                rssi_level = 1; break;
            case -87 ... -77:
                rssi_level = 2; break;
            case -76 ... -66:
                rssi_level = 3; break;
            case -65 ... -55:
                rssi_level = 4; break;
            default:
                if(records[i].rssi < -100){
                    rssi_level = 0;
                }else{
                    rssi_level = 5;
                }
                break;
        }

	// 逐条打印扫描到的WiFi
        printf("—————【第 %2d 个WiFi】———————\n", i+1);
        printf("WiFi名称: %s\n", records[i].ssid);
        printf("信号强度: %d格\n", rssi_level);
        printf("WiFi: 安全类型: %d\n\n", records[i].authmode);
    }

    vTaskDelete(NULL);
}

运行结果图:
在这里插入图片描述

版权声明:本文为CSDN博主「Augtons正(单片机)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_50064262/article/details/119999907

Augtons正(单片机)

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

【导航】ESP32 教学篇目录 【快速跳转】

本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》 讲解如何使用 ESP-IDF 构建 ESP32 的WIFI及连接的程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨&#xf

CH579(M0内核)中断向量表偏移处理

通常情况下,为了产品后续的升级,程序都会分为两部分BootloaderApp,因为有两个程序,所以需要对中断向量表进行处理,否则,当程序已经跳转到app中运行,当中断产生的时候,响应函数仍然是Bootloader的中断函数,而不是app的 在S