·通过wifi_scan学习esp32wifi程序编写
在ESP32的设计开发中,我们必然会需要使用到wifi或ble功能,今天就讲解下如何将WIFI功能纳入到ESP32中来。
初始化WiFi环境
首先,WiFi子系统的初始化需要由我们自己来自行,当我们写自己的程序时,需要通过调用 esp_wifi_init() 方法 来完成 。
推荐的方式如下:
wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&config);
设置操作模式
ESP32可以是网络中的一个站点,也可以是其他设备的接入点。 请记住,当ESP32正在一个站,它可以连接到远程一个 ACCESS点(您的WiFi中心),当作为接入点时,其他WiFi站可以连接到ESP32(想想ESP32成为WiFi集线器)。通过我们的设定,我们可以选择我们的设备执行哪些操作模式的属性(站,接入点或站点接入点)。
这种设定是调用函数esp_wifi_set_mode()进行设定的,wifi_mode_t 其可具有 WIFI_MODE_NULL,WIFI_MODE_STA,WIFI_MODE_AP 或 WIFI_MODE_APSTA的值。
我们可以调用 esp_wifi_get_mode()来检索我们目前的模式。
扫描接入点
如果ESP32将要执行一个电台的角色,我们将需要连接到一个切入点。 我们可以要求列出我们可以使用的可用接入点尝试连接。 我们该用 esp_wifi_scan_start() 函数来 完成 。
WiFi扫描的结果存储在ESP32分配的动态存储器内部y,当我们调用 esp_wifi_scan_get_ap_records() 时,数据返回到我们 这也释放内部y分配的存储。我们称之为破坏性阅读。
扫描记录包含在包含 wifi_ap_record_t 结构 的一个实例 如下:
uint8_t bssid[6] uint8_t ssid[32] uint8_t primary wifi_second_chan_t second int8_t rssi wifi_auth_mode_t authmode
该 wifi_auth_mode_t 是以下选项中的一个:
• WIFI_AUTH_OPEN - 没有安全性。
• WIFI_AUTH_WEP - WEP安全性。
• WIFI_AUTH_WPA_PSK - WPA安全性。
• WIFI_AUTH_WPA2_PSK - WPA2安全。
• WIFI_AUTH_WPA_WPA2_PSK - WPA或WPA2安全性。
在发出开始执行扫描的请求后,我们将被通知扫描。
当 SYSTEM_EVENT_SCAN_DONE 事件发布 完成 。
事件数据包含找到的接入点的数量,我们可以通过调用esp_wifi_scan_get_ap_num()来得到。
我们希望在完成扫描之前自行取消扫描,我们可以调用esp_wifi_scan_stop()。
下面我们是一个完整的WIFI_SCAN的例子,通常,我们在事件处理程序中执行工作,当我们检测到扫描完成事件时,我们检索定位的接入点并记录其详细信息。
1 #include "freertos/FreeRTOS.h" 2 #include "freertos/task.h" 3 #include "freertos/event_groups.h" 4 #include "esp_wifi.h" 5 #include "esp_system.h" 6 #include "esp_event.h" 7 #include "esp_event_loop.h" 8 #include "esp_log.h" 9 #include "nvs_flash.h" 10 11 12 static EventGroupHandle_t wifi_event_group;//定义一个事件的句柄 13 const int SCAN_DONE_BIT = BIT0;//定义事件,占用事件变量的第0位,最多可以定义32个事件。 14 static wifi_scan_config_t scanConf = { 15 .ssid = NULL, 16 .bssid = NULL, 17 .channel = 0, 18 .show_hidden = 1 19 };//定义scanConf结构体,供函数esp_wifi_scan_start调用 20 21 static const char *TAG = "example"; 22 23 esp_err_t event_handler(void *ctx, system_event_t *event) 24 { 25 if (event->event_id == SYSTEM_EVENT_SCAN_DONE) { 26 xEventGroupSetBits(wifi_event_group, SCAN_DONE_BIT); //设置事件位 27 } 28 return ESP_OK; 29 } 30 31 static void initialise_wifi(void) //define a static function ,it's scope is this file 32 { 33 wifi_event_group = xEventGroupCreate(); //创建一个事件标志组 34 ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));//创建事件的任务 35 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//设置默认的wifi栈参数 36 ESP_ERROR_CHECK(esp_wifi_init(&cfg)); //初始化WiFi Alloc资源为WiFi驱动,如WiFi控制结构,RX / TX缓冲区,WiFi NVS结构等,此WiFi也启动WiFi任务。 37 ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));// Set the WiFi API configuration storage type 38 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));//Set the WiFi operating mode 39 ESP_ERROR_CHECK(esp_wifi_start()); 40 /* 41 * If mode is WIFI_MODE_STA, it create station control block and start station 42 * If mode is WIFI_MODE_AP, it create soft-AP control block and start soft-AP 43 * If mode is WIFI_MODE_APSTA, it create soft-AP and station control block and start soft-AP and station 44 */ 45 } 46 47 static void scan_task(void *pvParameters) 48 { 49 while(1) { 50 xEventGroupWaitBits(wifi_event_group, SCAN_DONE_BIT, 0, 1, portMAX_DELAY); //等待事件被置位,即等待扫描完成 51 ESP_LOGI(TAG, "WIFI scan doen"); 52 xEventGroupClearBits(wifi_event_group, SCAN_DONE_BIT);//清除事件标志位 53 54 uint16_t apCount = 0; 55 esp_wifi_scan_get_ap_num(&apCount);//Get number of APs found in last scan 56 printf("Number of access points found: %d\n", apCount); 57 if (apCount == 0) { 58 ESP_LOGI(TAG, "Nothing AP found"); 59 return; 60 }//如果apCount没有受到数据,则说明没有路由器 61 wifi_ap_record_t *list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount);//定义一个wifi_ap_record_t的结构体的链表空间 62 ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, list));//获取上次扫描中找到的AP列表。 63 int i; 64 printf("======================================================================\n"); 65 printf(" SSID | RSSI | AUTH \n"); 66 printf("======================================================================\n"); 67 for (i=0; i<apCount; i++) { 68 char *authmode; 69 switch(list[i].authmode) { 70 case WIFI_AUTH_OPEN: 71 authmode = "WIFI_AUTH_OPEN"; 72 break; 73 case WIFI_AUTH_WEP: 74 authmode = "WIFI_AUTH_WEP"; 75 break; 76 case WIFI_AUTH_WPA_PSK: 77 authmode = "WIFI_AUTH_WPA_PSK"; 78 break; 79 case WIFI_AUTH_WPA2_PSK: 80 authmode = "WIFI_AUTH_WPA2_PSK"; 81 break; 82 case WIFI_AUTH_WPA_WPA2_PSK: 83 authmode = "WIFI_AUTH_WPA_WPA2_PSK"; 84 break; 85 default: 86 authmode = "Unknown"; 87 break; 88 } 89 printf("%26.26s | % 4d | %22.22s\n",list[i].ssid, list[i].rssi, authmode); 90 }//将链表的数据信息打印出来 91 free(list);//释放链表 92 printf("\n\n");//换行 93 94 // scan again 95 vTaskDelay(2000 / portTICK_PERIOD_MS);//调用延时函数,再次扫描 96 //The true parameter cause the function to block until the scan is done. 97 ESP_ERROR_CHECK(esp_wifi_scan_start(&scanConf, 1));//扫描所有可用的AP。 98 } 99 100 101 } 102 103 int app_main(void) 104 { 105 nvs_flash_init();//初始化NVS flash storage 106 tcpip_adapter_init();//初始化i本机TCP/IP协议 107 initialise_wifi();//初始化wifi 108 109 xTaskCreate(&scan_task, "scan_task", 2048, NULL, 15, NULL);//创建扫描任务 110 111 ESP_ERROR_CHECK(esp_wifi_scan_start(&scanConf, 1)); //The true parameter cause the function to block until 112 //the scan is done. 113 return 0; 114 }
程序运行结果如下
处理WiFi事件
在作为WiFi设备运行的过程中,ESP32可能会发生某些事件需要知道。 这些可能对应用程序的运行很重要,我们知道不能让我们的应用程序块等待他们发生,因为我们不知道什么时候事情会发生,以及事件是否会发生。因此,我们应该定义一个回归函数,如果事件发生的话就被调用,该函数就是上例中的esp_event_loop_init(), 它注册一个功能当ESP32检测到某些类型的WiFi相关事件时调用。该回调函数被调用并传递一个丰富的数据结构,其中包括事件的类型和对应于该事件的关联数据。
导致回调发生的事件类型:
• 我们连接到接入点
• 我们 从一个接入点 断开连接
• 授权模式已更改
• 当我们处于接入点模式时,与我们连接的一个站点
• 当我们处于接入点模式时,站与我们断开连接
• SSID扫描完成
当ESP32 WiFi环境运行时,它会在某些情况下发布“事件”发生WiFi级别,例如新站连接。 我们可以注册一个回调发布事件时调用的函数。
回调识别函数是:
esp_err_t eventHandler(void *ctx, system_event_t *event) { // Handle event here ... return ESP_OK; }
通常我们需要include下面.h文件
• #include <esp_event.h>
• #include <esp_event_loop.h>
• #include <esp_wifi.h>
• #include <esp_err.h>
要注册回调函数,我们调用:esp_event_loop_init(eventHandler,NULL);
如果我们希望随后更改与我们的WiFi处理相关联的事件处理程序我们可以
esp_event_loop_set_cb(eventHandler,NULL);
当事件处理程序被调用时,事件参数将被填充事件。
此参数的数据类型是一个“system_event_t”,让我们现在看看传递给system_event_t中的事件处理程序的两个属性数据结构,其中包含:
system_event_id_t event_id
system_event_info_t event_info
这里event_id描述检测事件的种类,同时event_info包含基于在 event_id标识的类型事件的具体细节 。
• EVENT_ID - 一个枚举类型,有下列潜在价值:
◦ SYSTEM_EVENT_WIFI_READY - ESP32的WiFi我 已经准备好。
◦ SYSTEM_EVENT_SCAN_DONE - 接入点扫描完毕。 该scan_done 数据字段是有效的访问。
◦ SYSTEM_EVENT_STA_START - 作为一个station开始。
◦ SYSTEM_EVENT_STA_STOP - 停止作为一个station。
◦ SYSTEM_EVENT_STA_CONNECTED - 连接到的接入点作为站。所连接的数据字段是有效的,可以进行访问。
◦ SYSTEM_EVENT_STA_DISCONNECTED - 从ACCESS点作为一个站断开连接,断开连接的数据字段是可以有效的访问的。
◦SYSTEM_EVENT_STA_AUTHMODE_CHANGE - 身份验证模式已经改变。该auth_change 数据字段是可以有效的访问的。
◦SYSTEM_EVENT_STA_GOT_IP - 得到了 来自接入 分配的 IP地址指向我们连接到一个站点。 该 got_ip 数据字段是可以有效访问的。
◦ SYSTEM_EVENT_AP_START - 开始作为接入点。
◦SYSTEM_EVENT_AP_STOP - 停止作为接入点。
◦SYSTEM_EVENT_AP_STACONNECTED - 连接到一个站作为接入点。 该 sta_connected 数据字段是可以有效的访问的。
◦ SYSTEM_EVENT_AP_STADISCONNECTED - 从正在接入点断开一个站, 该 sta_disconnected 数据字段是可以有效的访问的。
◦ SYSTEM_EVENT_AP_PROBEREQRECVED - 作为接入点收到的探测请求,该 ap_probereqrecved 数据字段是可以有效的访问。
• event_info - 这是 键控关闭不同 的 数据类型 的C语言联合该事项标识 。其中包含的不同结构是:
结构体 领域 事件
system_event_sta_connected_t connected SYSTEM_EVENT_STA_CONNECTED
system_event_sta_disconnected_t disconnected SYSTEM_EVENT_STA_DISCONNECTED
system_event_sta_scan_done_t scan_done SYSTEM_EVENT_SCAN_DONE
system_event_sta_ authmode_change_t auth_change SYSTEM_EVENT_STA_AUTHMODE_CHANGE
system_event_sta_got_ip_t got_ip SYSTEM_EVENT_STA_GOT_IP
system_event_ap_staconnected_t sta_connected SYSTEM_EVENT_AP_STACONNECTED
system_event_ao_stadisconnected_t ap_probereqrecved SYSTEM_EVENT_AP_PROBEREQRECVED
这些数据结构包含与接收的事件类型有关的信息。
system_event_sta_connected_t
此数据类型与 SYSTEM_EVENT_STA_CONNECT 事件 相关联 。
uint8_t ssid[32] //SSID 是,这是我们连接WiFi网络名称。
uint8_t ssid_len //该 ssid_len 是在包含名称 SSID 字段中 的字节数 。
uint8_t bssid[6] //BSSID 是MAC地址的接入点
uint8_t channel //信道 是用于该连接的无线信道
wifi_auth_mode_t authmode //该authmode 是连接过程中使用的安全身份验证模式。
system_event_sta_disconnected_t
此数据类型与 SYSTEM_EVENT_STA_DISCONNECTED 事件 相关联 。
uint8_t ssid[32]
uint8_t ssid_len
uint8_t bssid[6]
uint8_t reason
原因代码reason是为什么我们断开连接的指示。 符号定义为每个数字原因,下面是原因代码:
• WIFI_REASON_UNSPECIFIED - 1
• WIFI_REASON_AUTH_EXPIRE - 2
• WIFI_REASON_AUTH_LEAVE - 3
• WIFI_REASON_ASSOC_EXPIRE - 4
• WIFI_REASON_ASSOC_TOOMANY - 5
• WIFI_REASON_NOT_AUTHED - 6
• WIFI_REASON_NOT_ASSOCED - 7
• WIFI_REASON_ASSOC_LEAVE - 8
• WIFI_REASON_ASSOC_NOT_AUTHED - 9
• WIFI_REASON_DISASSOC_PWRCAP_BAD - 10
• WIFI_REASON_DISASSOC_SUPCHAN_BAD - 11
• WIFI_REASON_IE_INVALID - 13
• WIFI_REASON_MIC_FAILURE - 14
• WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT - 15
• WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT - 16
• WIFI_REASON_IE_IN_4WAY_DIFFERS - 17
• WIFI_REASON_GROUP_CIPHER_INVALIð - 18
• WIFI_REASON_PAIRWISE_CIPHER_INVALID - 19
• WIFI_REASON_AKMP_INVALID - 20
• WIFI_REASON_UNSUPP_RSN_IE_VERSION - 21
• WIFI_REASON_INVALID_RSN_IE_CAP - 22
• WIFI_REASON_802_1X_AUTH_FAILED - 23
• WIFI_REASON_CIPHER_SUITE_REJECTED - 24
• WIFI_REASON_BE ACON_TIMEOUT - 200
• WIFI_REASON_NO_AP_FOUND - 201
• WIFI_REASON_AUTH_FAIL - 202
• WIFI_REASON_ASSOC_FAIL - 203
• WIFI_REASON_HANDSHAKE_TIMEOUT - 204
system_event_sta_scan_done_t
此数据类型与 SYSTEM_EVENT_SCAN_DONE 事件 相关联 。
uint32_t status
uint8_t number
uint8_t scan_id
system_event_authmode_change_t
此数据类型与 SYSTEM_EVENT_STA_AUTHMODE_CHANGE 事件 相关联 。
wifi_auth_mode_t old_mode
wifi_auth_mode_t new_mode
system_event_sta_got_ip_t
此数据类型与 SYSTEM_EVENT_STA_GOT_IP 事件 相关联 。
tcpip_adapter_ip_info_t ip_info
所述ip_info元件是 包含三个 一 tcpip_adapter_ip_info_t 的一个实例
字段:
• IP -The IP地址。
• netmask - 网络掩码。
• gw- 通信网关。
所有这三个领域是 ip4_addr_t 这是一个IP的32位代表地址。 在开发期间,您可能需要考虑记录的IP地址设备。 为此,您可以 使用:
ESP_LOGD(tag, "Got an IP: " IPSTR, IP2STR(&event->event_info.got_ip.ip_info.ip));
system_event_ap_staconnected_t
此数据类型与 SYSTEM_EVENT_AP_STACONNECTED 事件 相关联 。
uint8_t mac [6]
uint8_t aid
system_event_ap_stadisconnected_t
此数据类型与 SYSTEM_EVENT_AP_STADISCCONNECTED 事件 相关联 。
uint8_t mac[6]
uint8_t aid
system_event_ap_probe_req_rx_t
此数据类型与 SYSTEM_EVENT_AP_PROBREQRECVED 事件 相关联 。
int rssi
uint8_t mac [6]
如果我们启用正确的日志记录级别,我们可以看到事件到达及其内容。
例如:
D (2168) event: SYSTEM_EVENT_STA_CONNECTED, ssid:RASPI3, ssid_len:6, bssid:00:00:13:80:3d:bd, channel:6, authmode:3 V (2168) event: enter default callback V (2174) event: exit default callback
和
D (9036) event: SYSTEM_EVENT_STA_GOTIP, ip:192.168.5.62, mask:255.255.255.0, gw:192.168.5.1 V (9036) event: enter default callback I (9037) event: ip: 192.168.5.62, mask: 255.255.255.0, gw: 192.168.5.1 V (9043) event: exit default callback
站配置
当我们想用到ESP32作为wifi站,之后我们将认识到,在任何一个时间, 它只能连接到一个接入点。 换句话说,该装置在同一时间连接到两个或更多个接入点。
这是我们希望得到相关的接入点的标识设置数据结构称为 wifi_sta_config_t。该 wifi_sta_config_t 包含:
char ssid[32] char password[64] bool bssid_set uint8_t bssid[6]
该结构中包含两个非常重要的领域“SSID” 和“ password”。 SSID 字段是接入点的SSID,这是我们将连接到的AP的名称。password字段是密码的明文值将用于将我们的设备验证到目标接入点以允许连接。
对于这样的一个例子初始化结构可能是:
wifi_config_t staConfig = { .sta = { .ssid="<access point name>", .password="<password>", .bssid_set=false } };
一旦我们填写了这个结构的实例,我们就可以指示ESP32的使用了。
esp_wifi_set_config(WIFI_IF_STA, (wifi_config_t *)&staConfig);
我们之前讲到的模式设置也可以使用了
esp_wifi_set_mode(WIFI_MO DE_STA)
或
esp_wifi_set_mode(WIFI_MODE_APSTA)
启动WiFi环境
当WiFi声明通过后,可能会问的问题是“什么时候WiFi可以使用?”通常对于ESP32我们要告诉它它是一个站点或接入点,然后配置它参数如连接到哪个接入点(如果是站)或自己的接入点身份应该是(如果它将成为接入点)。 鉴于这些是一系列的步骤,我们实际上不希望ESP32执行这些任务直到我们完成了所有的设置。 例如,如果我们启动一个ESP32并设置它是一个接入点,如果他立刻作为接入点的话,可能会出现错误,或者暂时作为错误的接入点存在,这是我们不希望见到的。因此,我们必须学习最后的命令指示到WiFi子系统开始工作。 那个命令是esp_wifi_start()。在调用这个函数之前,我们正在做的是建立环境。只有通过调用 esp_wifi_start() ,WiFi子系统才开始做任何实际工作。同样的有个相应的命令esp_wifi_stop(),其可以停止WIFI子系统。
连接到接入点
通过前面的讲解,已经基本讲解了wifi的基本操作,ES32接入接入点AP的实例请看下一篇文章
关于esp32的省电模式的WiFi连接
相关知识:wifi相关的API接口