3·ESP32-C3入门教程——讲透wifi模块配置(从wifi scan到smart config)

        对于物联网实战来说,wifi模块当属重中之重。今天有了学习的新思路,既然是入门学习,从整体入手,开始主要是按照模块化的角度去学习,从整体上认识每个模块的功能,然后在回顾部分再去梳理一遍流程、每个API调用的原理以及相关函数的知识。

       

目录

level1:从wifi scan模式入门wifi模块

最简单的方式实现wifi扫描

加入回调,采用扫描的方式

level2:打开热点&连接路由器

ap模式

sta模式

level3:一键智能配置smart config

例程使用

实现原理

回顾——esp32的WIFI实现流程

主程序

回调函数

流程图

小结 


        学习ESP32,少了wifi怎么行?这一篇先从扫描wifi信息开始光速入门,再一步步学习如何配置和连接wifi,到如何打开热点模式,最后进阶多种多样的配网方式,好好研究一下wifi板块的功能。

        前两篇中涉及到的一些概念和用法可能对学习WIFI以及理解一些函数的用法有帮助,也记录了我过程中esp32学习方法的变化,链接指路→从点灯开始,学习定时器和PWM控制从按键入手,学习中断回调以及freertos的线程概念

        从本章起,我也将采用从模块化分析API接口的方式,从整体到局部,去学习ESP32的WIFI功能(注意:ESP32只支持2.4GHz WiFi,还不支持5GHz WiFi)

level1:从wifi scan模式入门wifi模块

最简单的方式实现wifi扫描

       先从扫描wifi开始认识ESP32的wifi模块,这一部分实现起来还是比较容易的,调用的接口也不多,源码以及模块化总结如下。

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"

#define DEFAULT_SCAN_LIST_SIZE 16

static const char *TAG = "scan";

void wifi_scan_result(void)
{            
    uint16_t number = DEFAULT_SCAN_LIST_SIZE;
    uint16_t ap_count = 0;
    wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
    memset(ap_info, 0, sizeof(ap_info));

    esp_wifi_scan_get_ap_records(&number, ap_info);
    esp_wifi_scan_get_ap_num(&ap_count);
    
    ESP_LOGI(TAG, "Total APs scanned = %d", ap_count);

    for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
        ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
        ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
        ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
    }
}

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        nvs_flash_erase();
        ret = nvs_flash_init();
    }
    
    esp_netif_init();
    esp_event_loop_create_default();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    esp_wifi_set_mode(WIFI_MODE_STA);
    esp_wifi_start();
    esp_wifi_scan_start(NULL, true);
    wifi_scan_result();
}

 运行结果如下: 

加入回调,采用扫描的方式

        在按键模块我就写过,将想要实现功能函数放到主函数中,虽然很好理解,除非只实现几个功能,不然对于一个复杂事件来说还是太笨了,功能实现的过程和顺序都会相互干扰。所以我们需要更进一步加入中断的学习。

         加入中断其实也比较容易实现,在前两篇文章中我有详细分析和讨论过,所以这里就不展开叙述了,源码以及模块化注释如下:

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"

#define DEFAULT_SCAN_LIST_SIZE 16

static const char *TAG = "scan";

uint16_t number = DEFAULT_SCAN_LIST_SIZE;
uint16_t ap_count = 0;
wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
void wifi_scan_result(void)
{            
    memset(ap_info, 0, sizeof(ap_info));
    esp_wifi_scan_get_ap_records(&number, ap_info);
    esp_wifi_scan_get_ap_num(&ap_count);
    ESP_LOGI(TAG, "Total APs scanned = %d", ap_count);
    for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
        ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
        ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
        ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
    }
}

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_SCAN_DONE) {
        wifi_event_sta_scan_done_t* scan_done = (wifi_event_sta_scan_done_t*) event_data;
        ESP_LOGE(TAG, "wifi scan done status = %d, number = %d", scan_done->status, scan_done->number);
        wifi_scan_result();        
    }
    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));
    }
}

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        nvs_flash_erase();
        ret = nvs_flash_init();
    }
    
    esp_netif_init();
    esp_event_loop_create_default();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL);
    esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL);

    esp_wifi_set_mode(WIFI_MODE_STA);
    esp_wifi_start();
    esp_wifi_scan_start(NULL, false);
}

level2:打开热点&连接路由器

        level1可以算是小试牛刀,有了WiFi的相关概念之后,我们可以更近一步,学习如何配置wifi模块,更近一步打开物联网的大门。

        sta模式和ap模式ESP官网都给了例程,上手就可以直接用,所以这两部分的代码我就直接引用了,只需要在vscode中,contorl+shift+p,输入example,在里面找到wifi模块的get-start,选择创建文件夹,然后编译烧录就可以看到结果了~

ap模式

       上面的SCAN部分理解透了,AP部分直接光速拿下,由于是从被动扫描模式切换到了主动发射模式,所以需要自己DIY一些部分,总的来看还是非常好理解的,烧录好后,可以打开手机连接ESP的热点(虽然没有网络)。

