ESP32 event loop(事件循环)(17)

news2024/11/22 21:14:35

提示:本博客作为学习笔记,有错误的地方希望指正,主要参考乐鑫技术手册说明结合实例代码分析,结合理论知识学习后示例分析以及常见问题说明。

文章目录

  • 一、ESP32 Event Loop 概述
    • 2、使用 esp_event APIs
    • 3、默认 event loop
    • 4、句柄注册注意事项
    • 5、句柄注册和句柄调度
    • 6、event loop 分析
    • 7、应用实例
    • 8、event loop 使用示例

一、ESP32 Event Loop 概述

  参考资料:ESP IDF编程手册V5.0
  事件循环库允许组件声明事件,其他组件可以注册处理程序–当这些事件发生时将执行的代码。这允许松散耦合的组件将所需的行为附加到其他组件的状态变化上,而无需应用程序的参与。例如,一个高水平的连接处理库可以直接订阅Wi-Fi子系统产生的事件并对这些事件采取行动。这也通过序列化和推迟代码的执行来简化事件处理。
  event loop 我的理解他就是一个任务回调,可以处理不同组件发送的不同事件,我们可以在回调中处理其他组件发送的一些事件以及接受发送的值,这样的好处就是降低各个组件之间的耦合性,使得代码阅读以及维护更加方便,乐鑫提供的event loop真的非常非常好用。就有点像QT中的信号与槽的绑定,当有事件post的时候,回执行到以注册改组件的回调函数中。

2、使用 esp_event APIs

  这个库的用户有两个关注对象:事件和事件循环。
  事件是注意到的事件的发生。例如,对于Wi-Fi,成功连接到接入点可能是一个事件。事件是用两部分标识符来引用的,这里会详细讨论。事件循环是事件源发布事件并由事件处理函数处理的载体。这两者在事件循环库的API中出现得很明显。
  使用这个库大致需要以下流程:

  1. 用户定义了一个函数,当事件被发布到一个循环时,该函数应该运行。这个函数被称为事件处理程序。它应该具有与esp_event_handler_t相同的签名。
  2. 使用esp_event_loop_create()创建一个事件循环,它输出一个类型为esp_event_loop_handle_t的循环的句柄。使用这个API创建的事件循环被称为用户事件循环。然而,有一种特殊类型的事件循环,称为默认事件循环,在此讨论。
  3. 组件使用esp_event_handler_register_with()将事件处理程序注册到循环中。处理程序可以在多个循环中注册,这里有更多的介绍。
  4. 事件源使用esp_event_post_to()将一个事件发布到循环中。
  5. 想要移除其处理程序的组件可以通过使用esp_event_handler_unregister_with()从循环中取消注册来实现。
  6. 不再需要的事件循环可以用esp_event_loop_delete()来删除。

  在代码中,上面的流程可能看起来如下:

// 1.定义事件处理程序
void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    // 事件处理程序逻辑
}

void app_main()
{
	// 需要一个esp_event_loop_args_t类型的配置结构来指定将要创建的循环的属性。获得一个esp_event_loop_handle_t
	// 类型的句柄,其他API需要它来引用这个循环来执行它们的操作。来执行它们的操作。
    esp_event_loop_args_t loop_args = {
        .queue_size = ....task_name = ...
        .task_priority = ....task_stack_size = ....task_core_id = ...
    };

    esp_event_loop_handle_t loop_handle;
    esp_event_loop_create(&loop_args, &loop_handle)// 3. 注册在(1)中定义的事件处理程序。MY_EVENT_BASE和MY_EVENT_ID指定了一个假想的
    // 处理程序run_on_event应该在它被发布到循环中时执行。
    esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event, ...)...

    // 4.将事件发布到循环中。这样就把事件排在了事件循环上。在某个时间点上
    // 事件循环执行注册到发布事件的事件处理程序,在本例中是run_on_event。
    // 为了简单起见,本例从app_main调用esp_event_post_to,但发布可以从
    // 任何其他任务(这是更有趣的用例)。
    esp_event_post_to(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, ...)...

    // 5.取消注册一个不需要的处理程序
    esp_event_handler_unregister_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event);

    ...

    // 6.删除一个不需要的事件循环
    esp_event_loop_delete(loop_handle)}

  声明和定义事件
  如前所述,事件由两部分标识组成:事件基础和事件ID。事件基数标识了一个独立的事件组;事件ID标识了该组中的事件。可以把事件基数和事件ID分别看作是一个人的姓和名。姓氏标识了一个家庭,而名字则标识了这个家庭中的一个人。
  事件循环库提供了宏,可以轻松地声明和定义事件基。
  事件基的声明:

