[05]ESP32 事件循环使用备忘录(基于 esp_event library)


文章更新版本 时间 备注
V0.1 发布于2019-08-15 00:36:51 初始版本
V0.2 更新于2020-04-23 20:10:16 增加默认事件循环,增加注意事项2/3

文章目录

    • 事件循环含义
    • ESP_IDF事件循环使用过程
    • 默认事件循环
    • 事件循环调试
    • 使用事件循环注意事项

事件循环含义

  • 如果学习过QT信号与槽,或者其他消息机制,这一部分会很好上手。ESP_IDF中的事件循环机制,可以实现不同组件之间的事件传递,能够降低不同组件之间的耦合,自由组合资源。举个简单的例子理解:
    A,B,C 三个节点想实现相互之间的消息传递,一种简单的方法是,A,B,C 分别不停的去访问其他节点,询问是否有新事件发生。这样势必会增加整体计算的负担,另外也会涉及到很多问题,比如何时清除事件标志,保证既能够通知到所有关心该事件的节点又能防止重复的通知。
  • 如果这时候引入D去统一管理,会把问题极大的简化。A,B,C分别到D处登记自己关心的事件,登记完成后由D去进行问询和分发,A,B,C可以专心做自己的事情,当需要发信号时,直接发送到D,当D发现有事件发生后,会根据登记的内容和先后顺序,通知对应的节点去处理。

ESP_IDF事件循环使用过程

  1. 使用ESP_EVENT_DECLARE_BASEESP_EVENT_DEFINE_BASE声明和定义事件base,使用枚举类型定义事件id,详情参考Declaring and defining events
  2. 定义事件处理函数 esp_event_handler_t
  3. 创建一个事件循环esp_event_loop_create(),创建完成后会返回事件循环句柄(作用相当于D)
  4. 向事件循环注册事件esp_event_handler_register_with(),添加回调函数。(注册关注哪个事件,事件发生后执行哪个函数)
  5. 事件发生时,向事件循环发生通知esp_event_post_to()。
// 1. Define the event handler
void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    // Event handler logic
}

void app_main()
{
    // 2. A configuration structure of type esp_event_loop_args_t is needed to specify the properties of the loop to be
    // created. A handle of type esp_event_loop_handle_t is obtained, which is needed by the other APIs to reference the loop
    // to perform their operations on.
    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. Register event handler defined in (1). MY_EVENT_BASE and MY_EVENT_ID specifies a hypothetical
    // event that handler run_on_event should execute on when it gets posted to the loop.
    esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event, ...);

    ...

    // 4. Post events to the loop. This queues the event on the event loop. At some point in time
    // the event loop executes the event handler registered to the posted event, in this case run_on_event.
    // For simplicity sake this example calls esp_event_post_to from app_main, but posting can be done from
    // any other tasks (which is the more interesting use case).
    esp_event_post_to(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, ...);

    ...

    // 5. Unregistering an unneeded handler
    esp_event_handler_unregister_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event);

    ...

    // 6. Deleting an unneeded event loop
    esp_event_loop_delete(loop_handle);
}

默认事件循环

User Event Loops Default 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 接口上有一些区别,前者不需要传入事件循环句柄。
  • 默认事件循环用于传递系统事件,例如 WiFi 事件等。
  • 默认事件循环也可以注册和订阅用户事件,优点是不需要创建单独的用户事件循环,节省空间。

事件循环调试

可以通过配置 CONFIG_ESP_EVENT_LOOP_PROFILING开启对系统内所有事件循环的信息收集,具体参考: esp_event_dump()

使用事件循环注意事项

  1. 以上方法是来自新的ESP_IDF功能设计,我们在网上例程中会经常见到

    esp_event_loop_init(system_event_cb_tcb, void *ctx)
    

    这是向上兼容的比较早期的事件循环的版本,这个事件循环用于接收系统事件(如WiFi链接,具体可看system_event_info_t),另外在初始化就限定了只能有一个回调函数,功能不如现在的庞大。可以在这里查阅详情esp_event_legacy

  2. 一个handle函数可以与多个event绑定。方法有两种,第一种是多次调用esp_event_handler_register_with();第二种是传入ESP_EVENT_ANY_BASE或者ESP_EVENT_ANY_ID实现与同一个base或同一个id的event绑定。具体可参考:Notes on Handler Registration

  3. 在一个task中如果一个事件注册了多个handle,事件循环将按照注册的先后顺序进行执行。但是如果在多个task中进行注册,需要考虑任务切换导致的注册顺序的不确定性。具体参考:Handler Registration and Handler Dispatch Order

你可能感兴趣的:(ESP系列物联网开发教程)