event: Implement event loop library

This commit is contained in:
Renz Christian Bagaporo 2018-10-26 13:14:19 +08:00 committed by Angus Gratton
parent cdb10ec5ad
commit 5a83347bec
40 changed files with 3760 additions and 5 deletions

View File

@ -928,6 +928,12 @@ example_test_001_01:
- ESP32
- Example_WIFI
example_test_001_02:
<<: *example_test_template
tags:
- ESP32
- Example_WIFI
example_test_002_01:
<<: *example_test_template
image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
@ -1168,7 +1174,19 @@ UT_001_36:
tags:
- ESP32_IDF
- UT_T1_1
UT_001_37:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
UT_001_38:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
UT_002_01:
<<: *unit_test_template
tags:

View File

@ -62,7 +62,7 @@ else()
"hwcrypto/sha.c")
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_REQUIRES driver tcpip_adapter)
set(COMPONENT_REQUIRES driver tcpip_adapter esp_event)
# driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t
# tcpip_adapter is a public requirement because esp_event.h uses tcpip_adapter types
set(COMPONENT_PRIV_REQUIRES

View File

@ -18,8 +18,8 @@
#include "esp_err.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_event_legacy.h"
#include "esp_task.h"
#include "esp_mesh.h"

View File

@ -0,0 +1,9 @@
set(COMPONENT_SRCS "default_event_loop.c"
"esp_event.c"
"esp_event_private.c")
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_PRIV_INCLUDEDIRS "private_include")
set(COMPONENT_REQUIRES log)
register_component()

View File

@ -0,0 +1,11 @@
menu "Event Loop Library"
config EVENT_LOOP_PROFILING
bool "Enable event loop profiling"
default n
help
Enables collections of statistics in the event loop library such as the number of events posted to/recieved by an event loop, number of
callbacks involved, number of events dropped to to a full event loop queue, run time of event handlers, and number of times/run
time of each event handler.
endmenu

View File

@ -0,0 +1,6 @@
#
# Component Makefile
#
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include
COMPONENT_SRCDIRS := .

View File

@ -0,0 +1,101 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_event.h"
#include "esp_event_internal.h"
/* ------------------------- Static Variables ------------------------------- */
static esp_event_loop_handle_t s_default_loop = NULL;
/* ---------------------------- Public API ---------------------------------- */
esp_err_t esp_event_handler_register(esp_event_base_t event_base, int32_t event_id,
esp_event_handler_t event_handler, void* event_handler_arg)
{
if (s_default_loop == NULL) {
return ESP_ERR_INVALID_STATE;
}
return esp_event_handler_register_with(s_default_loop, event_base, event_id,
event_handler, event_handler_arg);
}
esp_err_t esp_event_handler_unregister(esp_event_base_t event_base, int32_t event_id,
esp_event_handler_t event_handler)
{
if (s_default_loop == NULL) {
return ESP_ERR_INVALID_STATE;
}
return esp_event_handler_unregister_with(s_default_loop, event_base, event_id,
event_handler);
}
esp_err_t esp_event_post(esp_event_base_t event_base, int32_t event_id,
void* event_data, size_t event_data_size, TickType_t ticks_to_wait)
{
if (s_default_loop == NULL) {
return ESP_ERR_INVALID_STATE;
}
return esp_event_post_to(s_default_loop, event_base, event_id,
event_data, event_data_size, ticks_to_wait);
}
esp_err_t esp_event_loop_create_default()
{
if (s_default_loop) {
return ESP_ERR_INVALID_STATE;
}
esp_event_loop_args_t loop_args = {
.queue_size = CONFIG_SYSTEM_EVENT_QUEUE_SIZE,
.task_name = "sys_evt",
.task_stack_size = ESP_TASKD_EVENT_STACK,
.task_priority = ESP_TASKD_EVENT_PRIO,
.task_core_id = 0
};
esp_err_t err;
err = esp_event_loop_create(&loop_args, &s_default_loop);
if (err != ESP_OK) {
return err;
}
return ESP_OK;
}
esp_err_t esp_event_loop_delete_default()
{
if (!s_default_loop) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t err;
err = esp_event_loop_delete(s_default_loop);
if (err != ESP_OK) {
return err;
}
s_default_loop = NULL;
return ESP_OK;
}

View File

