本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》,讲解如何使用 ESP-IDF 构建 ESP32 程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨!
↓↓↓通过下方对话框进入专栏目录页↓↓↓
CSDN 请求进入目录 _ O x
是否进入ESP32教学导航(基于ESP-IDF)?
确定
- 一、ESP32 WIFI介绍
- 二、WiFi 的启动(STA 及 AP 模式)
- 1. WiFi STA 模式(连接到其他设备的热点)
- (1) 步骤及API简介
- (2) 代码示例
- 2. WiFi AP 模式(ESP32作为热点供其他设备链接)
- 三、STA模式下扫描外部WiFi
- 1. 步骤
- 2. 代码示例
Wi-Fi 库支持配置及监控 ESP32 Wi-Fi 连网功能。
支持配置:
-
基站模式(即 STA 模式或 Wi-Fi 客户端模式),此时 ESP32 连接到接入点 (AP)。
-
AP 模式(即 Soft-AP 模式或接入点模式),此时基站连接到 ESP32。
-
AP-STA 共存模式(ESP32 既是接入点,同时又作为基站连接到另外一个接入点)。
-
上述模式的各种安全模式(WPA、WPA2 及 WEP 等)。
-
扫描接入点(包括主动扫描及被动扫描)。
-
使用混杂模式监控 IEEE802.11 Wi-Fi 数据包。
① 初始化nvs_flash
② 初始化esp_netif
③ 创建事件循环event_loop
—— event_loop
是esp32库的一种事件处理模式,中文曰“事件循环”
④ 初始化、配置WiFi并启动
⑤ 事件处理(使用第③步创建的事件循环)
① 初始化nvs_flash
#include
...
...
/*尝试初始化一次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));
然后做其他的事情
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
的官方示例
在上一步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 指针;表示存放扫描结果的数组 |
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);
}
运行结果图:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)