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

一、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 ".")

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