ESP_EVENT_DECLARE_BASE(EVENT_BASE)

  事件基础定义:

ESP_EVENT_DEFINE_BASE(EVENT_BASE)

备注
在IDF中,系统事件的基础标识符是大写的,并且后缀为_EVENT。例如,Wi-Fi事件的基础被声明和定义为WIFI_EVENT,以太网事件的基础为ETHERNET_EVENT,等等。这样做的目的是让事件基数看起来像常数(尽管考虑到宏ESP_EVENT_DECLARE_BASE和ESP_EVENT_DEFINE_BASE的定义,它们是全局变量)。

  对于事件的ID,建议将其作为枚举来声明。再一次,为了提高可见性,这些通常被放在公共头文件中。
事件ID:

enum {
    EVENT_ID_1,
    EVENT_ID_2,
    EVENT_ID_3,
    ...
}

3、默认 event loop

  默认事件循环是一种特殊类型的循环,用于处理系统事件(例如,Wi-Fi事件)。这个循环的处理程序对用户是隐藏的。事件的创建、删除、处理程序的注册/注销和发布是通过用户事件循环的API的一个变体完成的。下表列举了这些变体,以及用户事件循环的对应关系。

用户 Event Loops默认 Event Loops
esp_event_loop_create()esp_event_loop_create_default()
esp_event_loop_delete()esp_event_loop_delete_default()
esp_event_handler_register_with()esp_event_handler_register()
esp_event_handler_unregister_with()esp_event_handler_unregister()
esp_event_post_to()esp_event_post()

  如果你比较两者的签名,除了默认事件循环API缺乏循环处理规范外,它们大部分是相似的。
  除了API的不同和系统事件被发布到的特殊指定之外,默认事件循环和用户事件循环的行为没有任何区别。用户甚至可以将自己的事件发布到默认的事件循环中,如果用户选择不创建自己的循环以节省内存的话。

4、句柄注册注意事项

  可以对多个事件单独注册一个处理程序,即使用多次调用esp_event_handler_register_with()。对于这些多次调用,可以指定具体的事件基数和事件ID,处理程序应该执行。
  然而,在某些情况下,一个处理程序最好是在(1)所有被发布到一个循环中的事件或(2)所有特定基础标识符的事件上执行。使用特殊事件基本标识符ESP_EVENT_ANY_BASE和特殊事件标识符ESP_EVENT_ANY_ID可以做到这一点。这些特殊的标识符可以作为esp_event_handler_register_with()的事件基础和事件ID参数被传递。
  因此,esp_event_handler_register_with()的有效参数是:

  1. 《event base》, 《event ID》 当具有《event base》和《event ID》的事件被发布到循环中时,处理程序会被执行。
  2. 《event base》, ESP_EVENT_ANY_ID - 当任何具有<事件基数>的事件被发布到循环中时,处理程序就会执行。
  3. ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID - 当任何事件被发布到循环中时,处理程序会被执行。

  作为一个例子,假设进行了以下处理程序的注册:

esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event_1, ...);
esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, ESP_EVENT_ANY_ID, run_on_event_2, ...);
esp_event_handler_register_with(loop_handle, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, run_on_event_3, ...);

  如果假设事件MY_EVENT_BASE, MY_EVENT_ID被发布,所有三个处理程序run_on_event_1、run_on_event_2和run_on_event_3将被执行。
  如果假设事件MY_EVENT_BASE, MY_OTHER_EVENT_ID被发布,只有run_on_event_2和run_on_event_3将被执行。
  如果假设事件MY_OTHER_EVENT_BASE, MY_OTHER_EVENT_ID被发布,只有run_on_event_3会执行。

5、句柄注册和句柄调度

  一般的规则是,对于在调度过程中与某个发布的事件相匹配的处理程序,那些首先被注册的处理程序也会被首先执行。然后,用户可以通过在其他处理程序之前注册这些处理程序来控制哪些处理程序首先被执行,前提是所有的注册都是通过一个任务来执行的。如果用户打算利用这一行为,那么如果有多个任务在注册处理程序,就必须谨慎行事。虽然 "先注册、先执行 "的行为仍然成立,但先执行的任务也会先注册其处理程序。一个任务一个接一个地注册的处理程序仍将按照相对于彼此的顺序被派发,但是如果该任务在注册之间被另一个也注册了处理程序的任务抢占,那么在派发期间,这些处理程序也将在两者之间被执行。

