ESP32学习笔记07-wifi连接

esp32 wifi连接网络

本文参考:ESP32学习入门:WiFi连接网络 - 知乎 (zhihu.com)

  • AP: 路由器的角色

  • sta:就是手机连接wifi的手机角色

  • sta/ap 模式

  • esp32 是一套2.4G的WiFi,和蓝牙4.2

  • esp32 硬件资源如下图:

在这里插入图片描述

WiFi是在TCP/IP协议的基础之上实现的2.4GHz的一种通信方式,不同的实现标准对应的的频带和最大速率不一样。

标准频率最大速率
802.11b2.4GHz11Mbps
802.11a5GHz54Mbps
802.11g2.4GHz54Mbps
802.11n2.4GHz,5GHz450Mbps
802.11ac5GHz1300Mbps
事件名称触发条件
WIFI_EVENT_STA_START如果调用函数 esp_wifi_start() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将初始化 LwIP 网络接口 (netif)。通常,应用程序的事件回调函数需调用 esp_wifi_connect() 来连接已配置的 AP。
WIFI_EVENT_STA_STOP如果调用函数 esp_wifi_stop() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将进行释放 station IP 地址、终止 DHCP 客户端服务、移除 TCP/UDP 相关连接并清除 LwIP station netif 等动作。此时,应用程序的事件回调函数通常不需做任何响应。
WIFI_EVENT_STA_CONNECTED如果调用函数 esp_wifi_connect() 后接收到返回值 ESP_OK,且 station 已成功连接目标 AP,则将产生此连接事件。接收到此事件后,事件任务将启动 DHCP 客户端服务并开始获取 IP 地址。此时,Wi-Fi 驱动程序已准备就绪,可发送和接收数据。如果您的应用程序不依赖于 LwIP(即 IP 地址),则此刻便可以开始应用程序开发工作。但是,如果您的应用程序需基于 LwIP 进行,则还需等待 got ip 事件发生后才可开始。
IP_EVENT_STA_GOT_IP当 DHCP 客户端成功从 DHCP 服务器获取 IPV4 地址或 IPV4 地址发生改变时,将引发此事件。此事件意味着应用程序一切就绪,可以开始任务(如:创建套接字)。

WiFi事件的处理流程

在这里插入图片描述

代码

/* 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_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"


#include "lwip/netif.h"
#include "lwip/sockets.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 "TP-LINK_45A1"
#define EXAMPLE_ESP_WIFI_PASS "chejia12"
//最大重试次数
#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)
{
    //第一步 esp_wifi_start() 产生WIFI_EVENT_STA_START事件
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        //第二步连接WiFi
        esp_wifi_connect();
    }
    //第三步:第二步执行成功以后 产生 WIFI_EVENT_STA_CONNECTED 连接成功后开始获取IP
    //                否则产生 WIFI_EVENT_STA_DISCONNECTED 连接失败事件
    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");
    }
    //获取ip地址成功以后产生 IP_EVENT_STA_GOT_IP 事件 ,表示连接成功
    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:" IPSTR, IP2STR(&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();
//  初始化TCP/IP协议栈
    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    //  初始化WiFi底层驱动
    esp_netif_create_default_wifi_sta();

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

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    // 注册用户层的事件以及相关事件处理函数
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    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 */
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false},
        },
    };
    // 配置好WiFi的工作模式 sta
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    // 启动WiFi
    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");
    }

    /* The event will not be processed after unregister */
    // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    // vEventGroupDelete(s_wifi_event_group);
}
static void tcp_client_task(void *pvParameters);
void app_main(void)
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
    xTaskCreate(tcp_client_task,"tcp_lient",4096,NULL,5,NULL);
}
static void tcp_client_task(void *pvParameters)
{
    char rx_buffer[128];
    char host_ip[] = "192.168.1.2";
    int  port = 8080;
    int  addr_family = 0;
    int  ip_protocol = 0;
    static const char *payload = "Hello, I am ESP32 Client.";
    while (1) {

        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(port);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;

        int sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, port);

        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
        if (err != 0) {
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Successfully connected");

        err = send(sock, payload, strlen(payload), 0);
		if (err < 0) {
			ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
			break;
		}

        while (1) {

            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
            }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}


代码解读

WiFi联网逻辑流程

在这里插入图片描述

  1. 创建一个事件组,用于通知联网失败还是成功
  2. 初始化tcp/ip协议栈
  3. 创建一个默认的事件循环
  4. 创建一个默认的 sta
  5. 初始化wifi
  6. 注册任意id的事件处理句柄
  7. 注册一个获取IP地址成功的事件处理句柄
  8. 配置好WiFi的工作模式 sta
  9. //设置ap的ssid 密码等信息
  10. 启动wifi-进入WIFI_EVENT_STA_START 事件循环
  11. 主任务等待事件处理循环,完后连接,获取ip成功

事件循环

  1. 第一步 esp_wifi_start() 产生 WIFI_EVENT_STA_START 事件

  2. 第二步连接WiFi

  3. 第三步:第二步执行成功以后 产生 WIFI_EVENT_STA_CONNECTED 连接成功后开始获取IP

    否则产生 WIFI_EVENT_STA_DISCONNECTED 连接失败事件,连接失败后启动重连机制

  4. 连接成功以后自动进入获取ip的事件,获取ip的事件底层自己处理

  5. 获取成功以后进入 IP_EVENT_STA_GOT_IP 模式,通知主任务

相关api介绍

xEventGroupCreate();初始化事件组
esp_netif_init()初始化TCP/IP协议堆栈
esp_event_loop_create_default()创建默认任务
esp_netif_create_default_wifi_sta()初始化STA模式下TCP/IP协议堆栈相关参数
esp_wifi_init(&cfg)初始化STA模式下TCP/IP适配器及处理函数
esp_event_handler_instance_register(WIFI_EVENT);正对WiFi事件WIFI_EVENT注册一个事件处理函数event_handler
esp_event_handler_instance_register(IP_EVENT)正对IP事件IP_EVENT注册一个事件处理函数event_handler
esp_wifi_set_mode(WIFI_MODE_STA)设置WiFi工作模式为STA
esp_wifi_set_config(WIFI_IF_STA, &wifi_config)设置WiFi要连接的AP账号和密码
esp_wifi_start()启动WiFi
xEventGroupWaitBits(s_wifi_event_group)等待连接成功事件(对应的位标志:WIFI_CONNECTED_BIT) 等待连接失败事件(对应的位标志:WIFI_FAIL_BIT)

这里面有一个难点是事件WIFI_EVENT_STA_START、IP_EVENT_STA_GOT_IP是如何产生,并最终驱动WIFI_CONNECTED_BIT注入到事件标注当中,以告诉用户WiFi连接成功。具体实现过程如下。

在调用函数esp_wifi_start()之后,Wi-Fi驱动程序将 WIFI_EVENT_STA_START 发布到事件任务(event task),事件任务(event task)调用应用程序事件回调函数(event_handler)执行esp_wifi_connect()函数。Wi-Fi驱动程序根据配置连接对应的AP,连接成功将 WIFI_EVENT_STA_CONNECTED 发布到事件任务(event task)。接收到此事件后,事件任务将启动 DHCP 客户端服务并开始获取 IP 地址。由于我们的案例中TCP/IP是基于LwIP 实现的,因此,还需等待IP_EVENT_STA_GOT_IP事件发生。

只有IP_EVENT_STA_GOT_IP事件发生,我们才能认为是连接上指定的AP,继而可以发送和接收数据。

测试结果

在这里插入图片描述

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值