玩转ESP32(2):WIFI的代码实现
ESP32作为一款WIFI+蓝牙芯片,WIFI的实现是其最基本的功能,而在ESP32中,利用WIFI可以实现STA、AP、STA+AP这三种方式。
STA代码实现
首先来看一个最简单的实现WIFI sta的例子。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#define EXAMPLE_WIFI_SSID "HUAWEI001"
#define EXAMPLE_WIFI_PASS "12345678"
static const char *TAG = "espressif";
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
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) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
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() );
}
void app_main()
{
ESP_ERROR_CHECK(nvs_flash_init());
initialise_wifi();
}
上述例子主要实现连接SSID为espressif的路由器,并获得相应的IP。SSID和PASSWORD可以根据自己的路由信息进行更改,在拿到IP之后我们就可以进行相应的网络操作(TCP/UDP)。
其中:
STA代码详解
现在,开始对上面的代码进行详细分析。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
上面是在实现WIFI sta所需要的头文件,其中前三个是freertos相关的头文件,后面几个都是espressif所定义的头文件,其中esp_system.h是系统相关头文件;esp_wifi.h是WIFI相关头文件;esp_event_loop.h是事件相关头文件。esp_log.h是espressif格式化输出的头文件。
#define EXAMPLE_WIFI_SSID "HUAWEI001"
#define EXAMPLE_WIFI_PASS "12345678"
static const char *TAG = "espressif";
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
上面是一些参数的定义,前面两个为ssid和密码,需要根据自己的路由器和密码进行更改。第3个是格式化输出的前缀,可以自由更改。wifi_event_group是我们定义的事件组,事件组主要是考虑到很多网络操作需要在我们连接到路由器并拿到IP之后才能进行。CONNECTED_BIT是我们定义的事件组Bit位,在此DEMO种只需要WIFI连接这一种事件。
*
Note.
事件组标志位和WIFI的事件组并不是一个东西,事件组标志位类似于原子操作,防止因为顺序错乱而导致程序问题。而WIFI事件组只是WIFI在连接过程中的某种状态。
*
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
WIFI在esp32中的连接是通过几组不同的事件处理来实现的,上述代码就是在作为STA连接WIFI中需要进行处理的事件。其中SYSTEM_EVENT_STA_START为初始事件状态,在此事情状态中,调用esp_wifi_connect()操作会连接到路由器(AP)上,连接成功后,事件状态位会变为SYSTEM_EVENT_STA_CONNECTED,此时会调用DHCP进行请求IP,在拿到IP之后事件状态会变更为SYSTEM_EVENT_STA_GOT_IP。在这里我们使用了xEventGroupSetBits,设置事件组标志,后续如果有需要在拿到IP后才进行的操作只需要调用xEventGroupWaitBits就可以避免在没有拿到IP之前进行网络交互操作。
在WIFI因为某些原因断开后,事件标志位变为SYSTEM_EVENT_STA_DISCONNECTED,此时需要调用esp_wifi_connect()重新连接,并清除事件组标志位。
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
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) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
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() );
}
上述代码是实现初始化WIFI的核心代码,其中tcpip_adapter_init()用来初始化TCP/IP协议栈,xEventGroupCreate()用来创建事件组标志。esp_event_loop_init(event_handler, NULL)用来初始化事件event_handler,回调event_handler即我们刚刚处理的各种WIFI事件。ESP_ERROR_CHECK用来检查返回值是否为ESP_OK。WIFI_INIT_CONFIG_DEFAULT()指定需要初始化底层的参数信息,esp_wifi_init用上述信息来初始化WIFI硬件。esp_wifi_set_storage设置连接的WIFI信息(SSID和PASSWORD)保存在哪里,可选的有RAM和FLASH。wifi_config是WIFI连接时的配置信息,作为STA时只需要考虑sta的参数信息,上述代码只是制定了最基本的ssid和password信息,除此之外,还可以指定bssid和channel等相关信息。ESP_LOGI是espressif的格式化输出信息,其他还包括ESP_LOGE(错误信息)、ESP_LOGD(调试信息)。esp_wifi_set_mode设置WIFI模式,除了WIFI_MODE_STA以外还包括WIFI_MODE_AP,WIFI_MODE_APSTA。esp_wifi_set_config设置STA模式的配置信息。esp_wifi_start()根据当前配置信息开启WIFI。
void app_main()
{
ESP_ERROR_CHECK(nvs_flash_init());
initialise_wifi();
}
void app_main()是espressif工程的入口,类似与C语言中的main,nvs_flash_init是初始化NVS存储,initialise_wifi即初始化WIFI。在程序的后面,当需要实现其他功能时,使用vTaskCreate来创建即可。
至此,WIFI的整个连接过程结束,
AP实现
AP的实现与STA的实现很类似,只需要更改一些配置信息即可。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
const static char *TAG = "espressif";
static void initialise_wifi(void)
{
tcpip_adapter_init();
ESP_ERROR_CHECK( esp_event_loop_init(NULL, 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) );
wifi_config_t wifi_config = {
.ap = {
.ssid = "TestAp",
.authmode = WIFI_AUTH_OPEN,
.max_connection = 3,
.channel = 12,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.ap.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
initialise_wifi();
}
在设置AP时,相对STA,大体相似,只有一小部分需要更改为AP信息。
ESP_ERROR_CHECK( esp_event_loop_init(NULL, NULL) );
在AP模式下,可以不需要回调函数,当然也可以写回调来获取更多信息,可以参考esp_event.h中的定义。
wifi_config_t wifi_config = {
.ap = {
.ssid = "TestAp",
.authmode = WIFI_AUTH_OPEN,
.max_connection = 3,
.channel = 12,
},
};
在此DEMO中,我设置的SSID为TestAp,authmode是open,即不加密,如果设置成加密,还需要添加.password。max_connection为支持的最大连接数。channel即信道为12。其他参数可以参考esp_wifi_types.h中的wifi_ap_config_t定义。
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.ap.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &wifi_config) );
wifi_config.ap.ssid打印当前配置的AP的SSID信息, esp_wifi_set_mode(WIFI_MODE_AP)设置为AP模式,esp_wifi_set_config(WIFI_IF_AP, &wifi_config)按照当前的配置信息来设置AP。
运行之后,用手机连接测试SSID:TestAp,会出现如下界面:
AP模式也设置成功。