6、event loop 分析

  可以启用配置选项CONFIG_ESP_EVENT_LOOP_PROFILING,以激活对所有创建的事件循环的统计数据收集。函数esp_event_dump()可以用来将收集的统计数据输出到一个文件流中。更多关于转储信息的细节可以在esp_event_dump() API参考中找到。

7、应用实例

  使用esp_event库的例子可以在system/esp_event中找到。这些例子包括事件声明、循环创建、处理程序注册和取消注册以及事件发布。
  其他同样采用esp_event库的例子:

  • NMEA解析器 ,它将对从GPS收到的语句进行解码。

8、event loop 使用示例

  在这个示例工程中我模拟了三个运用,一个motor电机的运用,一个是LED状态灯的运用,另外一个是WIFI的运用,整体的流程是wifi连接成功会发送事件给LED和motor组件,连接失败也是会发。当motor组件收到可以运行的指令之后就改变自己pwm值,同时也会发送给led。
在这里插入图片描述
  整体文件夹结构如下:
在这里插入图片描述
LED组件内容:
Led.h文件内容

#ifndef _Led_H_
#define _Led_H_
#include "System.h"

#define LED_EVENT_LOOP_BASE "LED"

#define BLUE_LED_LEDC_TIMER              LEDC_TIMER_0            //LED LEDC定时器
#define BLUE_LED_LEDC_MODE               LEDC_LOW_SPEED_MODE     //LED LEDC的速度模式
#define BLUE_LED_LEDC_OUTPUT_IO          GPIO_NUM_5              //LED LEDC绑定引脚
#define BLUE_LED_LEDC_CHANNEL            LEDC_CHANNEL_0          //LED LEDC通道
#define BLUE_LED_LEDC_DUTY_RES           LEDC_TIMER_13_BIT       //LED LEDC占空比分辨率
#define BLUE_LED_LEDC_DUTY               (4095)                  //LED LEDC占空比
#define BLUE_LED_LEDC_FREQUENCY          (5000)                  //LED LEDC频率 5 kHz


#define RED_GPIO_NUM                GPIO_NUM_18

typedef enum {
    LED_OFF = 0,
    LED_ON,
}led_on_off_state_t;


typedef enum {
    RED_LED_ON,
    RED_LED_OFF,
    BLUE_LED_SET_BRIGHTNESS_VALUE,
}led_state_t;


void led_init(void);

#endif

Led.c文件内容

#include "Led.h"


const static char * TAG = "LED";

static void led_ledc_set_value(uint16_t value);
static void set_red_led_state(led_on_off_state_t state);

static void led_event_loop_register_fun(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    switch (id)
    {
        case RED_LED_ON:
            set_red_led_state(LED_ON);
            ESP_LOGI(TAG,"RED_LED_ON");
            break;
        case RED_LED_OFF:
            ESP_LOGI(TAG,"RED_LED_OFF");
            set_red_led_state(LED_OFF);
            break;
        case BLUE_LED_SET_BRIGHTNESS_VALUE:
            uint16_t led_value = * (uint16_t *)event_data;  // 强制转换成uint16_t 指针地址然后取值
            led_ledc_set_value(led_value);
            ESP_LOGI(TAG,"BLUE_LED_SET_BRIGHTNESS_VALUE is : %d",led_value);
            break;
        default:
            break;
    }
}


static void blue_led_ledc_init(void)
{
    //LEDC定时器配置
    ledc_timer_config_t ledc_timer = {
        .speed_mode       = BLUE_LED_LEDC_MODE,         //设置定时器低速模式           
        .timer_num        = BLUE_LED_LEDC_TIMER,        //设置定时器0  
        .duty_resolution  = BLUE_LED_LEDC_DUTY_RES,     //设置定时器分辨率 13位
        .freq_hz          = BLUE_LED_LEDC_FREQUENCY,    //设置定时器频率 5kHz
        .clk_cfg          = LEDC_AUTO_CLK               //设置定时器时钟选择
    };  
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));    //设置定时器配置

    //配置LEDC通道
    ledc_channel_config_t ledc_channel = {                          
        .speed_mode     = BLUE_LED_LEDC_MODE,           //设置通道模式低速模式
        .channel        = BLUE_LED_LEDC_CHANNEL,        //设置LEDC通道0
        .timer_sel      = BLUE_LED_LEDC_TIMER,          //设置LEDC定时器
        .intr_type      = LEDC_INTR_DISABLE,            //设置LEDC中断类型       
        .gpio_num       = BLUE_LED_LEDC_OUTPUT_IO,      //设置LEDCGPIO引脚   
        .duty           = 0, // Set duty to 0%          //设置占空比
        .hpoint         = 0                             //LEDC通道hpoint值
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));//配置LEDC通道参数
}