sta模式

        sta模式其实和ap模式实现过程大体相似,只是模式选择和个别调用的接口不同,但是因为需要考虑的事件和情况更多,所以代码量会比ap模式多一些,在官方的例程中,可以直接调用和烧录,因为显示范围问题,我把截图分成了两部分。

        这里引入了事件的概念,下面有个方框没有备注上,是对上面事件信号的调用和监测,可以和上面那张图片对比着看。

         经过了按键和点灯的学习,我们可以举一反三,比较轻松的去学习WiFi环节的配置,但是想要联网,我们必须要在源代码中就要设置好网络的ID和密码,这样多少还是比较麻烦的。那么怎么样去做到可以实现随时随地就更换网络呢?esp提供了一个方案——smart config,我们只需要连接手机,就可以随时更换所想要的WiFi和密码。

level3:一键智能配置smart config

例程使用

        samrt config官方也给出了例程,我们只需要按照上述的方法即可创立例程。

        在使用这个例程之前,我们需要先进行一个APP的下载,链接指路→手机端的网络配置软件,会跳转到github,选择.apk的文件下载,然后我们只需要进行配置就好了。 

         

注意:ESP32只支持2.4GHz WiFi,还不支持5GHz WiFi,如果家里的网络多次配置失败的话,可以使用手机的热点,更容易成功。

实现原理

一键配网连接过程:

  1. ESP32-C3 开启混杂模式监听所有网络数据包。
  2. 手机连上 WiFi,开启 APP 软件,输入手机所在 WiFi 密码,请求配网。
  3. 手机通过广播、组播循环发送路由 SSID 和 PASSWD 数据包,按一定格式加密。
  4. ESP32 通过 UDP 包(长度)获取配置信息捕捉到路由 SSID 和 PASSWD,连接路由器。

        Smart Config 采用的是 UDP 广播模式 (UDP 接收 IP 地址是 255.255.255.255) 。 WiFi 设备先 scan 环境下 AP, 得到 AP 的相关信息,如工作的 channel。然后配置 WiFi 芯片工作 scan 到的 channel 上去接收 UDP 包,如果没有接收到,继续配置工作在另外的 channel 上,如此循环,直到收到 UDP 包为止。

        这种办法的致命缺点是成功率只有 70%,而且有些路由器不支持;但优点是能一键完成配网

回顾——esp32的WIFI实现流程

主程序

  • nvs_flash_init  初始化默认NVS分区
  • esp_netif_init  初始化底层TCP/IP堆栈
  • esp_event_1oop_create_default  创建默认事件循环
  • esp_netif_create_default_wifi_sta  使用默认WiFi Station配置创建esp_netif对象,将netif连 接到WiFi并注册默认WiFi处理程序 esp_wifi_init,为WiFi驱动初始化WiFi分配资源,如WiFi控制结构、RX/TX缓冲区、WiFi NVS结构等,这个WiFi也启动WiFi任务。必须先调用此API,然后才能调用所有其他WiFiAPI
  • esp_event_handler_instance_register  监听WIFI_EVENTWiFi任意事件,触发事件后,进入回 调函数
  • esp_event_handler_instance_register  监听IP_EVENT从连接的AP获得IP的事件,触发事件 后,进入回调函数
  • esp_event_handler_instance_register  监听SC_EVENT从SmartConfig任意事件,触发事件后, 进入回调函数
  • esp_wifi_set_mode  设置WiFi工作模式为station、soft-AP或station+soft-AP,默认模式为soft- AP模式。本程序设置为
  • station esp_wifi_start  根据配置,启动WiFi

回调函数

  • WIFI_EVENT_STA_START  WiFi station模式启动时
    • 【创建smartconfig_example_task 线程,开始SmartConfig】
  • WIFI_EVENT_STA_DISCONNECTED WiFi station模式失去连接。
    • 【清除CONNECTED_BIT标志位】
  • IP_EVENT_STA_GOT_IP  WiFi station模式从连接的AP那获得IP
    • 【设置CONNECTED_BIT标志位】
  • SC_EVENT_SCAN_DONE SmartConfig扫描AP列表结束
  • SC_EVENT_FOUND_CHANNEL  SmartConfig 从目标AP找到频道
  • SC_EVENT_GOT_SSID_PSWD  SmartConfig 获得WiFi信息(SSID和密码)时,
    • 【解析出WiFi的SSID和密码】
    • 【esp_wifi_disconnect断开当前WiFi连接】
    • 【esp_wifi_set_config WiFI配置,设置WIFI_IF_STA模式,设置WiFi的SSID和密码】
    • 【esp_wifi_connect WiFi连接】
  • SC_EVENT_SEND_ACK_DONE  SmartConfig 给App发送完成ACK
    • 【设置ESPTOUCH_DONE_BIT标志位】
       

流程图:

 

小结 

         WiFi部分是esp的优势所在,也是其在今天的物联网市场如此受欢迎的原因。官方给的例程和RAINMAKER以及ESP TOUCH的接口都非常方便,我们可以很轻易的在它的基础上拓展自己需要的功能,很多接口都可以直接调用,大大简化了我们需要的开发时间和步骤。

        还有一些部分因为时间安排原因,暂时没有展开写,比如像蓝牙部分、NVS实现过程,本地控制...但是从实战角度出发,这三篇穿插讲到的这些内容已经足够去让我们做一个比较小的实验demo了,还没有写到的内容会不断完善。

        当然,从实战的角度出发,光实现一个WiFi的连接,只是搭好了一座桥,怎么样让人们在桥上有秩序地行走,也就是数据如何在MCU和互联网之间传输信息,还需要更进一步。下一章咱们聊聊如何从本地控制到接入rainmaker,实现一个小的物联网控制模型。

        

猜你喜欢

转载自blog.csdn.net/TenYao_/article/details/128782611