mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-31 13:09:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			229 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| 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 occurrences 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.
 | |
| 
 | |
| Handler Registration and Handler Dispatch Order
 | |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | |
| 
 | |
| The general rule is that for handlers that match a certain posted event during dispatch, those which are registered first also gets executed first. The user can then control which handlers get executed first by
 | |
| registering them before other handlers, provided that all registrations are performed using a single task.
 | |
| If the user plans to take advantage of this behavior, caution must be exercised if there are multiple tasks registering handlers. While the 'first registered, first executed'
 | |
| behavior still holds true, the task which gets executed first will also get their handlers registered first. Handlers registered one after the other by a single task
 | |
| will still be dispatched in the order relative to each other, but if that task gets pre-empted in between registration by another task which also registers handlers; then during dispatch those
 | |
| handlers will also get executed in between.
 | |
| 
 | |
| 
 | |
| Event loop profiling
 | |
| --------------------
 | |
| 
 | |
| A configuration option :ref:`CONFIG_ESP_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 in :example:`system/esp_event`. The examples cover event declaration, loop creation, handler registration and unregistration and event posting.
 | |
| 
 | |
| Other examples which also adopt esp_event library:
 | |
| 
 | |
|     * :example:`NMEA Parser <peripherals/uart/nmea0183_parser>`, which will decode the statements received from GPS.
 | |
| 
 | |
| API Reference
 | |
| -------------
 | |
| 
 | |
| .. include-build-file:: inc/esp_event.inc
 | |
| .. include-build-file:: inc/esp_event_base.inc
 | |
| 
 | |
| Related Documents
 | |
| -----------------
 | |
| 
 | |
| .. toctree::
 | |
|     :maxdepth: 1
 | |
| 
 | |
|     Legacy event loop API reference <esp_event_legacy>
 | 