static void red_light_init(void)
{
    gpio_config_t io_conf = {};                         //零初始化结构体
    io_conf.intr_type = GPIO_INTR_DISABLE;              //禁用中断
    io_conf.mode = GPIO_MODE_OUTPUT;                    //设置为输出模式
    io_conf.pin_bit_mask = BIT64(RED_GPIO_NUM);         //要设置的引脚的位掩码,e.g.GPIO18/19
    io_conf.pull_down_en = 0;                           //禁用下拉模式
    io_conf.pull_up_en = 0;                             //禁用上拉模式
    gpio_config(&io_conf);                              //配置GPIO结构体参数
}


static void led_ledc_set_value(uint16_t value)
{
    ESP_ERROR_CHECK(ledc_set_duty(BLUE_LED_LEDC_MODE, BLUE_LED_LEDC_CHANNEL, value));
    ESP_ERROR_CHECK(ledc_update_duty(BLUE_LED_LEDC_MODE, BLUE_LED_LEDC_CHANNEL));
}

static void set_red_led_state(led_on_off_state_t state)
{
    gpio_set_level(RED_GPIO_NUM, state);
}

void led_init(void)
{
    // 配置LEDC外设
    blue_led_ledc_init();
    red_light_init();
    system_event_loop_register(LED_EVENT_LOOP_BASE,ESP_EVENT_ANY_ID,led_event_loop_register_fun,NULL);
    ESP_LOGI(TAG,"LED init OK");
}





CMakeLists.txt文件内容

idf_component_register( SRCS "src/Led.c"
                        INCLUDE_DIRS "include"
                        REQUIRES System)

Motor组件内容:
Motor.h文件内容

