文章目录

  • 前言
  • STA模式
  • 首先验证一下例程
  • 例程都有什么东西
  • 事件标志组
  • 事件处理回调函数
  • 初始化函数


前言

上一篇我们部署了ESP8266的集成开发环境,这一篇将开始ESP8266最主要的功能——WiFi的部分。其他诸如GPIO等外设的部分放在实际需要的时候再弄,没必要单独列一篇出来。
WiFi下有两种主要模式:STA和AP模式,前者可连接现有的路由器,后者本身建立热点,用手机上的功能来说,STA模式就相当于WLAN,AP模式就相当于移动热点。

STA模式

我们参考examples/wifi/getting-started/station,建立一个自己的工程文件夹:
在VScode里直接复制粘贴即可

首先验证一下例程

右键在集成终端中打开,make menuconfig

配置好板子参数,注意一下,比hello_world例程多了一步:Example Configuration,里边是例程里涉及到的一些宏,这个例程下就是WiFi名称和密码等:

esp8266热点广告 esp8266创建wifi热点_#include


WiFi SSID:你的路由器的名称,即你手机上能搜到的那个WiFi名称。

WiFi Password:WiFi密码

Maximum retry:重连次数,不一定能一次连上,需要多连几次,默认即可

保存退出,然后make

esp8266热点广告 esp8266创建wifi热点_物联网_02

例程都有什么东西

/* WiFi station 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 "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "nvs.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_MAXIMUM_RETRY  CONFIG_ESP_MAXIMUM_RETRY

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi station";

static int s_retry_num = 0;

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:%s",
                 ip4addr_ntoa(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();

    ESP_ERROR_CHECK(esp_event_loop_create_default());

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    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 */

    if (strlen((char *)wifi_config.sta.password)) {
        wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
    }

    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() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
    vEventGroupDelete(s_wifi_event_group);
}

void app_main()
{
    ESP_ERROR_CHECK(nvs_flash_init());

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
}

头文件不用管,前面几个宏也不用管

事件标志组

事件标志组,通俗一点理解就是一组事件标志位,多个事件标志位组合成一个方便访问的变量。

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

s_wifi_event_group:事件标志组句柄,我们跳转到声明的位置,一个void 类型的指针,只是用来存储多个事件标志位而已(一个数据掰成N个位用)。

esp8266热点广告 esp8266创建wifi热点_esp8266热点广告_03


下边两个宏就是事件标志位,表示事件发生,因为整个过程需要一些时间,不能一直在这儿死等,所以等待标志位产生(类似8位机系统常见的轮询方式)。

static const char *TAG = "wifi station";

这句无实际意义,只是标注整个程序的名称而已

static int s_retry_num = 0;

这句是声明一个全局变量,重试次数,在每次发生重连的时候+1.

事件处理回调函数

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:%s",
                 ip4addr_ntoa(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

event_handler:事件处理回调函数,

event_base:事件类型

event_id:事件对应的ID

其余参数均为可选参数

这个事件回调函数里总共有两种类型的事件:WIFI_EVENT和IP_EVENT,分别是连接WiFi和获取IP事件。

这个回调函数应该是可以分开写的,分别写一个WIFI_EVENT和IP_EVENT的事件回调,但意义不大。

esp8266热点广告 esp8266创建wifi热点_IP_04


如果超过设定的重连次数,则更新事件标志位WIFI_FAIL_BIT。

esp8266热点广告 esp8266创建wifi热点_物联网_05


如果获取到IPv4,则更新事件标志位WIFI_CONNECTED_BIT。

初始化函数

void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();

    ESP_ERROR_CHECK(esp_event_loop_create_default());

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    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 */

    if (strlen((char *)wifi_config.sta.password)) {
        wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
    }

    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() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
    vEventGroupDelete(s_wifi_event_group);
}
s_wifi_event_group = xEventGroupCreate();//创建事件标志组
 tcpip_adapter_init();//初始化TCP/IP的协议栈
 ESP_ERROR_CHECK(esp_event_loop_create_default());//创建事件循环回调,处理系统事件,创建一个默认事件处理WiFi的连接。
 ESP_ERROR_CHECK();//检查函数返回结果,如果非ESP_OK则会打印相应的错误代码和错误信息。
 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
 ESP_ERROR_CHECK(esp_wifi_init(&cfg));//配置WiFi参数,

使用默认参数,打开Config项:

#define WIFI_INIT_CONFIG_DEFAULT() { \
    .event_handler = &esp_event_send, \
    .osi_funcs = NULL, \
    .qos_enable = WIFI_QOS_ENABLED,\
    .ampdu_rx_enable = WIFI_AMPDU_RX_ENABLED,\
    .rx_ba_win = WIFI_AMPDU_RX_BA_WIN,\
    .rx_ampdu_buf_num = WIFI_AMPDU_RX_AMPDU_BUF_NUM,\
    .rx_ampdu_buf_len = WIFI_AMPDU_RX_AMPDU_BUF_LEN,\
    .rx_max_single_pkt_len = WIFI_RX_MAX_SINGLE_PKT_LEN,\
    .rx_buf_len = WIFI_HW_RX_BUFFER_LEN,\
    .amsdu_rx_enable = WIFI_AMSDU_RX_ENABLED,\
    .rx_buf_num = CONFIG_ESP8266_WIFI_RX_BUFFER_NUM,\
    .rx_pkt_num = CONFIG_ESP8266_WIFI_RX_PKT_NUM,\
    .left_continuous_rx_buf_num = CONFIG_ESP8266_WIFI_LEFT_CONTINUOUS_RX_BUFFER_NUM,\
    .tx_buf_num = CONFIG_ESP8266_WIFI_TX_PKT_NUM,\
    .nvs_enable = WIFI_NVS_ENABLED,\
    .nano_enable = 0,\
    .wpa3_sae_enable = WIFI_WPA3_ENABLED, \
    .magic = WIFI_INIT_CONFIG_MAGIC\
};

好多项我也不知道是啥,暂时就按默认来吧。下面是结构体的详情:

/**
 * @brief WiFi stack configuration parameters passed to esp_wifi_init call.
 */
typedef struct {
    system_event_handler_t event_handler;           /**< WiFi event handler */
    void*                  osi_funcs;               /**< WiFi OS functions */
    uint8_t                qos_enable;              /**< WiFi QOS feature enable flag */
    uint8_t                ampdu_rx_enable;         /**< WiFi AMPDU RX feature enable flag */
    uint8_t                rx_ba_win;               /**< WiFi Block Ack RX window size */
    uint8_t                rx_ampdu_buf_num;        /**< WiFi AMPDU RX buffer number */
    uint32_t               rx_ampdu_buf_len;        /**< WiFi AMPDU RX buffer length */
    uint32_t               rx_max_single_pkt_len;   /**< WiFi RX max single packet size */
    uint32_t               rx_buf_len;              /**< WiFi RX buffer size */
    uint8_t                amsdu_rx_enable;         /**< WiFi AMSDU RX feature enable flag */
    uint8_t                rx_buf_num;              /**< WiFi RX buffer number */
    uint8_t                rx_pkt_num;              /**< WiFi RX packet number */
    uint8_t                left_continuous_rx_buf_num; /**< WiFi Rx left continuous rx buffer number */
    uint8_t                tx_buf_num;              /**< WiFi TX buffer number */
    uint8_t                nvs_enable;              /**< WiFi NVS flash enable flag */
    uint8_t                nano_enable;             /**< Nano option for printf/scan family enable flag */
    uint8_t                wpa3_sae_enable;         /**< WiFi WPA3 feature enable flag*/
    uint32_t               magic;                 /**< WiFi init magic number, it should be the last field */
} wifi_init_config_t;
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
 ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));//注册事件回调函数
 wifi_config_t wifi_config = {
 .sta = {
 .ssid = EXAMPLE_ESP_WIFI_SSID,
 .password = EXAMPLE_ESP_WIFI_PASS
 },
 };//设置WiFi名称和密码

esp8266热点广告 esp8266创建wifi热点_IP_06


翻译了一下上边的注释:

设置密码意味着站点将连接到所有安全模式,包括WEP/WPA。

但是,不建议使用这些模式。如果你的接入点不支持WPA2,这些模式可以通过注释下面一行来启用

意思是这句使用的WPA2方式加密,如果接入点不支持WPA2,就注释这一行,使用WPA模式加密即可。

esp8266热点广告 esp8266创建wifi热点_esp8266热点广告_07


这一堆就是设置WiFi模式以及刚才设置的WiFi密码,然后开启WiFi。

esp8266热点广告 esp8266创建wifi热点_物联网_08

顾名思义,等待事件标志组返回,也就是连接成功或者失败的标志,具体设置在前面说的事件标志位说了。

esp8266热点广告 esp8266创建wifi热点_#define_09


检查是哪个事件标志位生效了。

esp8266热点广告 esp8266创建wifi热点_IP_10


事件回调函数注销,事件标志组注销,事件完成。

此时就已经以STA模式连接上热点了。