ESP-IDF Smartconfig代码理解

ESP32 Smartconfig

前言

对每一款wifi芯片来说配网功能都是必不可少的,乐鑫的配网方式是smartconfig,所以理解smartconfig是学习ESP32必不可少的。以下是刚研究源代码的理解,如果有错误的地方还请提出批评和指正。

源代码查看

我是使用VSCODE搭建的ESP32开发环境,也可以使用官方教程搭建开发环境。首先选中例程smart_config创建工程。
VSCODE创建smart_config工程
smartconfig_main.c就是smartconfig功能的源代码

代码理解

ESP-IDF是基于FreeRTOS的,所以首先需要学习一下FreeRTOS的使用,此例程用到了FreeRTOS任务,事件标志组等功能。还需要了解ESP-IDF事件组的功能以及WIFI使用的流程

ESP32 WIFI Station使用流程

一、Wi-Fi/LwIP 初始化阶段

1.1、调用函数 esp_netif_init() 创建一个 LwIP 核心任务,并初始化 LwIP 相关工作。
1.2、调用函数 esp_event_loop_create() 创建一个系统事件任务,并初始化应用程序事件的回调函数
1.3、调用函数 esp_netif_create_default_wifi_sta() 创建有 TCP/IP 堆栈的默认网络接口实例绑定 station
1.4、通过调用函数 esp_wifi_init() 创建 Wi-Fi 驱动程序任务,并初始化 Wi-Fi 驱动程序
1.5、通过调用 OS API 创建应用程序任务

二、Wi-Fi配置阶段

2.1、调用函数 esp_wifi_set_mode(WIFI_MODE_STA) 将 Wi-Fi 模式配置为 station 模式

三、Wi-Fi启动阶段

3.1、调用函数 esp_wifi_start() 启动 Wi-Fi 驱动程序。
3.2、Wi-Fi 驱动程序将事件 WIFI_EVENT_STA_START 发布到事件任务中,然后,事件任务将执行一些正常操作并调用应用程序的事件回调函数

四、Wi-Fi连接阶段

4.1、调用函数 esp_wifi_connect() 后,Wi-Fi 驱动程序将启动内部扫描/连接过程。
4.2、如果内部扫描/连接过程成功,将产生 WIFI_EVENT_STA_CONNECTED 事件。然后,事件任务将启动 DHCP 客户端服务,最终触发 DHCP 程序。
4.3、在此情况下,应用程序的事件回调函数会将 WIFI_EVENT_STA_CONNECTED 事件中继到应用程序任务中。

五、Wi-Fi获取IP阶段

5.1、一旦步骤 4.2 中的 DHCP 客户端初始化完成,Wi-Fi 驱动程序将进入 获取 IP 阶段。
5.2、如果 Wi-Fi 成功从 DHCP 服务器接收到 IP 地址,则将引发 IP_EVENT_STA_GOT_IP 事件,事件任务将执行正常处理

六、Wi-Fi断开阶段

6.1、当 Wi-Fi 因为某些原因(例如:AP 掉电、RSSI 较弱等)连接中断时,将产生WIFI_EVENT_STA_DISCONNECTED 事件。此事件也可能在上文阶段 3 中发生。在这里,事件任务将通知 LwIP 任务清除/移除所有 UDP/TCP 连接。然后,所有应用程序套接字都将处于错误状态。也就是说,WIFI_EVENT_STA_DISCONNECTED 事件发生时,任何套接字都无法正常工作
6.2、上述情况下,应用程序的事件回调函数会将 WIFI_EVENT_STA_DISCONNECTED 事件中继到应用程序任务中

七、Wi-Fi IP更改阶段

7.1、如果 IP 地址发生更改,将引发 IP_EVENT_STA_GOT_IP 事件,其中 “ip_change” 被置为 “true”。
7.2、此事件对应用程序至关重要。这一事件发生时,适合关闭所有已创建的套接字并进行重新创建。

八、Wi-Fi清理阶段

8.1、调用函数 esp_wifi_disconnect() 断开 Wi-Fi 连接。
8.2、调用函数 esp_wifi_stop() 终止 Wi-Fi 驱动程序。
8.3、调用函数 esp_wifi_deinit() 清理 Wi-Fi 驱动程序。

ESP32 事件

一、WIFI_EVENT_SCAN_DONE

扫描完成事件,由 esp_wifi_scan_start() 函数触发,将在以下情况下产生:
1、扫描已完成,例如:Wi-Fi 已成功找到目标 AP 或已扫描所有信道。
2、当前扫描因函数 esp_wifi_scan_stop() 而终止。
3、在当前扫描完成之前调用了函数 esp_wifi_scan_start()。此时,新的扫描将覆盖当前扫描过程,并生成一个扫描完成事件。
以下情况下将不会产生扫描完成事件:
1、当前扫描被阻止。
2、当前扫描是由函数 esp_wifi_connect() 触发的