/**
 * @file Motor.h
 * @author wsp
 * @brief 
 * @version 0.1
 * @date 2023-05-16
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef _Motor_H_
#define _Motor_H_
#include "System.h"


#define MOTOR_EVENT_LOOP_BASE "MOTOR"

#define MOTOR_LEDC_TIMER              LEDC_TIMER_0            //Motor LEDC定时器
#define MOTOR_LEDC_MODE               LEDC_LOW_SPEED_MODE     //Motor LEDC的速度模式
#define MOTOR_LEDC_OUTPUT_IO          GPIO_NUM_19             //Motor LEDC绑定引脚
#define MOTOR_LEDC_CHANNEL            LEDC_CHANNEL_1          //Motor LEDC通道
#define MOTOR_LEDC_DUTY_RES           LEDC_TIMER_13_BIT       //Motor LEDC占空比分辨率
#define MOTOR_LEDC_DUTY               (4095)                  //Motor LEDC占空比
#define MOTOR_LEDC_FREQUENCY          (5000)                  //Motor LEDC频率 5 kHz



typedef enum {
    MOTOR_SET_RUN_STATE,
    MOTOR_SET_NOT_RUN_STATE,
}motor_state_t;


void motor_init(void);

#endif

Motor.c文件内容

#include "Motor.h"

const static char * TAG = "Motor";
static bool motor_run_state = false;

static void motor_run_state_set(bool state);
static bool motor_run_state_get(void);

static void motor_event_loop_register_fun(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    switch (id)
    {
        case MOTOR_SET_RUN_STATE:
            ESP_LOGI(TAG,"MOTOR_SET_RUN_STATE");
            motor_run_state_set(true);
            break;
        case MOTOR_SET_NOT_RUN_STATE:
            ESP_LOGI(TAG,"MOTOR_SET_NOT_RUN_STATE");
            motor_run_state_set(false);
            break;
        default:
            break;
    }
}

static void motor_ledc_init(void)
{
    //LEDC定时器配置
    ledc_timer_config_t ledc_timer = {
        .speed_mode       = MOTOR_LEDC_MODE,            //设置定时器低速模式           
        .timer_num        = MOTOR_LEDC_TIMER,           //设置定时器0  
        .duty_resolution  = MOTOR_LEDC_DUTY_RES,        //设置定时器分辨率 13位
        .freq_hz          = MOTOR_LEDC_FREQUENCY,       //设置定时器频率 5kHz
        .clk_cfg          = LEDC_AUTO_CLK               //设置定时器时钟选择
    };  
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));    //设置定时器配置

    //配置LEDC通道
    ledc_channel_config_t ledc_channel = {                          
        .speed_mode     = MOTOR_LEDC_MODE,              //设置通道模式低速模式
        .channel        = MOTOR_LEDC_CHANNEL,           //设置LEDC通道0
        .timer_sel      = MOTOR_LEDC_TIMER,             //设置LEDC定时器
        .intr_type      = LEDC_INTR_DISABLE,            //设置LEDC中断类型       
        .gpio_num       = MOTOR_LEDC_OUTPUT_IO,         //设置LEDCGPIO引脚   
        .duty           = 0, // Set duty to 0%          //设置占空比
        .hpoint         = 0                             //LEDC通道hpoint值
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));//配置LEDC通道参数
}


static void motor_set_speed_value(int value)
{
    ESP_ERROR_CHECK(ledc_set_duty(MOTOR_LEDC_MODE, MOTOR_LEDC_CHANNEL, value));
    ESP_ERROR_CHECK(ledc_update_duty(MOTOR_LEDC_MODE, MOTOR_LEDC_CHANNEL));
}


static void motor_run_state_set(bool state)
{
    motor_run_state = state;
}

static bool motor_run_state_get(void)
{
    return motor_run_state;
}

void motor_task(void *arg)
{
    int speed_value = 0;
    while (1) {
        if (motor_run_state_get()) {
            motor_set_speed_value(speed_value);
            speed_value += 100;
            if(speed_value >= 4000)
                speed_value = 0;
            system_event_loop_post_to(LED_EVENT_LOOP_BASE, BLUE_LED_SET_BRIGHTNESS_VALUE, (void *)&speed_value, sizeof(speed_value), 10 / portTICK_PERIOD_MS);
            ESP_LOGI(TAG,"Motor task running motor value : %d", speed_value);
        }
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}


void motor_init(void)
{
    // 配置LEDC外设
    motor_ledc_init();
    system_event_loop_register(MOTOR_EVENT_LOOP_BASE,ESP_EVENT_ANY_ID,motor_event_loop_register_fun,NULL);
    xTaskCreate(motor_task, "motor_task", 1024 * 2, NULL, 8, NULL);
    ESP_LOGI(TAG,"motor init ok");
}


CMakeLists.txt文件内容

idf_component_register( SRCS "src/Motor.c"
                        INCLUDE_DIRS "include"
                        REQUIRES System)

System组件内容:
System.h文件内容

/**
 * @file System.h
 * @author wsp
 * @brief 
 * @version 0.1
 * @date 2023-05-16
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef _System_H_
#define _System_H_

#include <stdio.h>
#include "driver/ledc.h"
#include "esp_err.h"

#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 <stdlib.h>
#include "freertos/queue.h"
#include "driver/gpio.h"

#include "Led.h"
#include "my_wifi.h"
#include "Motor.h"

void system_event_loop_register(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void * event_handler_arg);
void system_event_loop_unregister(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler);
void system_event_loop_post_to(esp_event_base_t event_base, int32_t event_id, const void * event_data, size_t event_data_size, TickType_t ticks_to_wait);

void system_init(void);

#endif

System.c文件内容

#include "System.h"

const static char * TAG = "System";

esp_event_loop_handle_t system_event_loop_handle = NULL;

void system_event_loop_register(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void * event_handler_arg)
{
    esp_event_handler_register_with(system_event_loop_handle, event_base, event_id, event_handler,event_handler_arg);
}

void system_event_loop_unregister(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler)
{
    esp_event_handler_unregister_with(system_event_loop_handle, event_base, event_id, event_handler);
}

void system_event_loop_post_to(esp_event_base_t event_base, int32_t event_id, const void * event_data, size_t event_data_size, TickType_t ticks_to_wait)
{
    esp_event_post_to(system_event_loop_handle, event_base, event_id, event_data, event_data_size, ticks_to_wait);
}

void system_init(void)
{
    esp_event_loop_args_t loop_args = {
        .queue_size = 6,
        .task_name = "event loop",
        .task_priority = 9,
        .task_stack_size = 1024 * 12,
        .task_core_id = 1,
    };

    esp_event_loop_create(&loop_args, &system_event_loop_handle);
    assert(system_event_loop_handle);
    ESP_LOGI(TAG,"Init System");
    led_init();
    motor_init();
    my_wifi_init();
}

CMakeLists.txt文件内容

idf_component_register( SRCS "src/System.c"
                        INCLUDE_DIRS "include"
                        REQUIRES LED Motor WIFI main)

WIFI组件内容:
WIFI.h文件内容

/**
 * @file my_wifi.h
 * @author wsp
 * @brief 
 * @version 0.1
 * @date 2023-05-16
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef _wifi_H_
#define _wifi_H_
#include "System.h"


void my_wifi_init(void);

#endif

WIFI.c文件内容

/**
 * @file 2_WIFI_STA.c
 * @author WSP
 * @brief  WIFI STA
 * @version 0.1
 * @date 2022-10-30
 * @copyright Copyright (c) 2022
 */