@ -0,0 +1,856 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "esp_log.h"
#include "esp_event.h"
#include "esp_event_internal.h"
#include "esp_event_private.h"
#ifdef CONFIG_EVENT_LOOP_PROFILING
#include "esp_timer.h"
#endif
/* ---------------------------- Definitions --------------------------------- */
#ifdef CONFIG_EVENT_LOOP_PROFILING
// loop@<address,name> rx:<total_recieved> dr:<total_dropped> inv:<total_number_of_invocations> run:<total_runtime>
#define LOOP_DUMP_FORMAT "loop@%p,%s rx:%u dr:%u inv:%u run:%lld us\n"
// event@<base:id> proc:<total_processed> run:<total_runtime>
#define EVENT_DUMP_FORMAT "\tevent@%s:%d proc:%u run:%lld us\n"
// handler@<address> inv:<total_invoked> run:<total_runtime>
#define HANDLER_DUMP_FORMAT "\t\thandler@%p inv:%u run:%lld us\n"
#define PRINT_DUMP_INFO(dst, sz, ...) do { \
int cb = snprintf(dst, sz, __VA_ARGS__); \
dst += cb; \
sz -= cb; \
} while(0);
#endif
/* ------------------------- Static Variables ------------------------------- */
static const char* TAG = "event";
static const char* esp_event_any_base = "any";
#ifdef CONFIG_EVENT_LOOP_PROFILING
static SLIST_HEAD(esp_event_loop_instance_list_t, esp_event_loop_instance) s_event_loops =
SLIST_HEAD_INITIALIZER(s_event_loops);
static portMUX_TYPE s_event_loops_spinlock = portMUX_INITIALIZER_UNLOCKED;
#endif
/* ------------------------- Static Functions ------------------------------- */
#ifdef CONFIG_EVENT_LOOP_PROFILING
static int esp_event_dump_prepare()
{
esp_event_loop_instance_t* loop_it;
esp_event_base_instance_t* base_it;
esp_event_id_instance_t* id_it;
esp_event_handler_instance_t* handler_it;
// Count the number of items to be printed. This is needed to compute how much memory to reserve.
int loops = 0, events = 0, handlers = 0;
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_FOREACH(loop_it, &s_event_loops, loop_entry) {
SLIST_FOREACH(handler_it, &(loop_it->loop_handlers), handler_entry) {
handlers++;
}
SLIST_FOREACH(base_it, &(loop_it->event_bases), event_base_entry) {
SLIST_FOREACH(handler_it, &(base_it->base_handlers), handler_entry) {
handlers++;
}
// Print event-level handlers
SLIST_FOREACH(id_it, &(base_it->event_ids), event_id_entry) {
SLIST_FOREACH(handler_it, &(id_it->handlers), handler_entry) {
handlers++;
}
events++;
}
events++;
}
events++;
loops++;
}
portEXIT_CRITICAL(&s_event_loops_spinlock);
// Reserve slightly more memory than computed
int allowance = 3;
int size = (((loops + allowance) * (sizeof(LOOP_DUMP_FORMAT) + 10 + 20 + 3 * 11 + 20 )) +
((events + allowance) * (sizeof(EVENT_DUMP_FORMAT) + 10 + 20 + 11 + 20)) +
((handlers + allowance) * (sizeof(HANDLER_DUMP_FORMAT) + 10 + 11 + 20)));
return size;
}
#endif
static void esp_event_loop_run_task(void* args)
{
esp_err_t err;
esp_event_loop_handle_t event_loop = (esp_event_loop_handle_t) args;
ESP_LOGD(TAG, "running task for loop %p", event_loop);
while(1) {
err = esp_event_loop_run(event_loop, portMAX_DELAY);
if (err != ESP_OK) {
break;
}
}
ESP_LOGE(TAG, "suspended task for loop %p", event_loop);
vTaskSuspend(NULL);
}
// Functions that operate on handler instance
static esp_event_handler_instance_t* handler_instance_create(esp_event_handler_t event_handler, void* event_handler_arg)
{
esp_event_handler_instance_t* handler_instance = calloc(1, sizeof(*handler_instance));
if (handler_instance != NULL) {
handler_instance->handler = event_handler;
handler_instance->arg = event_handler_arg;
}
return handler_instance;
}
static void handler_instance_delete(esp_event_handler_instance_t* handler_instance)
{
free(handler_instance);
}
// Functions that operate on handler instance list
static esp_event_handler_instance_t* handler_instances_find(esp_event_handler_instances_t* handlers, esp_event_handler_t handler)
{
esp_event_handler_instance_t* it;
SLIST_FOREACH(it, handlers, handler_entry) {
if (it->handler == handler) {
break;
}
}
return it;
}
static void handler_instances_add(esp_event_handler_instances_t* handlers, esp_event_handler_instance_t* handler_instance)
{
SLIST_INSERT_HEAD(handlers, handler_instance, handler_entry);
}
static void handler_instances_remove(esp_event_handler_instances_t* handlers, esp_event_handler_instance_t* handler_instance)
{
SLIST_REMOVE(handlers, handler_instance, esp_event_handler_instance, handler_entry);
handler_instance_delete(handler_instance);
}
static void handler_instances_remove_all(esp_event_handler_instances_t* handlers)
{
esp_event_handler_instance_t* it;
esp_event_handler_instance_t* temp;
SLIST_FOREACH_SAFE(it, handlers, handler_entry, temp) {
handler_instances_remove(handlers, it);
}
}
// Functions that operate on event id instance
static void* event_id_instance_create(int32_t event_id)
{
esp_event_id_instance_t* event_id_instance = calloc(1, sizeof(*event_id_instance));
if (event_id_instance != NULL) {
event_id_instance->id = event_id;
SLIST_INIT(&(event_id_instance->handlers));
}
return event_id_instance;
}
static void event_id_instance_delete(esp_event_id_instance_t* event_id_instance)
{
handler_instances_remove_all(&(event_id_instance->handlers));
free(event_id_instance);
}
// Functions that operate on event id instance list
static void event_id_instances_remove(esp_event_id_instances_t* head, esp_event_id_instance_t* event_id_instance)
{
SLIST_REMOVE(head, event_id_instance, esp_event_id_instance, event_id_entry);
event_id_instance_delete(event_id_instance);
}
// Functions that operate on event base instance
static esp_event_base_instance_t* event_base_instance_create(esp_event_base_t event_base)
{
esp_event_base_instance_t* event_base_instance = calloc(1, sizeof(*event_base_instance));
if (event_base_instance != NULL) {
event_base_instance->base = event_base;
SLIST_INIT(&(event_base_instance->base_handlers));
SLIST_INIT(&(event_base_instance->event_ids));
}
return event_base_instance;
}
static void event_base_instance_delete(esp_event_base_instance_t* event_base_instance)
{
esp_event_id_instance_t* it;
esp_event_id_instance_t* temp;
handler_instances_remove_all(&(event_base_instance->base_handlers));
SLIST_FOREACH_SAFE(it, &(event_base_instance->event_ids), event_id_entry, temp) {
event_id_instances_remove(&(event_base_instance->event_ids), it);
}
free(event_base_instance);
}
static void event_base_instance_add_event_id_instance(esp_event_base_instance_t* event_base_instance, esp_event_id_instance_t* event_id_instance)
{
SLIST_INSERT_HEAD(&(event_base_instance->event_ids), event_id_instance, event_id_entry);
}
static esp_event_id_instance_t* event_base_instance_find_event_id_instance(esp_event_base_instance_t* event_base_instance, int32_t event_id)
{
esp_event_id_instance_t* it;
SLIST_FOREACH(it, &(event_base_instance->event_ids), event_id_entry) {
if (it->id == event_id) {
break;
}
}
return it;
}
// Functions that operate on event base instances list
static void event_base_instances_remove(esp_event_base_instances_t* head, esp_event_base_instance_t* event_base_instance)
{
SLIST_REMOVE(head, event_base_instance, esp_event_base_instance, event_base_entry);
event_base_instance_delete(event_base_instance);
}
// Functions that operate on loop instances
static void loop_add_event_base_instance(esp_event_loop_instance_t* loop, esp_event_base_instance_t* event_base_instance) {
SLIST_INSERT_HEAD(&(loop->event_bases), event_base_instance, event_base_entry);
}
static void loop_remove_all_event_base_instance(esp_event_loop_instance_t* loop)
{
esp_event_base_instance_t* it;
esp_event_base_instance_t* temp;
SLIST_FOREACH_SAFE(it, &(loop->event_bases), event_base_entry, temp) {
event_base_instances_remove(&(loop->event_bases), it);
}
}
static esp_event_base_instance_t* loop_find_event_base_instance(esp_event_loop_instance_t* loop, esp_event_base_t event_base)
{
esp_event_base_instance_t* it;
SLIST_FOREACH(it, &(loop->event_bases), event_base_entry) {
if (it->base == event_base) {
break;
}
}
return it;
}
// Functions that operate on post instance
static esp_err_t post_instance_create(esp_event_base_t event_base, int32_t event_id, void* event_data, int32_t event_data_size, esp_event_post_instance_t* post)
{
void** event_data_copy = NULL;
// Make persistent copy of event data on heap.
if (event_data != NULL && event_data_size != 0) {
event_data_copy = calloc(1, event_data_size);
if (event_data_copy == NULL) {
ESP_LOGE(TAG, "alloc for post data to event %s:%d failed", event_base, event_id);
return ESP_ERR_NO_MEM;
}
memcpy(event_data_copy, event_data, event_data_size);
}
post->base = event_base;
post->id = event_id;
post->data = event_data_copy;
ESP_LOGD(TAG, "created post for event %s:%d", event_base, event_id);
return ESP_OK;
}
static void post_instance_delete(esp_event_post_instance_t* post)
{
free(post->data);
}
static esp_event_handler_instances_t* find_handlers_list(esp_event_loop_instance_t* loop, esp_event_base_t event_base,
int32_t event_id)
{
esp_event_handler_instances_t* handlers = NULL;
esp_event_base_instance_t* base = NULL;
esp_event_id_instance_t* event = NULL;
if (event_base == esp_event_any_base && event_id == ESP_EVENT_ANY_ID) {
handlers = &(loop->loop_handlers);
} else {
base = loop_find_event_base_instance(loop, event_base);
if (base != NULL) {
if (event_id == ESP_EVENT_ANY_ID) {
handlers = &(base->base_handlers);
} else {
event = event_base_instance_find_event_id_instance(base, event_id);
if (event != NULL) {
handlers = &(event->handlers);
}
}
}
}
return handlers;
}
/* ---------------------------- Public API --------------------------------- */
esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, esp_event_loop_handle_t* event_loop)
{
assert(event_loop_args);
esp_event_loop_instance_t* loop;
esp_err_t err = ESP_ERR_NO_MEM; // most likely error
loop = calloc(1, sizeof(*loop));
if (loop == NULL) {
ESP_LOGE(TAG, "alloc for event loop failed");
goto on_err;
}
loop->queue = xQueueCreate(event_loop_args->queue_size , sizeof(esp_event_post_instance_t));
if (loop->queue == NULL) {
ESP_LOGE(TAG, "create event loop queue failed");
goto on_err;
}
loop->mutex = xSemaphoreCreateRecursiveMutex();
if (loop->mutex == NULL) {
ESP_LOGE(TAG, "create event loop mutex failed");
goto on_err;
}
#ifdef CONFIG_EVENT_LOOP_PROFILING
loop->profiling_mutex = xSemaphoreCreateMutex();
if (loop->profiling_mutex == NULL) {
ESP_LOGE(TAG, "create event loop profiling mutex failed");
goto on_err;
}
#endif
SLIST_INIT(&(loop->loop_handlers));
SLIST_INIT(&(loop->event_bases));
// Create the loop task if requested
if (event_loop_args->task_name != NULL) {
BaseType_t task_created = xTaskCreatePinnedToCore(esp_event_loop_run_task, event_loop_args->task_name,
event_loop_args->task_stack_size, (void*) loop,
event_loop_args->task_priority, &(loop->task), event_loop_args->task_core_id);
if (task_created != pdPASS) {
ESP_LOGE(TAG, "create task for loop failed");
err = ESP_FAIL;
goto on_err;
}
loop->name = event_loop_args->task_name;
ESP_LOGD(TAG, "created task for loop %p", loop);
} else {
loop->name = "";
loop->task = NULL;
}
loop->running_task = NULL;
#ifdef CONFIG_EVENT_LOOP_PROFILING
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_INSERT_HEAD(&s_event_loops, loop, loop_entry);
portEXIT_CRITICAL(&s_event_loops_spinlock);
#endif
*event_loop = (esp_event_loop_handle_t) loop;
ESP_LOGD(TAG, "created event loop %p", loop);
return ESP_OK;
on_err:
if(loop->queue != NULL) {
vQueueDelete(loop->queue);
}
if(loop->mutex != NULL) {
vSemaphoreDelete(loop->mutex);
}
#ifdef CONFIG_EVENT_LOOP_PROFILING
if(loop->profiling_mutex != NULL) {
vSemaphoreDelete(loop->profiling_mutex);
}
#endif
free(loop);
return err;
}
// On event lookup performance: The library implements the event list as a linked list, which results to O(n)
// lookup time. The test comparing this implementation to the O(lg n) performance of rbtrees
// (https://github.com/freebsd/freebsd/blob/master/sys/sys/tree.h)
// indicate that the difference is not that substantial, especially considering the additional
// pointers per node of rbtrees. Code for the rbtree implementation of the event loop library is archived
// in feature/esp_event_loop_library_rbtrees if needed.
esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t ticks_to_run)
{
assert(event_loop);
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
esp_event_post_instance_t post;
TickType_t marker = xTaskGetTickCount();
TickType_t end = 0;
esp_event_handler_instance_t* temp;
#if( configUSE_16_BIT_TICKS == 1 )
int32_t remaining_ticks = ticks_to_run;
#else
int64_t remaining_ticks = ticks_to_run;
#endif
while(xQueueReceive(loop->queue, &post, ticks_to_run) == pdTRUE) {
esp_event_base_instance_t* base = NULL;
esp_event_id_instance_t* event = NULL;
// Reserve space for three possible matches: (1) the entry for handlers registered to all events in the loop, the
// (2) entry matching events with a specified base and (3) the entry matching both base and id.
#define LOOP_LEVEL_HANDLER 0
#define BASE_LEVEL_HANDLER 1
#define EVENT_LEVEL_HANDLER 2
esp_event_handler_instances_t* handlers_list[EVENT_LEVEL_HANDLER + 1] = {0};
// The event has already been unqueued, so ensure it gets executed.
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
loop->running_task = xTaskGetCurrentTaskHandle();
handlers_list[LOOP_LEVEL_HANDLER] = &(loop->loop_handlers);
base = loop_find_event_base_instance(loop, post.base);
if (base) {
event = event_base_instance_find_event_id_instance(base, post.id);
handlers_list[BASE_LEVEL_HANDLER] = &(base->base_handlers);
if (event) {
handlers_list[EVENT_LEVEL_HANDLER] = &(event->handlers);
}
}
bool exec = false;
for (int i = LOOP_LEVEL_HANDLER; i <= EVENT_LEVEL_HANDLER; i++) {
if (handlers_list[i] != NULL) {
esp_event_handler_instance_t* it;
SLIST_FOREACH_SAFE(it, handlers_list[i], handler_entry, temp) {
ESP_LOGD(TAG, "running post %s:%d with handler %p on loop %p", post.base, post.id, it->handler, event_loop);
#ifdef CONFIG_EVENT_LOOP_PROFILING
int64_t start, diff;
start = esp_timer_get_time();
#endif
// Execute the handler
(*(it->handler))(it->arg, post.base, post.id, post.data);
#ifdef CONFIG_EVENT_LOOP_PROFILING
diff = esp_timer_get_time() - start;
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
it->total_times_invoked++;
it->total_runtime += diff;
if (i == LOOP_LEVEL_HANDLER) {
loop->loop_handlers_invoked++;
loop->loop_handlers_runtime += diff;
} else if (i == BASE_LEVEL_HANDLER) {
base->base_handlers_invoked++;
base->base_handlers_runtime += diff;
} else {
event->handlers_invoked++;
event->handlers_runtime += diff;
}
loop->total_handlers_invoked++;
loop->total_handlers_runtime += diff;
xSemaphoreGive(loop->profiling_mutex);
#endif
}
}
exec |= true;
}
if (ticks_to_run != portMAX_DELAY) {
end = xTaskGetTickCount();
remaining_ticks -= end - marker;
// If the ticks to run expired, return to the caller
if (remaining_ticks <= 0) {
xSemaphoreGiveRecursive(loop->mutex);
break;
} else {
marker = end;
}
}
loop->running_task = NULL;
xSemaphoreGiveRecursive(loop->mutex);
if (!exec) {
// No handlers were registered, not even loop/base level handlers
ESP_LOGW(TAG, "no handlers have been registered for event %s:%d posted to loop %p", post.base, post.id, event_loop);
}
}
return ESP_OK;
}
esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
{
assert(event_loop);
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
SemaphoreHandle_t loop_mutex = loop->mutex;
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
#ifdef CONFIG_EVENT_LOOP_PROFILING
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_REMOVE(&s_event_loops, loop, esp_event_loop_instance, loop_entry);
portEXIT_CRITICAL(&s_event_loops_spinlock);
#endif
// Delete the task if it was created
if (loop->task != NULL) {
vTaskDelete(loop->task);
}
// Remove all registered events in the loop
handler_instances_remove_all(&(loop->loop_handlers));
loop_remove_all_event_base_instance(loop);
// Drop existing posts on the queue
esp_event_post_instance_t post;
while(xQueueReceive(loop->queue, &post, 0) == pdTRUE) {
free(post.data);
}
// Cleanup loop
vQueueDelete(loop->queue);
free(loop);
// Free loop mutex before deleting
xSemaphoreGiveRecursive(loop_mutex);
vSemaphoreDelete(loop_mutex);
ESP_LOGD(TAG, "deleted loop %p", (void*) event_loop);
return ESP_OK;
}
esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
int32_t event_id, esp_event_handler_t event_handler, void* event_handler_arg)
{
assert(event_loop);
assert(event_handler);
if (event_base == ESP_EVENT_ANY_BASE && event_id != ESP_EVENT_ANY_ID) {
ESP_LOGE(TAG, "registering to any event base with specific id unsupported");
return ESP_ERR_INVALID_ARG;
}
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
esp_event_base_instance_t* base = NULL;
esp_event_id_instance_t* event = NULL;
esp_event_handler_instance_t* handler = NULL;
esp_event_handler_instances_t* handlers = NULL;
bool base_created = false;
bool event_created = false;
if (event_base == ESP_EVENT_ANY_BASE) {
event_base = esp_event_any_base;
}
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
if (event_base == esp_event_any_base && event_id == ESP_EVENT_ANY_ID) {
// Add to the loop-level handlers
handlers = &(loop->loop_handlers);
} else {
// If base instance does not exist, create one
if ((base = loop_find_event_base_instance(loop, event_base)) == NULL) {
base = event_base_instance_create(event_base);
if (base == NULL) {
xSemaphoreGiveRecursive(loop->mutex);
return ESP_ERR_NO_MEM;
}
base_created = true;
}
// Add to the event base instance level handlers
if (event_id == ESP_EVENT_ANY_ID) {
handlers = &(base->base_handlers);
} else {
if (base_created ||
(event = event_base_instance_find_event_id_instance(base, event_id)) == NULL) {
event = event_id_instance_create(event_id);
// If it does not exist, create one
if (event == NULL) {
if (base_created) {
event_base_instance_delete(base);
}
xSemaphoreGiveRecursive(loop->mutex);
return ESP_ERR_NO_MEM;
}
event_created = true;
}
// Add to the event id instance level handlers
handlers = &(event->handlers);
}
}
// Add handler to the list
if (base_created || event_created ||
(handler = handler_instances_find(handlers, event_handler)) == NULL) {
handler = handler_instance_create(event_handler, event_handler_arg);
if (handler == NULL) {
if (event_created) {
event_id_instance_delete(event);
}
if (base_created) {
event_base_instance_delete(base);
}
xSemaphoreGiveRecursive(loop->mutex);
return ESP_ERR_NO_MEM;
}
handler_instances_add(handlers, handler);
// If a new event base/ event id instance was created, add them to the appropriate list
if (event_created) {
event_base_instance_add_event_id_instance(base, event);
}
if (base_created) {
loop_add_event_base_instance(loop, base);
}
ESP_LOGD(TAG, "registered handler %p for event %s:%d", event_handler, event_base, event_id);
} else {
handler->arg = event_handler_arg;
ESP_LOGW(TAG, "handler %p for event %s:%d already registered, overwriting", event_handler, event_base, event_id);
}
xSemaphoreGiveRecursive(loop->mutex);
return ESP_OK;
}
esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
int32_t event_id, esp_event_handler_t event_handler)
{
assert(event_loop);
assert(event_handler);
if (event_base == ESP_EVENT_ANY_BASE && event_id != ESP_EVENT_ANY_ID) {
ESP_LOGE(TAG, "unregistering to any event base with specific id unsupported");
return ESP_FAIL;
}
if (event_base == ESP_EVENT_ANY_BASE) {
event_base = esp_event_any_base;
}
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
esp_event_handler_instance_t* handler = NULL;
esp_event_handler_instances_t* handlers = find_handlers_list(loop, event_base, event_id);
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
if (handlers != NULL &&
(handler = handler_instances_find(handlers, event_handler)) != NULL) {
handler_instances_remove(handlers, handler);
ESP_LOGD(TAG, "unregistered handler %p from event %s:%d", event_handler, event_base, event_id);
} else {
ESP_LOGW(TAG, "handler %p for event %s:%d not registered, ignoring", event_handler, event_base, event_id);
}
xSemaphoreGiveRecursive(loop->mutex);
return ESP_OK;
}
esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop, esp_event_base_t event_base, int32_t event_id,
void* event_data, size_t event_data_size, TickType_t ticks_to_wait)
{
assert(event_loop);
if (event_base == ESP_EVENT_ANY_BASE || event_id == ESP_EVENT_ANY_ID) {
ESP_LOGE(TAG, "posting nonspecific event base or id unsupported");
return ESP_ERR_INVALID_ARG;
}
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
esp_event_post_instance_t post;
esp_err_t err = post_instance_create(event_base, event_id, event_data, event_data_size, &post);
if (err != ESP_OK) {
return err;
}
BaseType_t result = pdFALSE;
// Find the task that currently executes the loop. It is safe to query loop->task since it is
// not mutated since loop creation. ENSURE THIS REMAINS TRUE.
if (loop->task == NULL) {
// The loop has no dedicated task. Find out what task is currently running it.
result = xSemaphoreTakeRecursive(loop->mutex, ticks_to_wait);
if (result == pdTRUE) {
if (loop->running_task != xTaskGetCurrentTaskHandle()) {
xSemaphoreGiveRecursive(loop->mutex);
result = xQueueSendToBack(loop->queue, &post, ticks_to_wait);
} else {
xSemaphoreGiveRecursive(loop->mutex);
result = xQueueSendToBack(loop->queue, &post, 0);
}
}
} else {
// The loop has a dedicated task.
if (loop->task != xTaskGetCurrentTaskHandle()) {
result = xQueueSendToBack(loop->queue, &post, ticks_to_wait);
} else {
result = xQueueSendToBack(loop->queue, &post, 0);
}
}
if (result != pdTRUE) {
post_instance_delete(&post);
#ifdef CONFIG_EVENT_LOOP_PROFILING
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
loop->events_dropped++;
xSemaphoreGive(loop->profiling_mutex);
#endif
return ESP_ERR_TIMEOUT;
}
#ifdef CONFIG_EVENT_LOOP_PROFILING
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
loop->events_recieved++;
xSemaphoreGive(loop->profiling_mutex);
#endif
ESP_LOGD(TAG, "posted %s:%d to loop %p", post.base, post.id, event_loop);
return ESP_OK;
}
esp_err_t esp_event_dump(FILE* file)
{
#ifdef CONFIG_EVENT_LOOP_PROFILING
assert(file);
esp_event_loop_instance_t* loop_it;
esp_event_base_instance_t* base_it;
esp_event_id_instance_t* id_it;
esp_event_handler_instance_t* handler_it;
// Allocate memory for printing
int sz = esp_event_dump_prepare();
char* buf = calloc(sz, sizeof(char));
char* dst = buf;
// Print info to buffer
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_FOREACH(loop_it, &s_event_loops, loop_entry) {
PRINT_DUMP_INFO(dst, sz, LOOP_DUMP_FORMAT, loop_it, loop_it->name, loop_it->events_recieved,
loop_it->events_dropped, loop_it->total_handlers_invoked, loop_it->total_handlers_runtime);
// Print loop-level handler
PRINT_DUMP_INFO(dst, sz, esp_event_any_base, ESP_EVENT_ANY_ID, loop_it->loop_handlers_invoked,
loop_it->loop_handlers_runtime);
SLIST_FOREACH(handler_it, &(loop_it->loop_handlers), handler_entry) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler, handler_it->total_times_invoked,
handler_it->total_runtime);
}
SLIST_FOREACH(base_it, &(loop_it->event_bases), event_base_entry) {
// Print base-level handler
PRINT_DUMP_INFO(dst, sz, EVENT_DUMP_FORMAT, base_it->base, ESP_EVENT_ANY_ID,
base_it->base_handlers_invoked, base_it->base_handlers_runtime);
SLIST_FOREACH(handler_it, &(base_it->base_handlers), handler_entry) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler,
handler_it->total_times_invoked, handler_it->total_runtime);
}
// Print event-level handlers
SLIST_FOREACH(id_it, &(base_it->event_ids), event_id_entry) {
PRINT_DUMP_INFO(dst, sz, EVENT_DUMP_FORMAT, base_it->base, id_it->id,
id_it->handlers_invoked, id_it->handlers_runtime);
SLIST_FOREACH(handler_it, &(id_it->handlers), handler_entry) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler,
handler_it->total_times_invoked, handler_it->total_runtime);
}
}
}
}
portEXIT_CRITICAL(&s_event_loops_spinlock);
// Print the contents of the buffer to the file
fprintf(file, buf);
// Free the allocated buffer
free(buf);
#endif
return ESP_OK;
}