二、WIFI_EVENT_STA_START

如果调用函数 esp_wifi_start() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将初始化 LwIP 网络接口 (netif)。通常,应用程序的事件回调函数需调用 esp_wifi_connect() 来连接已配置的 AP

三、WIFI_EVENT_STA_CONNECTED

如果调用函数 esp_wifi_connect() 后接收到返回值 ESP_OK,且 station 已成功连接目标 AP,则将产生此连接事件。接收到此事件后,事件任务将启动 DHCP 客户端服务并开始获取 IP 地址。此时,Wi-Fi 驱动程序已准备就绪,可发送和接收数据。如果您的应用程序不依赖于 LwIP(即 IP 地址),则此刻便可以开始应用程序开发工作。但是,如果您的应用程序需基于 LwIP 进行,则还需等待 got ip 事件发生后才可开始

四、WIFI_EVENT_STA_DISCONNECTED

此事件将在以下情况下产生:
1、调用了函数 esp_wifi_disconnect()、esp_wifi_stop() 或 esp_wifi_deinit(),且 Wi-Fi station 已成功连接至 AP。
2、调用了函数 esp_wifi_connect(),但 Wi-Fi 驱动程序因为某些原因未能成功连接至 AP,例如:未扫描到目标 AP、验证超时等。或存在多个 SSID 相同的 AP,station 无法连接所有已找到的 AP,也将产生该事件。
3、Wi-Fi 连接因为某些原因而中断,例如:station 连续多次丢失 N beacon、AP 脱离 station 连接、AP 验证模式改变等

五、IP_EVENT_STA_GOT_IP

当 DHCP 客户端成功从 DHCP 服务器获取 IPV4 地址或 IPV4 地址发生改变时,将引发此事件。此事件意味着应用程序一切就绪,可以开始任务(如:创建套接字)。

源代码分析

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

首先初始化默认 NVS 分区。然后执行initialise_wifi()

static void initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    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) );
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}

上边我们介绍了WIFI Station的使用流程,initialise_wifi()就是按流程进行初始化,
1、esp_netif_init()函数创建一个 LwIP 核心任务
2、xEventGroupCreate()函数创建一个FreeRTOS事件标志组,用于事件或者任务的同步
3、esp_event_loop_create_default()创建一个系统事件任务,并初始化应用程序事件的回调函数
4、esp_netif_create_default_wifi_sta()创建有 TCP/IP 堆栈的默认网络接口实例绑定 station
5、esp_wifi_init()创建 Wi-Fi 驱动程序任务,并初始化 Wi-Fi 驱动程序(按默认配置进行初始化)
6、esp_event_handler_register()将事件处理程序注册到系统事件循环
7、esp_wifi_set_mode()将 Wi-Fi 模式配置为 station 模式
8、esp_wifi_start()启动 Wi-Fi 驱动程序。
在第6步我们注册了三个事件,所以要在回调函数中进行事件的处理

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) {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i=0; i<33; i++) {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

事件处理函数主要是当事件触发是执行相应的处理,调用函数 esp_wifi_start() 后接收到返回值 ESP_OK,此时会触发WIFI_EVENT_STA_START事件,所以在此事件触发时通过xTaskCreate创建了一个samartconfig的任务,用来进行smartconfig配网(下边进行分析),当检测到WIFI_EVENT_STA_DISCONNECTED事件调用esp_wifi_connect()连接WIFI,并且调用xEventGroupClearBits()清除事件标志组的CONNECTED_BIT标志,当IP_EVENT_STA_GOT_IP事件触发时调用xEventGroupSetBits()函数向事件标志组发送了一个事件标志组的CONNECTED_BIT标志,在SC_EVENT_SEND_ACK_DONE事件触发时发送一个ESPTOUCH_DONE_BIT标志

static void smartconfig_example_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
    
}

上边在WIFI_EVENT_STA_START事件触发时建立了一个任务,进行smartconfig配网,此任务就实现了配网的功能。首先调用esp_smartconfig_set_type()设置配网方式,然后调用esp_smartconfig_start()按照默认配置参数开始进行配网,然后一直在获取相应的事件标志组的标志,当获取到CONNECTED_BIT说明已经连接上WIFI,当获取到ESPTOUCH_DONE_BIT说明配网完成,停止配网,并且删除本身任务。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Karl Zhangq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值