#include "my_wifi.h"


#define WIFI_CONNECTED_BIT  BIT0
#define WIFI_FAIL_BIT       BIT1

#define EXAMPLE_ESP_WIFI_SSID       "MEIZU 16th"
#define EXAMPLE_ESP_WIFI_PASSWORD   "qwert12345"
#define EXAMPLE_ESP_MAXIMUM_RETRY   10

// WIFI_AUTH_WEP
// WIFI_AUTH_WPA_PSK
// WIFI_AUTH_WPA2_PSK
// WIFI_AUTH_WPA_WPA2_PSK
// WIFI_AUTH_WPA3_PSK
// WIFI_AUTH_WPA2_WPA3_PSK
// WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK

static EventGroupHandle_t s_wifi_event_group;
const static char * TAG = "wifi station";
static bool wifi_disconnect_flag = false;


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();                         // 连接热点
        wifi_disconnect_flag = false;
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        ESP_LOGI(TAG, "retry to connected to the AP");
        if (wifi_disconnect_flag == false) {
            wifi_disconnect_flag = true;
            system_event_loop_post_to(MOTOR_EVENT_LOOP_BASE, MOTOR_SET_NOT_RUN_STATE, NULL, 0, 10 / portTICK_PERIOD_MS);
            system_event_loop_post_to(LED_EVENT_LOOP_BASE, RED_LED_OFF, NULL, 0, 10 / portTICK_PERIOD_MS);
        }
    } 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));
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();           // 创建事件组
    ESP_ERROR_CHECK(esp_netif_init());                  // 初始化lwip 协议栈
    ESP_ERROR_CHECK(esp_event_loop_create_default());   // 创建event loop
    // 这个API注销wifi处理程序,并从wifi中分离创建的对象。(如果esp_netif为NULL,则此函数为空操作)
    esp_netif_create_default_wifi_sta();        
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();// 获取WIFI默认配置
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));               // 初始化WIFI

    esp_event_handler_instance_t instance_any_id;       // 创建事件句柄变量
    esp_event_handler_instance_t instance_got_ip;       // 创建事件句柄变量
    // 注册WIFI事件
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    // 注册IP事件
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));
    wifi_config_t wifi_config = {
        .sta = {
            .ssid       = EXAMPLE_ESP_WIFI_SSID,                    // 配置STA热点连接的热点名
            .password   = EXAMPLE_ESP_WIFI_PASSWORD,                // 配置要连接的热点密码
            .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,// 在快速扫描模式下接受的最弱authmode
            .pmf_cfg = {
            // 配置管理框配置。将广播在RSN的能力在RSN IE。
            .capable = true,  // 弃用变量。如果其他设备也通告了PMF能力,则设备将始终以PMF模式连接
            .required = false // 声明需要受保护的管理框架。设备将不关联到非pmf能力的设备。
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));              // 设置WIFI的模式
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config)); // 配置WIFI的参数
    ESP_ERROR_CHECK(esp_wifi_start());                              // 开启WIFI

    ESP_LOGI(TAG,"wifi_init_sta finished");    
    // 创建事件组
    EventBits_t bit = xEventGroupWaitBits(  s_wifi_event_group,
                                            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                            pdFALSE,
                                            pdFALSE,
                                            portMAX_DELAY);
    // 连接事件组                                        
    if (bit & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG,"connected to ap SSID :%s passdord:%s",
                EXAMPLE_ESP_WIFI_SSID,
                EXAMPLE_ESP_WIFI_PASSWORD);
    } 
    system_event_loop_post_to(MOTOR_EVENT_LOOP_BASE, MOTOR_SET_RUN_STATE, NULL, 0, 10 / portTICK_PERIOD_MS);
    system_event_loop_post_to(LED_EVENT_LOOP_BASE, RED_LED_ON, NULL, 0, 10 / portTICK_PERIOD_MS);                                
}

