ESP32S3网络编程学习笔记(1)—— Wi-Fi扫描实验

前言

(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动/单片机/RTOS的实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)学习本文之前,建议先学习:ESP32S3网络编程学习笔记(0)—— 计算机网络基础科普

实操

工程目录和CMakeList修改

(1)在main文件夹中创建APP文件夹,并且在APP文件夹中创建wifi.cwifi.h两个文件。

在这里插入图片描述

(2)在main/CMakeLists.txt中修改为如下代码。

idf_component_register(SRC_DIRS "." "./app"
                    INCLUDE_DIRS "." "./app")

在这里插入图片描述

WIFI程序配置

wifi.c

(1)在wifi.c中补充如下代码。

/**
 ****************************************************************************************************
 * @file     led.c
 * @brief    正点原子ESP32S3开发板的WIFI扫描实验
 * @author   zhangyixu
 * @version  1.0.0
 * @date     2024-04-06
 * @Copyright (c) 仅供学习,如需商用,请邮件zhangyixu02@gmail.com
 ****************************************************************************************************
 * @attention
 * 
 * 个人博客:www.zyxbeyourself.blog.csdn.net
 * 本人邮件:zhangyixu02@gmail.com
 ****************************************************************************************************
 * @par Change Logs:
 * 
 * Date Author Notes
 * 2024-04-06 zhangyixu 初始版本
*/

#include "esp_log.h"
#include "esp_wifi.h"
#include "wifi.h"

/* 存储12个WIFI名称 */
#define DEFAULT_SCAN_LIST_SIZE  10

static const char *TAG = "WIFI";

void wifi_init(void)
{
    /*-------------- 1、Wi-Fi/LwIP 初始化阶段 --------------*/
    /* 1.1、网卡初始化,LWIP内核初始化 */
    ESP_ERROR_CHECK(esp_netif_init());
    /* 1.2、创建新的事件循环 */
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    /* 1.3、用户初始化STA模式 */
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);
    /* wifi配置初始化 */
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    /*-------------- 2、Wi-Fi/LwIP 配置阶段 --------------*/
    /* 设置WIFI为STA模式 */
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

    /*-------------- 3、Wi-Fi/LwIP 启动阶段 --------------*/
    /* 启动WIFI */
    ESP_ERROR_CHECK(esp_wifi_start());
}

void wifi_scan(void)
{
    uint16_t number = DEFAULT_SCAN_LIST_SIZE;
    uint16_t ap_count = 0;
    wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE] = {0};

    /* 开始扫描附件的WIFI */
    esp_wifi_scan_start(NULL, true);
    /* 获取上次扫描中找到的AP数量 */
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
    ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);
    /* 获取上次扫描中找到的AP列表 */
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
    /* 下面是打印附件的WIFI信息 */
    for (int i = 0; (i < number) && (i < ap_count); i++)
    {
        ESP_LOGI(TAG, "NUM \t\t%d", 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, "MAC \t\t%02X-%02X-%02X-%02X-%02X-%02X\n", ap_info[i].bssid[0], ap_info[i].bssid[1], ap_info[i].bssid[2], ap_info[i].bssid[3], ap_info[i].bssid[4], ap_info[i].bssid[5]);
    }
}

wifi.h

(1)在wifi.h中补充如下代码。

/**
 ****************************************************************************************************
 * @file     led.h
 * @brief    正点原子ESP32S3开发板的WIFI扫描实验
 * @author   zhangyixu
 * @version  1.0.0
 * @date     2024-04-06
 * @Copyright (c) 仅供学习,如需商用,请邮件zhangyixu02@gmail.com
 ****************************************************************************************************
 * @attention
 * 
 * 个人博客:www.zyxbeyourself.blog.csdn.net
 * 本人邮件:zhangyixu02@gmail.com
 ****************************************************************************************************
 * @par Change Logs:
 * Date Author Notes
 * 2024-04-06 zhangyixu 初始版本
*/

#ifndef __WIFI_H__
#define __WIFI_H__

    #ifdef __cplusplus
    extern "C" {
    #endif /* __cplusplus */
    /*============================ INCLUDES ======================================*/
    
    /*============================ MACROS ========================================*/
    
    /*============================ TYPES =========================================*/

    /*============================ GLOBAL VARIABLES ==============================*/

    /*============================ PROTOTYPES ====================================*/
    /**
     * @brief   将Wi-Fi初始化为sta并设置扫描方法
     *
     * @param   无
     *
     * @return  无
     */
    void wifi_init(void);
    /**
     * @brief   进行WIFI扫描
     *
     * @param   无
     *
     * @return  无
     */
    void wifi_scan(void);

    #ifdef __cplusplus
    }

    #endif /* __cplusplus */

