In this assignment, we will decouple the alarm and temperature reading code by using the default event loop.
Assignment steps#
- Create the events
TEMP_EVENT_BASE-temp_event_idALARM_EVENT_BASE-alarm_event_id_t
- Create the handler functions
alarm_event_handlertemp_event_handler
- Register the handler functions
- Create the two timers
esp_timer_createesp_timer_start_periodic
- Create the timer callback functions to post the event every 5s and 200ms
temp_timer_callbackalarm_timer_callback
- Create a infinite sleep loop in main
Create the events#
Define the two event bases outside any function (global)
ESP_EVENT_DEFINE_BASE(TEMP_EVENT_BASE); ESP_EVENT_DEFINE_BASE(ALARM_EVENT_BASE);Define their
event_idas an enum (in this case, both comprise just a single value):typedef enum { TEMP_EVENT_MEASURE, } temp_event_id_t; typedef enum { ALARM_EVENT_CHECK, } alarm_event_id_t;
Create handler functions#
We create the two event handler functions which do the actual job of posting the data on the MQTT channel.
- Create the two handler function:
static void temp_event_handler(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data) { float temp; if (temperature_sensor_read_celsius(sensor, &temp) == ESP_OK) { cloud_manager_send_temperature(cloud, temp); } else { ESP_LOGW("APP", "Failed to read temperature"); } } static void alarm_event_handler(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data) { if (is_alarm_set(alarm)) { ESP_LOGI("APP", "ALARM ON!!"); cloud_manager_send_alarm(cloud); } }
Register the handler functions#
In the
app_main, register the two handler function:ESP_ERROR_CHECK(esp_event_handler_register(TEMP_EVENT_BASE, TEMP_EVENT_MEASURE, temp_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(ALARM_EVENT_BASE, ALARM_EVENT_CHECK, alarm_event_handler, NULL));
Create the timers#
The actual source of event in this assignment are timers, so we need to start two of them.
- Create and start the temperature and alarm timers:
// Create and start periodic timers (app_main) // Temperature timer const esp_timer_create_args_t temp_timer_args = { .callback = &temp_timer_callback, .name = "temp_timer" }; esp_timer_handle_t temp_timer; ESP_ERROR_CHECK(esp_timer_create(&temp_timer_args, &temp_timer)); ESP_ERROR_CHECK(esp_timer_start_periodic(temp_timer, TEMPERATURE_MEAS_PERIOD_US)); // Alarm timer const esp_timer_create_args_t alarm_timer_args = { .callback = &alarm_timer_callback, .name = "alarm_timer" }; esp_timer_handle_t alarm_timer; ESP_ERROR_CHECK(esp_timer_create(&alarm_timer_args, &alarm_timer)); ESP_ERROR_CHECK(esp_timer_start_periodic(alarm_timer, ALARM_CHECK_PERIOD_US));
The two macros ALARM_CHECK_PERIOD_US and TEMPERATURE_MEAS_PERIOD_US could be added as defines at the beginning of the code or as a module’s parameter.
- For the sake of semplicity, define them at the beginning of
app_main.cas#define TEMPERATURE_MEAS_PERIOD_US (5 * 1000000) #define ALARM_CHECK_PERIOD_US (200 * 1000)
Create the timer callback functions#
In the previous code, we gave as .callback the functions temp_timer_callback and a alarm_timer_callback.
These functions are called when the timer expires.
- Create the timer call back function to post the corresponding event
static void temp_timer_callback(void* arg) { esp_event_post(TEMP_EVENT_BASE, TEMP_EVENT_MEASURE, NULL, 0, 0); } static void alarm_timer_callback(void* arg) { esp_event_post(ALARM_EVENT_BASE, ALARM_EVENT_CHECK, NULL, 0, 0); }
The event loop will take care of calling the right function when the event is triggered.
Main sleep#
The last thing to do, is to let the main continue to run while events are triggered.
Add an infinite idle loop
while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); }
Assignment solution code#
Show full assignment code
#include "cloud_manager.h"
#include "temperature_sensor.h"
#include "alarm.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_timer.h"
#define TEMPERATURE_MEAS_PERIOD_US (5 * 1000000)
#define ALARM_CHECK_PERIOD_US (200 * 1000)
ESP_EVENT_DEFINE_BASE(TEMP_EVENT_BASE);
ESP_EVENT_DEFINE_BASE(ALARM_EVENT_BASE);
static bool previous_alarm_set = false;
typedef enum {
TEMP_EVENT_MEASURE,
} temp_event_id_t;
typedef enum {
ALARM_EVENT_CHECK,
} alarm_event_id_t;
static temperature_sensor_t *sensor = NULL;
static alarm_t *alarm = NULL;
static cloud_manager_t *cloud = NULL;
static void temp_event_handler(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data) {
float temp;
if (temperature_sensor_read_celsius(sensor, &temp) == ESP_OK) {
cloud_manager_send_temperature(cloud, temp);
} else {
ESP_LOGW("APP", "Failed to read temperature");
}
}
static void alarm_event_handler(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data) {
bool alarm_state = is_alarm_set(alarm);
if (alarm_state && !previous_alarm_set) {
printf("ALARM ON!!\n");
cloud_manager_send_alarm(cloud);
}
previous_alarm_set = alarm_state;
}
static void temp_timer_callback(void* arg) {
esp_event_post(TEMP_EVENT_BASE, TEMP_EVENT_MEASURE, NULL, 0, 0);
}
static void alarm_timer_callback(void* arg) {
esp_event_post(ALARM_EVENT_BASE, ALARM_EVENT_CHECK, NULL, 0, 0);
}
void app_main(void)
{
ESP_LOGI("APP", "Starting...");
ESP_ERROR_CHECK(esp_event_loop_create_default());
sensor = temperature_sensor_create();
alarm = alarm_create();
cloud = cloud_manager_create();
printf("Connecting...\n");
ESP_ERROR_CHECK(cloud_manager_connect(cloud));
printf("Connected!\n");
// Register event handlers
ESP_ERROR_CHECK(esp_event_handler_register(TEMP_EVENT_BASE, TEMP_EVENT_MEASURE, temp_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(ALARM_EVENT_BASE, ALARM_EVENT_CHECK, alarm_event_handler, NULL));
// Create and start periodic timers
const esp_timer_create_args_t temp_timer_args = {
.callback = &temp_timer_callback,
.name = "temp_timer"
};
esp_timer_handle_t temp_timer;
ESP_ERROR_CHECK(esp_timer_create(&temp_timer_args, &temp_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(temp_timer, TEMPERATURE_MEAS_PERIOD_US));
const esp_timer_create_args_t alarm_timer_args = {
.callback = &alarm_timer_callback,
.name = "alarm_timer"
};
esp_timer_handle_t alarm_timer;
ESP_ERROR_CHECK(esp_timer_create(&alarm_timer_args, &alarm_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(alarm_timer, ALARM_CHECK_PERIOD_US));
// The main task can now just sleep
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
// Cleanup (unreachable in this example)
cloud_manager_disconnect(cloud);
cloud_manager_delete(cloud);
temperature_sensor_delete(sensor);
alarm_delete(alarm);
}
You can find the complete solution project in the assignment_2_1 folder in the GitHub repo.
Conclusion#
Using an event loop decouples the management of the alarm and temperature sensor. In this specific assignment, we could have reached the same result by using the timers callback function to do the same and avoid all the event loop overhead. But in general, events can come from a variety of sources and event loop offer an unified approach to decouple the application logic.
Next step#
If you still have time: Assignment 2.2.
In exercise 2.2, you’ll add another event source — this time triggered by a GPIO.
Otherwise: Lecture 3