View File

@ -0,0 +1,44 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_event_private.h"
#include "esp_event_internal.h"
#include "esp_log.h"
bool esp_event_is_handler_registered(esp_event_loop_handle_t event_loop, esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler)
{
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
bool result = false;
xSemaphoreTake(loop->mutex, portMAX_DELAY);
esp_event_base_instance_t* base_it;
SLIST_FOREACH(base_it, &(loop->event_bases), event_base_entry) {
esp_event_id_instance_t* event_it;
SLIST_FOREACH(event_it, &(base_it->event_ids), event_id_entry) {
esp_event_handler_instance_t* handler_it;
SLIST_FOREACH(handler_it, &(event_it->handlers), handler_entry) {
if (base_it->base == event_base && event_it->id == event_id && handler_it->handler == event_handler) {
result = true;
goto out;
}
}
}
}
out:
xSemaphoreGive(loop->mutex);
return result;
}

View File

@ -0,0 +1,336 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_EVENT_H_
#define ESP_EVENT_H_
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_event_base.h"
#include "esp_event_legacy.h"
#ifdef __cplusplus
extern "C" {
#endif
/// Configuration for creating event loops
typedef struct {
int32_t queue_size; /**< size of the event loop queue */
const char* task_name; /**< name of the event loop task; if NULL,
a dedicated task is not created for event loop*/
UBaseType_t task_priority; /**< priority of the event loop task, ignored if task name is NULL */
uint32_t task_stack_size; /**< stack size of the event loop task, ignored if task name is NULL */
BaseType_t task_core_id; /**< core to which the event loop task is pinned to,
ignored if task name is NULL */
} esp_event_loop_args_t;
/**
* @brief Create a new event loop.
*
* @param[in] event_loop_args configuration structure for the event loop to create
* @param[out] event_loop handle to the created event loop
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list
* - ESP_FAIL: Failed to create task loop
* - Others: Fail
*/
esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, esp_event_loop_handle_t* event_loop);
/**
* @brief Delete an existing event loop.
*
* @param[in] event_loop event loop to delete
*
* @return
* - ESP_OK: Success
* - Others: Fail
*/
esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop);
/**
* @brief Create default event loop
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list
* - ESP_FAIL: Failed to create task loop
* - Others: Fail
*/
esp_err_t esp_event_loop_create_default();
/**
* @brief Delete the default event loop
*
* @return
* - ESP_OK: Success
* - Others: Fail
*/
esp_err_t esp_event_loop_delete_default();
/**
* @brief Dispatch events posted to an event loop.
*
* This function is used to dispatch events posted to a loop with no dedicated task, i.e task name was set to NULL
* in event_loop_args argument during loop creation. This function includes an argument to limit the amount of time
* it runs, returning control to the caller when that time expires (or some time afterwards). There is no guarantee
* that a call to this function will exit at exactly the time of expiry. There is also no guarantee that events have
* been dispatched during the call, as the function might have spent all of the alloted time waiting on the event queue.
* Once an event has been unqueued, however, it is guaranteed to be dispatched. This guarantee contributes to not being
* able to exit exactly at time of expiry as (1) blocking on internal mutexes is necessary for dispatching the unqueued
* event, and (2) during dispatch of the unqueued event there is no way to control the time occupied by handler code
* execution. The guaranteed time of exit is therefore the alloted time + amount of time required to dispatch
* the last unqueued event.
*
* In cases where waiting on the queue times out, ESP_OK is returned and not ESP_ERR_TIMEOUT, since it is
* normal behavior.
*
* @param[in] event_loop event loop to dispatch posted events from
* @param[in] ticks_to_run number of ticks to run the loop
*
* @note encountering an unknown event that has been posted to the loop will only generate a warning, not an error.
*
* @return
* - ESP_OK: Success
* - Others: Fail
*/
esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t ticks_to_run);
/**
* @brief Register an event handler to the system event loop.
*
* This function can be used to register a handler for either: (1) specific events,
* (2) all events of a certain event base, or (3) all events known by the system event loop.
*
* - specific events: specify exact event_base and event_id
* - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id
* - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id
*
* Registering multiple handlers to events is possible. Registering a single handler to multiple events is
* also possible. However, registering the same handler to the same event multiple times would cause the
* previous registrations to be overwritten.
*
* @param[in] event_base the base id of the event to register the handler for
* @param[in] event_id the id of the event to register the handler for
* @param[in] event_handler the handler function which gets called when the event is dispatched
* @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called
*
* @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should
* ensure that event_handler_arg still points to a valid location by the time the handler gets called
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for the handler
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_handler_register(esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler,
void* event_handler_arg);
/**
* @brief Register an event handler to a specific loop.
*
* This function behaves in the same manner as esp_event_handler_register, except the additional
* specification of the event loop to register the handler to.
*
* @param[in] event_loop the event loop to register this handler function to
* @param[in] event_base the base id of the event to register the handler for
* @param[in] event_id the id of the event to register the handler for
* @param[in] event_handler the handler function which gets called when the event is dispatched
* @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for the handler
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler,
void* event_handler_arg);
/**
* @brief Unregister a handler with the system event loop.
*
* This function can be used to unregister a handler so that it no longer gets called during dispatch.
* Handlers can be unregistered for either: (1) specific events, (2) all events of a certain event base,
* or (3) all events known by the system event loop
*
* - specific events: specify exact event_base and event_id
* - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id
* - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id
*
* This function ignores unregistration of handlers that has not been previously registered.
*
* @param[in] event_base the base of the event with which to unregister the handler
* @param[in] event_id the id of the event with which to unregister the handler
* @param[in] event_handler the handler to unregister
*
* @return ESP_OK success
* @return ESP_ERR_INVALIG_ARG invalid combination of event base and event id
* @return others fail
*/
esp_err_t esp_event_handler_unregister(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler);
/**
* @brief Unregister a handler with the system event loop.
*
* This function behaves in the same manner as esp_event_handler_unregister, except the additional specification of
* the event loop to unregister the handler with.
*
* @param[in] event_loop the event loop with which to unregister this handler function
* @param[in] event_base the base of the event with which to unregister the handler
* @param[in] event_id the id of the event with which to unregister the handler
* @param[in] event_handler the handler to unregister
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler);
/**
* @brief Posts an event to the system default event loop. The event loop library keeps a copy of event_data and manages
* the copy's lifetime automatically (allocation + deletion); this ensures that the data the
* handler recieves is always valid.
*
* @param[in] event_base the event base that identifies the event
* @param[in] event_id the the event id that identifies the event
* @param[in] event_data the data, specific to the event occurence, that gets passed to the handler
* @param[in] event_data_size the size of the event data
* @param[in] ticks_to_wait number of ticks to block on a full event queue
*
* @note posting events from an ISR is not supported
*
* @return
* - ESP_OK: Success
* - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_post(esp_event_base_t event_base,
int32_t event_id,
void* event_data,
size_t event_data_size,
TickType_t ticks_to_wait);
/**
* @brief Posts an event to the specified event loop. The event loop library keeps a copy of event_data and manages
* the copy's lifetime automatically (allocation + deletion); this ensures that the data the
* handler recieves is always valid.
*
* This function behaves in the same manner as esp_event_post_to, except the additional specification of the event loop
* to post the event to.
*
* @param[in] event_loop the event loop to post to
* @param[in] event_base the event base that identifies the event
* @param[in] event_id the the event id that identifies the event
* @param[in] event_data the data, specific to the event occurence, that gets passed to the handler
* @param[in] event_data_size the size of the event data
* @param[in] ticks_to_wait number of ticks to block on a full event queue
*
* @note posting events from an ISR is not supported
*
* @return
* - ESP_OK: Success
* - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
void* event_data,
size_t event_data_size,
TickType_t ticks_to_wait);
/**
* @brief Dumps statistics of all event loops.
*
* Dumps event loop info in the format:
*
@verbatim
event loop
event
handler
handler
event
handler
handler
event loop
event
handler
...
...
...
where:
event loop
format: address,name rx:total_recieved dr:total_dropped inv:total_number_of_invocations run:total_runtime
where:
address - memory address of the event loop
name - name of the event loop
total_recieved - number of successfully posted events
total_number_of_invocations - total number of handler invocations performed so far
total_runtime - total runtime of all invocations so far
event
format: base:id proc:total_processed run:total_runtime
where:
base - event base
id - event id
total_processed - number of instances of this event that has been processed
total_runtime - total amount of time in microseconds used for invoking handlers of this event
handler
format: address inv:total_invoked run:total_runtime
where:
address - address of the handler function
total_invoked - number of times this handler has been invoked
total_runtime - total amount of time used for invoking this handler
@endverbatim
*
* @param[in] file the file stream to output to
*
* @note this function is a noop when CONFIG_EVENT_LOOP_PROFILING is disabled
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list
* - Others: Fail
*/
esp_err_t esp_event_dump(FILE* file);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef ESP_EVENT_H_