#endif /* __WIFI_H__ */

main.c

(1)在main.c中补充如下代码。

/**
 ****************************************************************************************************
 * @file     led.c
 * @brief    正点原子ESP32S3开发板的WIFI扫描实验
 * @author   zhangyixu
 * @version  1.0.0
 * @date     2024-04-06
 * @Copyright (c) 仅供学习,如需商用,请邮件zhangyixu02@gmail.com
 ****************************************************************************************************
 * @attention
 * 
 * 个人博客:www.zyxbeyourself.blog.csdn.net
 * 本人邮件:zhangyixu02@gmail.com
 ****************************************************************************************************
 * @par Change Logs:
 * 
 * Date Author Notes
 * 2024-04-06 zhangyixu 初始版本
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "led.h"
#include "nvs_flash.h"
#include "wifi.h"


void app_main(void)
{
    esp_err_t ret;

    ret = nvs_flash_init();             /* 初始化NVS */

    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();
    }
    led_init();             /* 初始化LED */
    wifi_init();
    wifi_scan();
    while(1)
    {
        led_toggle();
        vTaskDelay(pdMS_TO_TICKS(1000));   /* 延时1s */
    }
}

运行结果

(1)烧录运行之后,能够一共扫描到多少个Wi-Fi,并且显示扫描到的前10个Wi-Fi名字,信号强度,MAC地址。

在这里插入图片描述

解析

为什么需要初始化NVS

(1)在esp_wifi_init()函数中,将会要使用到NVS相关内容,如果不先将NVS初始化,将无法成功读取到对应的内容,因此会导致初始化失败。
(2)如果我们不想使用NVS,可以打开menuconfig,按照下图关闭下图这个选项。

在这里插入图片描述

Wi-Fi配置初始化流程介绍

(1)在前面的计算机网络科普一文中我解释了APSTA的区别,因为本次实验是Wi-Fi扫描实验,因此我们需要将ESP32S3配置为STA模式。现在我们看看乐鑫科技官方给出来的STA模式配置流程。
(2)下图为截取部分,我们只需要看红框里面的内容即可。这个时候有人会问了,为什么只要看前三步呢?原因很简单,因为我们这里只会进行WIFI扫描,并不会进行连接操作,因此只需要看前三步骤。

在这里插入图片描述

1.初始化阶段

(1)上图,先看1.1步,我们知道需要初始化LwIPLwIP 是一个开源的 TCP/IP 协议栈,专门设计用于嵌入式系统。我们直接调用esp_netif_init()函数就可以将网卡LwIP内核进行初始化。

/* 1.1、网卡初始化,LWIP内核初始化 */
ESP_ERROR_CHECK(esp_netif_init());

(2)创建事件循环有两个函数esp_event_loop_create_default()esp_event_loop_create()函数。esp_event_loop_create_default()其实就是对esp_event_loop_create()进行了一层封装,没有特殊需求,一般调用esp_event_loop_create_default()
这个函数的调用是必要的,因为WIFI扫描实验需要在扫描过程中处理一些事件,例如扫描开始事件,扫描完成事件。如果没有创建事件循环,这些事件将无法被正确处理,导致Wi-Fi扫描实验无法正常进行。

/* 1.2、创建新的事件循环 */
ESP_ERROR_CHECK(esp_event_loop_create_default());

(3.1)前面说了,Wi-Fi扫描实验需要在扫描过程中处理一些事件。而这些事件由esp_netif_create_default_wifi_sta()函数创建。这个事件能够初始化Wi-FiSTA模式,官方的解释是创建有 TCP/IP 堆栈的默认网络接口实例绑定 station

/* 1.3、创建有 TCP/IP 堆栈的默认网络接口实例绑定 station */
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);

(3.2)这一步和上一步同属于1.3步骤,这里是 创建 Wi-Fi 驱动程序任务,并初始化 Wi-Fi 驱动程序。

/* 1.3、创建 Wi-Fi 驱动程序任务,并初始化 Wi-Fi 驱动程序 */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

(4)这个时候肯定就有同学有疑问了,怎么没有1.4创建应用程序任务呢?原因很简单,我们这里只是进行 Wi-Fi 扫描,并不会进行连接,因此这一步进行省略。

1.2配置阶段

(1)这句话就是将 Wi-Fi配置为STA模式。如果不是在中国,可能还需要调用esp_wifi_set_xxx ()进行更多的配置,例如:协议模式、国家代码、带宽等。

/* 2、设置 Wi-Fi为STA模式 */
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

1.3启动阶段

(1)因为我们不需要连接Wi-Fi,因此无需关心 WIFI_EVENT_STA_CONNECTED 事件。只需要调用如下代码即可。