void my_wifi_init(void)
{
    // 初始化flash
    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();
}

CMakeLists.txt文件内容

idf_component_register( SRCS "src/my_wifi.c"
                        INCLUDE_DIRS "include"
                        REQUIRES System)

main组件内容:
main.c文件内容

#include "System.h"


void app_main(void)
{
    system_init();
}

CMakeLists.txt文件内容

idf_component_register(SRCS "main.c"
                    INCLUDE_DIRS ".")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java进阶-面向对象进阶(抽象类接口内部类)

1 抽象类 1.1 概述 1.1.1 抽象类引入 ​ 父类中的方法&#xff0c;被它的子类们重写&#xff0c;子类各自的实现都不尽相同。那么父类的方法声明和方法主体&#xff0c;只有声明还有意义&#xff0c;而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话…

Marvin - LLM驱动的AI函数开发包【Python】

Marvin是一个用于构建 AI 驱动软件的Python库。 Marvin 的工作是将 AI 直接集成到你的代码库中&#xff0c;使其看起来和感觉起来与任何其他功能一样。 Marvin 引入了一个新概念&#xff0c;称为 AI 函数&#xff08;AI functions&#xff09;。 AI函数与传统函数的不同之处在…

Java进阶-常用的API

1 Math类 1.1 概述 tips&#xff1a;了解内容 查看API文档&#xff0c;我们可以看到API文档中关于Math类的定义如下&#xff1a; Math类所在包为java.lang包&#xff0c;因此在使用的时候不需要进行导包。并且Math类被final修饰了&#xff0c;因此该类是不能被继承的。 Math类…

springboot+jsp超市库存进销存管理系统-a597o

Spring框架是Java平台的一个开放源代码的Full-stack(全栈)应用程序框架&#xff0c;和控制翻转容器的实现。Spring框架的一些核心功能理论&#xff0c;可以用于所有Java应用&#xff0c;Spring还为Java EE构建的Web应用提供大量的扩展支持。Spring框架没有实现任何的编程模型&a…

PHP生成带有雪花背景的验证码

<?session_start();?> <FORM METHOD=POST ACTION=""> <input type=text name=number maxlength=4><img src="YanZhengMa.php?act=init"> <INPUT TYPE="submit" name="sub"> </FORM><?//检…

【分布族谱】正态分布和二项分布的关系

文章目录 正态分布二项分布验证 正态分布 正态分布&#xff0c;最早由棣莫弗在二项分布的渐近公式中得到&#xff0c;而真正奠定其地位的&#xff0c;应是高斯对测量误差的研究&#xff0c;故而又称Gauss分布。测量是人类定量认识自然界的基础&#xff0c;测量误差的普遍性&am…

本地电脑做服务器搭建私人音乐网站ThinkMusic + cpolar内网穿透

文章目录 1. 前言2. 本地网页搭建2.1 环境使用2.2 支持组建选择 3. 网页安装3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 转发自CSDN lisacpolar的文章&#xff1a;ThinkMusic源码搭建音乐网站&#xff0c;并实现公网访问 1. 前言 在我们的日…

数据结构初阶(1)(一些学习数据结构所需掌握的先导知识:包装类、装箱与拆箱、泛型【泛型的编译——擦除机制、泛型的上界、泛型方法、裸类型】、List简介)

目录 包装类 基本数据类型&#xff08;Primitive Types&#xff09;&#xff1a; 包装类&#xff08;Wrapper Classes&#xff09;&#xff1a; 装箱和拆箱 自动装箱和自动拆箱 泛型 泛型的编译——很重要的擦除机制&#xff1a; 泛型的上界&#xff1a; 泛型方法&…

LC-1335. 工作计划的最低难度(区间DP、记忆化搜索==>动态规划)