View File

@ -0,0 +1,43 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_EVENT_BASE_H_
#define ESP_EVENT_BASE_H_
#ifdef __cplusplus
extern "C" {
#endif
// Defines for declaring and defining event base
#define ESP_EVENT_DECLARE_BASE(id) extern esp_event_base_t id;
#define ESP_EVENT_DEFINE_BASE(id) esp_event_base_t id = #id;
// Event loop library types
typedef const char* esp_event_base_t; /**< unique pointer to a subsystem that exposes events */
typedef void* esp_event_loop_handle_t; /**< a number that identifies an event with respect to a base */
typedef void (*esp_event_handler_t)(void* event_handler_arg,
esp_event_base_t event_base,
int32_t event_id,
void* event_data); /**< function called when an event is posted to the queue */
// Defines for registering/unregistering event handlers
#define ESP_EVENT_ANY_BASE NULL /**< register handler for any event base */
#define ESP_EVENT_ANY_ID -1 /**< register handler for any event id */
#ifdef __cplusplus
}
#endif
#endif // #ifndef ESP_EVENT_BASE_H_

View File

@ -0,0 +1,100 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_EVENT_INTERNAL_H_
#define ESP_EVENT_INTERNAL_H_
#include "esp_event.h"
#ifdef __cplusplus
extern "C" {
#endif
/// Event handler
typedef struct esp_event_handler_instance {
esp_event_handler_t handler; /**< event handler function*/
void* arg; /**< event handler argument */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t total_times_invoked; /**< number of times this handler has been invoked */
int64_t total_runtime; /**< total runtime of this handler across all calls */
#endif
SLIST_ENTRY(esp_event_handler_instance) handler_entry; /**< next event handler in the list */
} esp_event_handler_instance_t;
typedef SLIST_HEAD(esp_event_handler_instances, esp_event_handler_instance) esp_event_handler_instances_t;
typedef struct esp_event_id_instance {
int32_t id;
esp_event_handler_instances_t handlers; /**< list of handlers to be executed when
this event is raised */
SLIST_ENTRY(esp_event_id_instance) event_id_entry; /**< pointer to the next event node on the linked list */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t handlers_invoked; /**< total number of times the event has been
raised and processed in the loop */
int64_t handlers_runtime; /**< total time spent in executing handlers */
#endif
} esp_event_id_instance_t;
typedef SLIST_HEAD(esp_event_id_instances, esp_event_id_instance) esp_event_id_instances_t;
/// Event
typedef struct esp_event_base_instance {
esp_event_base_t base; /**< base identifier of the event */
esp_event_handler_instances_t base_handlers; /**< event base level handlers, handlers for
all events with this base */
esp_event_id_instances_t event_ids; /**< list of event ids with this base */
SLIST_ENTRY(esp_event_base_instance) event_base_entry; /**< pointer to the next event node on the linked list */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t base_handlers_invoked; /**< total number of base-level handlers invoked */
int64_t base_handlers_runtime; /**< amount of time processing base-level handlers */
#endif
} esp_event_base_instance_t;
typedef SLIST_HEAD(esp_event_base_instances, esp_event_base_instance) esp_event_base_instances_t;
/// Event loop
typedef struct esp_event_loop_instance {
const char* name; /**< name of this event loop */
QueueHandle_t queue; /**< event queue */
TaskHandle_t task; /**< task that consumes the event queue */
TaskHandle_t running_task; /**< for loops with no dedicated task, the
task that consumes the queue */
SemaphoreHandle_t mutex; /**< mutex for updating the events linked list */
esp_event_handler_instances_t loop_handlers; /**< loop level handlers, handlers for all events
registered in the loop */
esp_event_base_instances_t event_bases; /**< events linked list head pointer */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t events_recieved; /**< number of events successfully posted to the loop */
uint32_t events_dropped; /**< number of events dropped due to queue being full */
uint32_t loop_handlers_invoked; /**< total number of loop-level handlers invoked */
int64_t loop_handlers_runtime; /**< amount of time processing loop-level handlers */
uint32_t total_handlers_invoked; /**< total number of handlers invoked */
int64_t total_handlers_runtime; /**< total amount of time dedicated to processing this loop */
SLIST_ENTRY(esp_event_loop_instance) loop_entry; /**< next event loop in the list */
SemaphoreHandle_t profiling_mutex;
#endif
} esp_event_loop_instance_t;
/// Event posted to the event queue
typedef struct esp_event_post_instance {
esp_event_base_t base; /**< the event base */
int32_t id; /**< the event id */
void** data; /**< data associated with the event */
} esp_event_post_instance_t;
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef ESP_EVENT_INTERNAL_H_

View File

@ -0,0 +1,54 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_EVENT_PRIVATE_H_
#define ESP_EVENT_PRIVATE_H_
#include "esp_event.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Searches handlers registered with an event loop to see if it has been registered.
*
* @param[in] event_loop the loop to search
* @param[in] event_base the event base to search
* @param[in] event_id the event id to search
* @param[in] event_handler the event handler to look for
*
* @return true handler registered
* @return false handler not registered
*
* @return
* - true: Handler registered
* - false: Handler not registered
*/
bool esp_event_is_handler_registered(esp_event_loop_handle_t event_loop, esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler);
/**
* @brief Deinitializes the event loop library
*
* @return
* - ESP_OK: Success
* - Others: Fail
*/
esp_err_t esp_event_loop_deinit();
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef ESP_EVENT_PRIVATE_H_

View File

@ -0,0 +1,5 @@
set(COMPONENT_SRCDIRS ".")
set(COMPONENT_PRIV_INCLUDEDIRS "../private_include" ".")
set(COMPONENT_PRIV_REQUIRES unity esp_event)
register_component()

View File

@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_PRIV_INCLUDEDIRS := ../private_include .
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,6 @@ set(COMPONENT_SRCS "emac_dev.c"
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_REQUIRES)
set(COMPONENT_PRIV_REQUIRES tcpip_adapter)
set(COMPONENT_PRIV_REQUIRES tcpip_adapter esp_event)
register_component()

View File

@ -27,3 +27,5 @@
#define IDF_PERFORMANCE_MIN_TCP_TX_THROUGHPUT 40
#define IDF_PERFORMANCE_MIN_UDP_RX_THROUGHPUT 80
#define IDF_PERFORMANCE_MIN_UDP_TX_THROUGHPUT 50
// events dispatched per second by event loop library
#define IDF_PERFORMANCE_MIN_EVENT_DISPATCH 25000

View File

@ -184,6 +184,9 @@ INPUT = \
../../components/esp32/include/esp32/pm.h \
### esp_timer, High Resolution Timer
../../components/esp32/include/esp_timer.h \
### esp_event, Event Loop Library
../../components/esp_event/include/esp_event.h \
../../components/esp_event/include/esp_event_base.h \
### ESP Pthread parameters
../../components/pthread/include/esp_pthread.h \
###

View File

@ -0,0 +1,206 @@
Event Loop Library
==================
Overview
--------
The event loop library allows components to declare events to which other components can register handlers -- code which will
execute when those events occur. This allows loosely coupled components to attach desired behavior to changes in state of other components
without application involvement. For instance, a high level connection handling library may subscribe to events produced
by the wifi subsystem directly and act on those events. This also simplifies event processing by serializing and deferring
code execution to another context.
Using ``esp_event`` APIs
------------------------
There are two objects of concern for users of this library: events and event loops.
Events are occurences of note. For example, for WiFi, a successful connection to the access point may be an event.
Events are referenced using a two part identifier which are discussed more :ref:`here <esp-event-declaring-defining-events>`.
Event loops are the vehicle by which events get posted by event sources and handled by event handler functions.
These two appear prominently in the event loop library APIs.
Using this library roughly entails the following flow:
1. A user defines a function that should run when an event is posted to a loop. This function is referred to as the event handler. It should have the same signature as :cpp:type:`esp_event_handler_t`.
2. An event loop is created using :cpp:func:`esp_event_loop_create`, which outputs a handle to the loop of type :cpp:type:`esp_event_loop_handle_t`. Event loops created using this API are referred to as user event loops. There is, however, a special type of event loop called the default event loop which are discussed :ref:`here <esp-event-default-loops>`.
3. Components register event handlers to the loop using :cpp:func:`esp_event_handler_register_with`. Handlers can be registered with multiple loops, more on that :ref:`here <esp-event-handler-registration>`.
4. Event sources post an event to the loop using :cpp:func:`esp_event_post_to`.
5. Components wanting to remove their handlers from being called can do so by unregistering from the loop using :cpp:func:`esp_event_handler_unregister_with`.
6. Event loops which are no longer needed can be deleted using :cpp:func:`esp_event_loop_delete`.
In code, the flow above may look like as follows:
.. code-block:: c
// 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);
}
.. _esp-event-declaring-defining-events:
Declaring and defining events
-----------------------------
As mentioned previously, events consists of two-part identifers: the event base and the event ID. The event base identifies an independent group
of events; the event ID identifies the event within that group. Think of the event base and event ID as a
person's last name and first name, respectively. A last name identifies a family, and the first name identifies a person within that family.
The event loop library provides macros to declare and define the event base easily.
Event base declaration:
.. code-block:: c
ESP_EVENT_DECLARE_BASE(EVENT_BASE)
Event base definition:
.. code-block:: c
ESP_EVENT_DEFINE_BASE(EVENT_BASE)
.. note::
In IDF, the base identifiers for system events are uppercase and are postfixed with ``_EVENT``. For example, the base for wifi events is declared and defined
as ``WIFI_EVENT``, the ethernet event base ``ETHERNET_EVENT``, and so on. The purpose is to have event bases look like constants (although
they are global variables considering the defintions of macros ``ESP_EVENT_DECLARE_BASE`` and ``ESP_EVENT_DEFINE_BASE``).
For event ID's, declaring them as enumerations is recommended. Once again, for visibility, these are typically placed in public header files.
Event ID:
.. code-block:: c
enum {
EVENT_ID_1,
EVENT_ID_2,
EVENT_ID_3,
...
}
.. _esp-event-default-loops:
Default Event Loop
------------------
The default event loop is a special type of loop used for system events (WiFi events, for example). The handle for this
loop is hidden from the user. The creation, deletion, handler registration/unregistration and posting of events is done
through a variant of the APIs for user event loops. The table below enumerates those variants, and the user event
loops equivalent.
+---------------------------------------------------+---------------------------------------------------+
| User Event Loops | Default Event Loops |
+===================================================+===================================================+
| :cpp:func:`esp_event_loop_create` | :cpp:func:`esp_event_loop_create_default` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_loop_delete` | :cpp:func:`esp_event_loop_delete_default` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_handler_register_with` | :cpp:func:`esp_event_handler_register` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_handler_unregister_with` | :cpp:func:`esp_event_handler_unregister` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_post_to` | :cpp:func:`esp_event_post` |
+---------------------------------------------------+---------------------------------------------------+
If you compare the signatures for both, they are mostly similar except the for the lack of loop handle
specification for the default event loop APIs.
Other than the API difference and the special designation to which system events are posted to, there is no difference
to how default event loops and user event loops behave. It is even possible for users to post their own events
to the default event loop, should the user opt to not create their own loops to save memory.
.. _esp-event-handler-registration:
Notes on Handler Registration
-----------------------------
It is possible to register a single handler to multiple events individually, i.e. using multiple calls to :cpp:func:`esp_event_handler_register_with`.
For those multiple calls, the specific event base and event ID can be specified with which the handler should execute.
However, in some cases it is desirable for a handler to execute on (1) all events that get posted to a loop or (2) all events
of a particular base identifier. This is possible using the special event base identifier ``ESP_EVENT_ANY_BASE`` and
special event ID ``ESP_EVENT_ANY_ID``. These special identifiers may be passed as the event base and event ID arguments
for :cpp:func:`esp_event_handler_register_with`.
Therefore, the valid arguments to :cpp:func:`esp_event_handler_register_with` are:
1. <event base>, <event ID> - handler executes when the event with base <event base> and event ID <event ID> gets posted to the loop
2. <event base>, ESP_EVENT_ANY_ID - handler executes when any event with base <event base> gets posted to the loop
3. ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID - handler executes when any event gets posted to the loop
As an example, suppose the following handler registrations were performed:
.. code-block:: c
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, ...);
If the hypothetical event ``MY_EVENT_BASE``, ``MY_EVENT_ID`` is posted, all three handlers ``run_on_event_1``, ``run_on_event_2``,
and ``run_on_event_3`` would execute.
If the hypothetical event ``MY_EVENT_BASE``, ``MY_OTHER_EVENT_ID`` is posted, only ``run_on_event_2`` and ``run_on_event_3`` would execute.
If the hypothetical event ``MY_OTHER_EVENT_BASE``, ``MY_OTHER_EVENT_ID`` is posted, only ``run_on_event_3`` would execute.
Event loop profiling
--------------------
A configuration option :envvar:`CONFIG_EVENT_LOOP_PROFILING` can be enabled in order to activate statistics collection for all event loops created.
The function :cpp:func:`esp_event_dump` can be used to output the collected statistics to a file stream. More details on the information included in the dump
can be found in the :cpp:func:`esp_event_dump` API Reference.
Application Example
-------------------
Examples on using the ``esp_event`` library can be found on :example:`system/esp_event`. The examples cover event declaration, loop creation, handler registration
and unregistration and event posting.
API Reference
-------------
.. include:: /_build/inc/esp_event.inc
.. include:: /_build/inc/esp_event_base.inc