/* 3.1、启动 Wi-Fi 驱动程序 */
ESP_ERROR_CHECK(esp_wifi_start());

关键API介绍

esp_wifi_scan_start()

(1)这个函数表示开始扫描附近的Wi-Fi设备,我们先讲解一下esp_wifi_scan_start()第一个参数的作用。
<1>如果ssid不为NULL,则只扫描SSID与该值相同的AP。否则扫描所有SSIDAP
注:ssid就是Wi-Fi的名字。AP表示热点或者说是Wi-Fi。
<2>如果bssid不为NULL,则只扫描MAC地址与该值相同的AP
注:不了解MAC地址是什么意思的,请看上一篇计算机网络科普。
<3>如果channel为0,则进行全信道扫描。否则只扫描指定的信道。
<4>show_hidden如果为true则扫描时会包括隐藏SSIDAP。否则会忽略隐藏SSIDAP
<5>scan_type如果为WIFI_SCAN_TYPE_ACTIVE,则进行主动扫描。如果是WIFI_SCAN_TYPE_PASSIVE,则进行被动扫描。如下为主动扫描和被动扫描的区别。通常情况下,移动设备会优先使用主动扫描,而固定设备则更倾向于使用被动扫描

  • 主动扫描:客户端主动发送Probe Request帧,询问周围是否有APAP收到Probe Request后,会立即回复Probe Response帧,告知自己的信息。主动扫描速度更快,但会增加网络流量和功耗。
  • 被动扫描:客户端被动地监听AP周期性发送的Beacon帧。AP会定期广播Beacon帧,包含自身的SSID、安全性等信息。被动扫描速度较慢,但不会增加网络流量和功耗。
  • 区别:主动扫描可以发现隐藏SSIDAP,被动扫描无法发现。主动扫描可以更快地发现AP,但会增加功耗。被动扫描不会主动打扰AP,但可能会漏掉某些AP

<6>scan_time用于控制每个信道的扫描时间。对于被动描,scan_time.passive字段指定每个信道的扫描时间。对于主动扫描,每个信道的扫描时间由scan_time.active.minscan_time.active.max决定。
<7>home_chan_dwell_time用于控制在主信道上停留的时间。当进行快速扫描时,会优先在主信道上停留较长时间,以提高扫描效率。
(2)第二个参数就比较容易理解,如果是true那么需要等待Wi-Fi扫描完成之后才会进行esp_wifi_scan_start()下面的函数。如果是false,那么无需等待Wi-Fi扫描完成就会马上执行下面的任务。
(3)如果没有特殊需求,esp_wifi_scan_start()函数第一个参数一般填写NULL默认为主动扫描扫描所有通道扫描所有Wi-Fi忽略隐藏Wi-Fi主动扫描时间为0~120ms主信道上停留的时间为360ms

/**
 * @brief   扫描所有可用的 AP
 *
 * @param    config    扫描的配置设置,如果设置为 NULL 将使用默认设置,默认值为 show_hidden:false、scan_type:active、scan_time.active.min:0、scan_time.active.max:120 毫秒、scan_time.passive :360 毫秒
 *          -block     如果true,此API将阻止调用者直到扫描完成,否则将立即返回
 *
 * @return   ESP_OK                    配置成功
 *         - ESP_ERR_WIFI_NOT_INIT     WiFi未由esp_wifi_init初始化
 *         - ESP_ERR_WIFI_NOT_STARTED  esp_wifi_start未启动WiFi
 *         - ESP_ERR_WIFI_TIMEOUT      阻塞扫描超时
 *         - ESP_ERR_WIFI_STATE        调用esp_wifi_scan_start时wifi仍在连接
 */
esp_err_t esp_wifi_scan_start(const wifi_scan_config_t *config, bool block);

/** @brief 每个通道的活动扫描时间范围 */
typedef struct {
    uint32_t min;  /**< 每个通道的最小活动扫描时间,单位:毫秒 */
    uint32_t max;  /**< 每个通道的最大活动扫描时间,单位:毫秒,超过1500ms可能导致工作站与AP断开连接,不建议使用。  */
} wifi_active_scan_time_t;

/** @brief 每个通道的主动和被动扫描时间的总和 */
typedef struct {
    wifi_active_scan_time_t active;  /**< 每个通道的主动扫描时间,单位:毫秒 */
    uint32_t passive;                /**< 每通道被动扫描时间,单位:毫秒,超过1500ms可能导致工作站与AP断开连接,不建议使用 */
} wifi_scan_time_t;