1335. 工作计划的最低难度 难度困难97 你需要制定一份 d 天的工作计划表。工作之间存在依赖&#xff0c;要想执行第 i 项工作&#xff0c;你必须完成全部 j 项工作&#xff08; 0 < j < i&#xff09;。 你每天 至少 需要完成一项任务。工作计划的总难度是这 d 天每一…

JavaSE进阶(一)—— 面向对象进阶(static、单例、代码块、继承)

目录 一、static静态关键字 1. static是什么&#xff0c;static修饰成员变量的用法 2. 成员方法的分类 2.1 使用场景 3. static修饰成员方法的内存原理 4. static的注意事项[拓展] 二、static应用知识&#xff1a;工具类 1. 工具类是什么 2. 工具类的好处 3. 为什么工…

域名信息收集

被动信息收集 0x01 Certificate Transparency&#xff08;证书透明&#xff09; 证书透明度(Certificate Transparency)是证书授权机构的一个项目&#xff0c;证书授权机构会将每个SSL/TLS证书发布到公共日志中。一个SSL/TLS证书通常包含域名、子域名和邮件地址。查找某个域名…

Java进阶-字符串的使用

1.API 1.1API概述 什么是API ​ API (Application Programming Interface) &#xff1a;应用程序编程接口 java中的API ​ 指的就是 JDK 中提供的各种功能的 Java类&#xff0c;这些类将底层的实现封装了起来&#xff0c;我们不需要关心这些类是如何实现的&#xff0c;只需要…

PPT技能之描边字,要大要醒目别整没用的

文字仅仅加粗&#xff0c;还是不够明显&#xff0c;不妨用上描边字。不会Photoshop&#xff0c;不会PS&#xff0c;也可以做好看的描边字。 描边字&#xff0c;是在文字的边缘加上边框&#xff0c;让文字更醒目、突出。 你的关注&#xff0c;是我最大的动力&#xff01;你的转…

【K8s】基本存储、高级存储(PV和PVC)、配置存储

文章目录 背景一、基本存储1、EmptyDir2、HostPath3、NFS 二、高级存储1、认识PV和PVC2、PV3、PVC4、生命周期 三、配置存储1、ConfigMap2、 Secret 背景 程序运行在容器中&#xff0c;而容器的生命周期可能极其短暂&#xff0c;容器销毁&#xff0c;数据丢失&#xff0c;因此…

pip install出现socket.timeout: The read operation timed out的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

MySQL 8.0不再担心被垃圾SQL搞爆内存

MySQL 8.0.28引入的新功能 MySQL 8.0.28开始&#xff0c;新增一个特性&#xff0c;支持监控统计并限制各个连接&#xff08;会话&#xff09;的内存消耗&#xff0c;避免大量用户连接因为执行垃圾SQL消耗过多内存&#xff0c;造成可能被OOM kill的风险。 首先&#xff0c;需要…

【Servlet】图解HTTP请求Tomcat服务器实现前后端交互

文章目录 前言如何构造HTTP请求&#xff1f;1、通过form表单构造HTTP请求① form发送GET请求② form发送POST请求 2、通过Ajax构造HTTP请求同步等待与异步等待的感性理解 3、使用postman构造HTTP请求4、通过 Java socket 构造 HTTP 请求 如何实现前端给后端传参1、query string…

Jetpack Compose 中的debug调试

之前在 Jetpack Compose 中的重组作用域和性能优化 一文中主要介绍了如何从代码层面进行一些优化以及提到了一些值得注意的优化事项&#xff0c;本文主要来了解一下官方提供了哪些工具层面的手段可以帮助我们进行Compose的debug性能调试。 常规debug方式 这个跟以前的方式一样…

『python爬虫』22. 初识自动化测试工具selenium(保姆级图文)

目录 1. selenium的安装配置2. 为什么使用selenium&#xff1f;它的优势和缺点是什么&#xff1f;3. selenium的基本使用4. selenium实战之csdn搜索python博文4.1 点击选择文章4.2 搜索栏输入搜索关键词4.3 实现代码 总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 …

【MYSQL】索引的原理(B+树实现)和操作

目录 1.MYSQL与磁盘交互的基本单位 2.索引的原理 3.聚簇索引 4.索引操作 认识磁盘 1.MYSQL与磁盘交互的基本单位 为了提高基本的IO效率&#xff0c; MySQL进行IO的基本单位是16KB 一次使用16KB&#xff08;称为page&#xff09;为单位大于磁道的扇面&#xff08;521B&…