View File

@ -14,6 +14,7 @@ System API
Inter-Processor Call <ipc>
High Resolution Timer <esp_timer>
Logging <log>
Event Loop Library <esp_event>
Application Level Tracing <app_trace>
Power Management <power_management>
Sleep Modes <sleep_modes>

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/system/esp_event.rst

View File

@ -1,5 +1,5 @@
# System Examples
Configuration and management of memory, interrupts, WDT (watchdog timer), OTA (over the air updates), deep sleep and logging.
Configuration and management of memory, interrupts, WDT (watchdog timer), OTA (over the air updates), deep sleep logging, and event loops.
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(default_event_loop)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := default_event_loop
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,166 @@
# Event Loop Library ('esp_event') Default Event Loop Example
This example illustrates the basics of the event loop library. To keep the example simple, demonstration is limited to the use of the default event loop.
The default event loop is the event loop the system uses for its events. For some use cases, this could be sufficient.
However, should the user need to create their own event loops, the example `user_event_loops` should be able to provide guidance.
Here are the things this example illustrates:
### Declaring and Defining Events
This example shows the typical setup of having the event base and event IDs declared in a header
file and then having the definitions in a source file.
Declaration of the event base makes use of the macro `ESP_EVENT_DECLARE_BASE`, while the event IDs are declared as an `enum`; see
``event_source.h``. The source file ``main.c`` holds the definition of the event base using the macro `ESP_EVENT_DEFINE_BASE`.
### Creating the Default Event Loop
This example illustrates the creation of the default event loop using the API function `esp_event_loop_create_default`.
### Posting Events to the Default Event Loop
Simply put, posting an event to a loop is the act of queueing its handlers for execution. For the default loop, this is done using the API `esp_event_post`. The ability to pass event-specific data to the handler is also illustrated.
### Handler Registration/Unregistration
This example illustrates handler registration to the default event loop using `esp_event_handler_register` for (1) specific events, (2) *any* event under a certain base, and (3) *any* event. This also shows the possbility of registering multiple handlers to the same event.
Unregistering a handler is done using `esp_event_handler_register`. Unregistering a handler means that it no longer executes even when the event it was previously registered to gets posted to the loop.
## Example Flow Explained
The example flow is best explained using a sample log output.
```
I (297) default_event_loop: setting up
I (297) default_event_loop: starting event sources
I (297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: posting to default loop
I (297) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 1 out of 5
I (317) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: all_event_handler
I (327) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_any_handler
I (337) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_started_handler
I (347) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (347) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 1 times
I (827) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 2 out of 5
I (827) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (827) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 2 times
I (1297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (1297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (1307) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (1317) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 1 out of 3 times
I (1327) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 3 out of 5
I (1337) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: unregistering task_iteration_handler
I (1337) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (1357) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 3 times
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (2297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (2297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (2307) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (2317) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 2 out of 3 times
I (2367) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 5 out of 5
I (2367) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (3297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (3297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (3307) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (3317) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: posting to default loop
I (3327) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 3 out of 3 times
I (3337) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: all_event_handler
I (3337) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_any_handler
I (3347) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_stopped_handler
I (3357) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer event source
I (3367) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source
```
### Setting
This example uses two event sources: a periodic timer and a task with a loop inside. Events are raised for the periodic timer when (1) the timer is started (2) the timer period expires and (3) the timer is stopped. Events are raised for the when (1) the loop iterates.
All of the events mentioned above has their own specific handler. There are additional handlers, however. One handler executes when *any* event under the periodic timer event is posted; while the other executes if *any* event is posted.
The number of periodic timer expiries and loop iterations are limited. When the limit for the timer expiry is reached,
the timer is stopped. When the limit for the iterarations is reached, the task is deleted. In the case of the loop iteration, there is another limit: the number of iterations for when its handler will be unregistered.
### Step-by-Step Explanation
The following text explains the important points of the sample log output.
a.
```
I (297) default_event_loop: setting up
```
At this stage the default event loop is created, as well as the handlers for the different events registered.
b.
```
I (297) default_event_loop: starting event sources
I (297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: posting to default loop
I (297) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 1 out of 5
```
The two event sources are started. The respective events are posted to the default event loop.
c.
```
I (317) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: all_event_handler
I (327) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_any_handler
I (337) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_started_handler
```
```
I (347) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (347) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 1 times
```
The handlers are executed for the events posted in **(b)**. In addition to event-specific handlers `timer_started_handler`
and `task_iteration_handler`, the handlers `timer_any_handler` and `all_event_handler` also executed.
The handler `timer_any_handler` executes for *any* timer event. It can be seen executing for the timer expiry and timer stopped events in the upcoming parts of the log.
On the other hand, `all_event_handler` executes for *any* event. This is the reason why it executes for both ``TIMER_EVENTS:TIMER_EVENT_STARTED`` and ``TASK_EVENTS:TASK_ITERATION_EVENT``.
The proceeding lines of the log follows the same pattern: the event is posted to the loop and the handlers are executed.
d.
```
...
I (1337) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: unregistering task_iteration_handler
```
At this point in the execution the handler `task_iteration_handler` is unregistered, therefore it no longer executes
when the event ``TASK_EVENTS:TASK_ITERATION_EVENT`` is posted.
```
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
...
I (2367) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 5 out of 5
I (2367) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
```
The iteration event continues to get posted, but only `all_event_handler` gets executed.
e.
```
...
I (3297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (3297) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (3307) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (3317) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: posting to default loop
I (3327) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 3 out of 3 times
...
I (3347) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_stopped_handler
I (3357) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer event source
```
When the periodic timer expiry limit is reached, the event ``TIMER_EVENTS:TIMER_EVENT_STOPPED`` is posted to the loop. The periodic timer is consequently deleted in the handler `timer_stopped_handler`.
```
...
I (3367) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source
...
```
The task containing the loop that posts iteration events also gets deleted. The example ends at this point.
---
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@ -0,0 +1,100 @@
from __future__ import print_function
import re
import os
import sys
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv('TEST_FW_PATH')
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
# Timer events
TIMER_EVENT_LIMIT = 3
TIMER_EXPIRY_HANDLING = "TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed {} out of " + str(TIMER_EVENT_LIMIT) + " times"
# Task events
TASK_ITERATION_LIMIT = 5
TASK_UNREGISTRATION_LIMIT = 3
TASK_ITERATION_POST = "TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, {} out of " + str(TASK_ITERATION_LIMIT)
TASK_ITERATION_HANDLING = "TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed {} times"
def _test_timer_events(dut):
dut.start_app()
print("Checking timer events posting and handling")
dut.expect("setting up")
dut.expect("starting event sources")
print("Finished setup")
dut.expect("TIMER_EVENTS:TIMER_EVENT_STARTED: posting to default loop")
print("Posted timer started event")
dut.expect("TIMER_EVENTS:TIMER_EVENT_STARTED: all_event_handler")
dut.expect("TIMER_EVENTS:TIMER_EVENT_STARTED: timer_any_handler")
dut.expect("TIMER_EVENTS:TIMER_EVENT_STARTED: timer_started_handler")
print("Handled timer started event")
for expiries in range(1, TIMER_EVENT_LIMIT + 1):
dut.expect("TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop")
print("Posted timer expiry event {} out of {}".format(expiries, TIMER_EVENT_LIMIT))
if expiries < TIMER_EVENT_LIMIT:
dut.expect_all("TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler",
"TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler",
TIMER_EXPIRY_HANDLING.format(expiries))
else:
dut.expect_all("TIMER_EVENTS:TIMER_EVENT_STOPPED: posting to default loop",
"TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler",
"TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler",
TIMER_EXPIRY_HANDLING.format(expiries))
print("Posted timer stopped event")
print("Handled timer expiry event {} out of {}".format(expiries, TIMER_EVENT_LIMIT))
dut.expect("TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_stopped_handler")
dut.expect("TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer event source")
print("Handled timer stopped event")
def _test_iteration_events(dut):
dut.start_app()
print("Checking iteration events posting and handling")
dut.expect("setting up")
dut.expect("starting event sources")
print("Finished setup")
for iteration in range(1, TASK_ITERATION_LIMIT + 1):
dut.expect(TASK_ITERATION_POST.format(iteration))
print("Posted iteration {} out of {}".format(iteration, TASK_ITERATION_LIMIT))
if iteration < TASK_UNREGISTRATION_LIMIT:
dut.expect_all("TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler", TASK_ITERATION_HANDLING.format(iteration))
elif iteration == TASK_UNREGISTRATION_LIMIT:
dut.expect_all("TASK_EVENTS:TASK_ITERATION_EVENT: unregistering task_iteration_handler", "TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler")
print("Unregistered handler at iteration {} out of {}".format(iteration, TASK_ITERATION_LIMIT))
else:
dut.expect_all("TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler")
print("Handled iteration {} out of {}".format(iteration, TASK_ITERATION_LIMIT))
dut.expect("TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source")
print("Deleted task event source")
@IDF.idf_example_test(env_tag='Example_WIFI')
def test_default_event_loop_example(env, extra_data):
dut = env.get_dut('default_event_loop', 'examples/system/event/default_event_loop')
_test_iteration_events(dut)
_test_timer_events(dut)
if __name__ == '__main__':
test_default_event_loop_example()

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@ -0,0 +1,53 @@
/* esp_event (event loop library) basic example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef EVENT_SOURCE_H_
#define EVENT_SOURCE_H_
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_timer.h"
#ifdef __cplusplus
extern "C" {
#endif
// This example makes use of two event sources: a periodic timer, and a task.
// Declarations for event source 1: periodic timer
#define TIMER_EXPIRIES_COUNT 3 // number of times the periodic timer expires before being stopped
#define TIMER_PERIOD 1000000 // period of the timer event source in microseconds
extern esp_timer_handle_t g_timer; // the periodic timer object
// Declare an event base
ESP_EVENT_DECLARE_BASE(TIMER_EVENTS); // declaration of the timer events family
enum { // declaration of the specific events under the timer event family
TIMER_EVENT_STARTED, // raised when the timer is first started
TIMER_EVENT_EXPIRY, // raised when a period of the timer has elapsed
TIMER_EVENT_STOPPED // raised when the timer has been stopped
};
// Declarations for event source 2: task
#define TASK_ITERATIONS_COUNT 5 // number of times the task iterates
#define TASK_ITERATIONS_UNREGISTER 3 // count at which the task event handler is unregistered
#define TASK_PERIOD 500 // period of the task loop in milliseconds
ESP_EVENT_DECLARE_BASE(TASK_EVENTS); // declaration of the task events family
enum {
TASK_ITERATION_EVENT, // raised during an iteration of the loop within the task
};
#ifdef __cplusplus
}
#endif
#endif // #ifndef EVENT_SOURCE_H_

View File

@ -0,0 +1,179 @@
/* esp_event (event loop library) basic example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "event_source.h"
static const char* TAG = "default_event_loop";
static char* get_id_string(esp_event_base_t base, int32_t id) {
char* event = "";
if (base == TIMER_EVENTS) {
switch(id) {
case TIMER_EVENT_STARTED:
event = "TIMER_EVENT_STARTED";
break;
case TIMER_EVENT_EXPIRY:
event = "TIMER_EVENT_EXPIRY";
break;
case TIMER_EVENT_STOPPED:
event = "TIMER_EVENT_STOPPED";
break;
}
} else {
event = "TASK_ITERATION_EVENT";
}
return event;
}
/* Event source periodic timer related definitions */
ESP_EVENT_DEFINE_BASE(TIMER_EVENTS);
esp_timer_handle_t TIMER;
// Callback that will be executed when the timer period lapses. Posts the timer expiry event
// to the default event loop.
static void timer_callback(void* arg)
{
ESP_LOGI(TAG, "%s:%s: posting to default loop", TIMER_EVENTS, get_id_string(TIMER_EVENTS, TIMER_EVENT_EXPIRY));
ESP_ERROR_CHECK(esp_event_post(TIMER_EVENTS, TIMER_EVENT_EXPIRY, NULL, 0, portMAX_DELAY));
}
// Handler which executes when the timer started event gets executed by the loop.
static void timer_started_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
ESP_LOGI(TAG, "%s:%s: timer_started_handler", base, get_id_string(base, id));
}
// Handler which executes when the timer expiry event gets executed by the loop. This handler keeps track of
// how many times the timer expired. When a set number of expiry is reached, the handler stops the timer
// and sends a timer stopped event.
static void timer_expiry_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
static int count = 0;
count++;
if (count >= TIMER_EXPIRIES_COUNT) {
// Stop the timer
ESP_ERROR_CHECK(esp_timer_stop(TIMER));
ESP_LOGI(TAG, "%s:%s: posting to default loop", base, get_id_string(base, TIMER_EVENT_STOPPED));
// Post the event that the timer has been stopped
ESP_ERROR_CHECK(esp_event_post(TIMER_EVENTS, TIMER_EVENT_STOPPED, NULL, 0, portMAX_DELAY));
}
ESP_LOGI(TAG, "%s:%s: timer_expiry_handler, executed %d out of %d times", base, get_id_string(base, id), count, TIMER_EXPIRIES_COUNT);
}
// Handler which executes when any timer event (started, expiry, stopped) get executed by the loop
static void timer_any_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
ESP_LOGI(TAG, "%s:%s: timer_any_handler", base, get_id_string(base, id));
}
// Handler which executes when the timer stopped event gets executed by the loop. Since the timer has been
// stopped, it is safe to delete it.
static void timer_stopped_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
ESP_LOGI(TAG, "%s:%s: timer_stopped_handler", base, get_id_string(base, id));
// Delete the timer
esp_timer_delete(TIMER);
ESP_LOGI(TAG, "%s:%s: deleted timer event source", base, get_id_string(base, id));
}
/* Event source task related definitions */
ESP_EVENT_DEFINE_BASE(TASK_EVENTS)
static void task_iteration_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
int iteration = *((int*) event_data);
ESP_LOGI(TAG, "%s:%s: task_iteration_handler, executed %d times", base, get_id_string(base, id), iteration);
}
static void task_event_source(void* args)
{
for(int iteration = 1; iteration <= TASK_ITERATIONS_COUNT; iteration++) {
ESP_LOGI(TAG, "%s:%s: posting to default loop, %d out of %d", TASK_EVENTS,
get_id_string(TASK_EVENTS, TASK_ITERATION_EVENT), iteration, TASK_ITERATIONS_COUNT);
// Post that the loop has iterated. Notice that the iteration count is passed to the handler. Take note
// that data passed during event posting is a deep copy of the original data.
ESP_ERROR_CHECK(esp_event_post(TASK_EVENTS, TASK_ITERATION_EVENT, &iteration, sizeof(iteration), portMAX_DELAY));
if (iteration == TASK_ITERATIONS_UNREGISTER){
ESP_LOGI(TAG, "%s:%s: unregistering task_iteration_handler", TASK_EVENTS, get_id_string(TASK_EVENTS, TASK_ITERATION_EVENT));
ESP_ERROR_CHECK(esp_event_handler_unregister(TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler));
}
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
}
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
ESP_LOGI(TAG, "%s:%s: deleting task event source", TASK_EVENTS, get_id_string(TASK_EVENTS, TASK_ITERATION_EVENT));
vTaskDelete(NULL);
}
/* Handler for all events */
static void all_event_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
ESP_LOGI(TAG, "%s:%s: all_event_handler", base, get_id_string(base, id));
}
/* Example main */
void app_main(void)
{
ESP_LOGI(TAG, "setting up");
// Create the default event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Register the specific timer event handlers.
ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, TIMER_EVENT_STARTED, timer_started_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, TIMER_EVENT_EXPIRY, timer_expiry_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, TIMER_EVENT_STOPPED, timer_stopped_handler, NULL));
// Register the handler for all timer family events. This will execute if the timer is started, expired or is stopped.
ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, ESP_EVENT_ANY_ID, timer_any_handler, NULL));
// Register the handler for task iteration event.
ESP_ERROR_CHECK(esp_event_handler_register(TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, NULL));
// Register the handler for all event. This will execute if either the timer events or the task iteration event
// is posted to the default loop.
ESP_ERROR_CHECK(esp_event_handler_register(ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, all_event_handler, NULL));
// Create and start the event sources
esp_timer_create_args_t timer_args = {
.callback = &timer_callback,
};
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &TIMER));
ESP_LOGI(TAG, "starting event sources");
// Create the event source task with the same priority as the current task
xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
ESP_ERROR_CHECK(esp_timer_start_periodic(TIMER, TIMER_PERIOD));
// Post the timer started event
ESP_LOGI(TAG, "%s:%s: posting to default loop", TIMER_EVENTS, get_id_string(TIMER_EVENTS, TIMER_EVENT_STARTED));
ESP_ERROR_CHECK(esp_event_post(TIMER_EVENTS, TIMER_EVENT_STARTED, NULL, 0, portMAX_DELAY));
}

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(user_event_loops)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := user_event_loops
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,117 @@
# Event Loop Library ('esp_event') User Event Loops Example
This example demonstrates the creation and use of user event loops. This example is a supplement
to the `default_event_loop` example, if the default event loop is not sufficient for the user's use case.
Here are the things this example illustrates:
### Creating Event Loops
Creating a loop entails populating the structure `esp_event_loop_args_t` with the desired parameters and calling `esp_event_loop_create`. The call to `esp_event_loop_create` produces a handle to the loop, which is used to perform actions on that loop such as handler registration/unregistration and posting events.
### Running Event Loops
Depending on the parameters, the user can create either a loop with a dedicated task or one without. The purpose of the dedicated task is to unqueue events from the loop and execute its handlers. For loops without the dedicated task, the user should make a call to `esp_event_loop_run` in an application task.
### Handler Registration/Unregistration,
Handler registration and unregistration works the same way as the default event loop, just with a different API, `esp_event_handler_register_with` and `esp_event_handler_register_with` respectively. There are two things this example highlights: (1) the possibility of registering the same handler for different loops and (2) the ability to pass static data to handlers.
### Posting Events to the Default Event Loop
Posting events also works the same way as the default event loop, except with a different API, `esp_event_post_to`.
## Example Flow Explained
The example flow is best explained using the sample log output.
```
I (296) user_event_loops: setting up
I (296) user_event_loops: starting event source
I (296) user_event_loops: starting application task
I (296) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 1 out of 10
I (316) user_event_loops: application_task: running application task
I (326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 1
I (826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 2 out of 10
I (826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 2
I (1326) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 3 out of 10
I (1326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 3
I (1426) user_event_loops: application_task: running application task
I (1826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 4 out of 10
I (1826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 4
I (2326) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 5 out of 10
I (2326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 5
I (2526) user_event_loops: application_task: running application task
I (2826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 6 out of 10
I (2826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 6
I (3326) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 7 out of 10
I (3326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 7
I (3626) user_event_loops: application_task: running application task
I (3826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 8 out of 10
I (3826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 8
I (4326) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 9 out of 10
I (4326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 9
I (4726) user_event_loops: application_task: running application task
I (4826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 10 out of 10
I (4826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 10
I (5826) user_event_loops: application_task: running application task
I (5826) user_event_loops: deleting task event source
I (6926) user_event_loops: application_task: running application task
I (8026) user_event_loops: application_task: running application task
I (9126) user_event_loops: application_task: running application task
...
```
### Setting
This example has a single event source: a task with a loop inside. Events are raised for the task event source when the loop iterates.
Two loops are created, one with a dedicated task and one without. Events are posted to either loops,depending on whether the iteration is odd or even. For the loop with a dedicated task, event handlers are automatically executed. However, for the loop without the dedicated task, a call to run the loop is made in one of the application tasks. As a result, the execution of the event handlers for this loop is interspersed with the execution of application task code.
### Step-by-Step Explanation
The following text explains the important points of this example's sample log output.
a.
```
I (296) user_event_loops: setting up
I (296) user_event_loops: starting event source
I (296) user_event_loops: starting application task
```
At this stage the two event loops are created, as well as the handlers for the iteration event registered. The event source is started, which will post the event to the appropriate loop. The application task which makes the call to run the loop without dedicated task, is also created and started.
b.
```
I (296) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 1 out of 10
I (316) user_event_loops: application_task: running application task
I (326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 1
I (826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 2 out of 10
I (826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 2
I (1326) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 3 out of 10
I (1326) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_without_task, iteration 3
I (1426) user_event_loops: application_task: running application task
I (1826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 4 out of 10
...
```
In this section of the log we see the odd iterations posted to the loop without dedicated task, and the even iterations to the loop with a dedicated task. For the event with dedicated task, event handlers are executed automatically. The loop without a dedicated task, on the other hand, runs in the context of the application task.
c.
```
...
I (4826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with_task, iteration 10 out of 10
I (4826) user_event_loops: handling TASK_EVENTS:TASK_ITERATION_EVENT from loop_with_task, iteration 10
I (5826) user_event_loops: application_task: running application task
I (5826) user_event_loops: deleting task event source
I (6926) user_event_loops: application_task: running application task
I (8026) user_event_loops: application_task: running application task
I (9126) user_event_loops: application_task: running application task
...
```
The last of the iteration event is posted, and the event source is deleted. Because the loop without the task no longer receive events to execute, only the application task code executes.
---
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@ -0,0 +1,50 @@
from __future__ import print_function
import re
import os
import sys
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv('TEST_FW_PATH')
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
TASK_ITERATION_LIMIT = 10
TASK_ITERATION_POSTING = "posting TASK_EVENTS:TASK_ITERATION_EVENT to {}, iteration {} out of " + str(TASK_ITERATION_LIMIT)
TASK_ITERATION_HANDLING = "handling TASK_EVENTS:TASK_ITERATION_EVENT from {}, iteration {}"
@IDF.idf_example_test(env_tag='Example_WIFI')
def test_user_event_loops_example(env, extra_data):
dut = env.get_dut('user_event_loops', 'examples/system/event/user_event_loops')
dut.start_app()
dut.expect("setting up")
dut.expect("starting event source")
dut.expect("starting application task")
print("Finished setup")
for iteration in range(1, TASK_ITERATION_LIMIT + 1):
loop = None
if (iteration % 2 == 0):
loop = "loop_with_task"
else:
loop = "loop_without_task"
dut.expect(TASK_ITERATION_POSTING.format(loop, iteration))
print("Posted iteration {} to {}".format(iteration, loop))
dut.expect(TASK_ITERATION_HANDLING.format(loop, iteration))
print("Handled iteration {} from {}".format(iteration, loop))
dut.expect("deleting task event source")
print("Deleted task event source")
if __name__ == '__main__':
test_user_event_loops_example()

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@ -0,0 +1,35 @@
/* esp_event (event loop library) basic example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef EVENT_SOURCE_H_
#define EVENT_SOURCE_H_
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_timer.h"
#ifdef __cplusplus
extern "C" {
#endif
// Declarations for the event source
#define TASK_ITERATIONS_COUNT 10 // number of times the task iterates
#define TASK_PERIOD 500 // period of the task loop in milliseconds
ESP_EVENT_DECLARE_BASE(TASK_EVENTS); // declaration of the task events family
enum {
TASK_ITERATION_EVENT // raised during an iteration of the loop within the task
};
#ifdef __cplusplus
}
#endif
#endif // #ifndef EVENT_SOURCE_H_

View File

@ -0,0 +1,123 @@
/* esp_event (event loop library) basic example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "event_source.h"
#include "esp_event_base.h"
static const char* TAG = "user_event_loops";
// Event loops
esp_event_loop_handle_t loop_with_task;
esp_event_loop_handle_t loop_without_task;
static void application_task(void* args)
{
while(1) {
ESP_LOGI(TAG, "application_task: running application task");
esp_event_loop_run(loop_without_task, 100);
vTaskDelay(10);
}
}
/* Event source task related definitions */
ESP_EVENT_DEFINE_BASE(TASK_EVENTS)
TaskHandle_t g_task;
static void task_iteration_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
// Two types of data can be passed in to the event handler: the handler specific data and the event-specific data.
//
// The handler specific data (handler_args) is a pointer to the original data, therefore, the user should ensure that
// the memory location it points to is still valid when the handler executes.
//
// The event-specific data (event_data) is a pointer to a deep copy of the original data, and is managed automatically.
int iteration = *((int*) event_data);
char* loop;
if (handler_args == loop_with_task) {
loop = "loop_with_task";
} else {
loop = "loop_without_task";
}
ESP_LOGI(TAG, "handling %s:%s from %s, iteration %d", base, "TASK_ITERATION_EVENT", loop, iteration);
}
static void task_event_source(void* args)
{
for(int iteration = 1; iteration <= TASK_ITERATIONS_COUNT; iteration++) {
esp_event_loop_handle_t loop_to_post_to;
if (iteration % 2 == 0) {
// if even, post to the event loop with dedicated task
loop_to_post_to = loop_with_task;
} else {
// if odd, post to the event loop without a dedicated task
loop_to_post_to = loop_without_task;
}
ESP_LOGI(TAG, "posting %s:%s to %s, iteration %d out of %d", TASK_EVENTS, "TASK_ITERATION_EVENT",
loop_to_post_to == loop_with_task ? "loop_with_task" : "loop_without_task",
iteration, TASK_ITERATIONS_COUNT);
ESP_ERROR_CHECK(esp_event_post_to(loop_to_post_to, TASK_EVENTS, TASK_ITERATION_EVENT, &iteration, sizeof(iteration), portMAX_DELAY));
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
}
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
ESP_LOGI(TAG, "deleting task event source");
vTaskDelete(NULL);
}
/* Example main */
void app_main(void)
{
ESP_LOGI(TAG, "setting up");
esp_event_loop_args_t loop_with_task_args = {
.queue_size = 5,
.task_name = "loop_task", // task will be created
.task_priority = uxTaskPriorityGet(NULL),
.task_stack_size = 2048,
.task_core_id = tskNO_AFFINITY
};
esp_event_loop_args_t loop_without_task_args = {
.queue_size = 5,
.task_name = NULL // no task will be created
};
// Create the event loops
ESP_ERROR_CHECK(esp_event_loop_create(&loop_with_task_args, &loop_with_task));
ESP_ERROR_CHECK(esp_event_loop_create(&loop_without_task_args, &loop_without_task));
// Register the handler for task iteration event. Notice that the same handler is used for handling event on different loops.
// The loop handle is provided as an argument in order for this example to display the loop the handler is being run on.
ESP_ERROR_CHECK(esp_event_handler_register_with(loop_with_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_with_task));
ESP_ERROR_CHECK(esp_event_handler_register_with(loop_without_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_without_task));
ESP_LOGI(TAG, "starting event source");
// Create the event source task with the same priority as the current task
xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
ESP_LOGI(TAG, "starting application task");
// Create the application task with the same priority as the current task
xTaskCreate(application_task, "application_task", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
}