/** @brief SSID扫描参数 */
typedef struct {
    uint8_t *ssid;               /**< SSID of AP */
    uint8_t *bssid;              /**< MAC address of AP */
    uint8_t channel;             /**< 通道,扫描特定的通道 */
    bool show_hidden;            /**< 使能扫描SSID隐藏的AP */
    wifi_scan_type_t scan_type;  /**< 扫描类型,主动或被动 */
    wifi_scan_time_t scan_time;  /**< 每通道扫描时间 */
    uint8_t home_chan_dwell_time;/**< 在扫描连续通道之间花费的时间*/
} wifi_scan_config_t;

esp_wifi_scan_get_ap_num()

(1)当我们使用esp_wifi_scan_start()函数扫描完Wi-Fi之后,我们可以利用这个函数获取道扫描道的Wi-Fi数量。

/**
 * @brief   获取上次扫描中找到的Wi-Fi数量
 *
 * @param    number  存储上次扫描中找到的Wi-Fi数量
 *
 * @return   ESP_OK                    获取成功
 *         - ESP_ERR_WIFI_NOT_INIT     Wi-Fi未由esp_wifi_init初始化
 *         - ESP_ERR_WIFI_NOT_STARTED  esp_wifi_start未启动Wi-Fi
 *         - ESP_ERR_INVALID_ARG       无效参数
 */
esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number);

esp_wifi_scan_get_ap_records()

(1)当我们调用esp_wifi_scan_start()函数

/**
 * @brief   获取上次扫描中找到的Wi-Fi列表
 *
 * @param    number      设置ap_records可以容纳的最大Wi-Fi数量,如果number为10,但是扫描到的Wi-Fi只有4个,此时number会变成4。
 *         - ap_records  保存找到的Wi-Fi
 *
 * @return   ESP_OK                    获取成功
 *         - ESP_ERR_WIFI_NOT_INIT     Wi-Fi未由esp_wifi_init初始化
 *         - ESP_ERR_WIFI_NOT_STARTED  esp_wifi_start未启动Wi-Fi
 *         - ESP_ERR_INVALID_ARG       无效参数
 *         - ESP_ERR_NO_MEM            内存不足
 */
esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records);

Wi-Fi扫描流程介绍

(1)进行Wi-Fi扫描实验的前提是将ESP32设置为了STA模式,并且成功启动了Wi-Fi,否则无法进行Wi-Fi扫描实验。
(2)我们初始化完ESP32的Wi-Fi之后,就能够直接调用esp_wifi_scan_get_ap_num()函数进行扫描Wi-Fi了,需要注意的是,esp_wifi_scan_get_ap_num()的第二个参数需要配置为true,我们需要扫描完Wi-Fi之后再进行下一步操作。
(3)扫描到的Wi-Fi信息存储在wifi_ap_record_t结构体类型的数组里面,这个对于新手而言只需要关心三个参数:

  • ssid:Wi-Fi名字
  • rssi:一般来说,它是一个负数。RSSI值越大,表示信号强度越强,接近0RSSI值表示非常好的信号强度,而接近于-100RSSI值表示非常弱的信号强度。在某些情况下,如果使用的是其他类型的无线技术或者有特殊配置,RSSI可能会是正数。但对于大多数情况下,尤其是WiFi蓝牙等常见的无线通信,RSSI通常是负数。
  • bssid:Wi-FiMAC地址

参考

(1)乐鑫官方文档:ESP32-S3 Wi-Fi station 一般情况
(2)B站:WIFI扫描 - 乐鑫 ESP32 物联网开发框架 ESP-IDF 开发入门 - 孤独的二进制出品
(3)飞书:【立创·实战派ESP32-C3】开发板文档教程

相关推荐

  1. 21-ESP32-S3实时时钟(RTC)

    2024-04-08 04:50:03       33 阅读
  2. ESP32网络编程实例-WebSocket服务器广播信息

    2024-04-08 04:50:03       69 阅读
  3. 单片机学习笔记——ESP32

    2024-04-08 04:50:03       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-08 04:50:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-08 04:50:03       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-08 04:50:03       82 阅读
  4. Python语言-面向对象

    2024-04-08 04:50:03       91 阅读

热门阅读

  1. 面试算法-138-移动零

    2024-04-08 04:50:03       32 阅读
  2. Kratos 基础学习记录

    2024-04-08 04:50:03       42 阅读
  3. hibernate检索方式

    2024-04-08 04:50:03       43 阅读
  4. 常见的几种字符串及其区别

    2024-04-08 04:50:03       36 阅读
  5. Linux介绍

    2024-04-08 04:50:03       33 阅读
  6. 记录CodeMirror一些常用的配置选项

    2024-04-08 04:50:03       37 阅读
  7. AI创业机会的探索

    2024-04-08 04:50:03       40 阅读
  8. MySQL-对象

    2024-04-08 04:50:03       32 阅读
  9. C++20 semaphore(信号量) 详解

    2024-04-08 04:50:03       33 阅读