Skip to main content

This site is a preview for github.com/espressif/developer-portal/pull/703

ESP-IDF Adv. - Assign. 2.1

··4 mins·
Table of Contents
Event loop: Manage temperature and alarm sensor via events (guided)
WS00B - This article is part of a series.
Part 7: This Article

In this assignment, we will decouple the alarm and temperature reading code by using the default event loop.

In the previous assignment code, we were already indirectly using this event loop for capturing MQTT and Wi-Fi events.

Assignment steps
#

  1. Create the events
    • TEMP_EVENT_BASE - temp_event_id
    • ALARM_EVENT_BASE - alarm_event_id_t
  2. Create the handler functions
    • alarm_event_handler
    • temp_event_handler
  3. Register the handler functions
  4. Create the two timers
    • esp_timer_create
    • esp_timer_start_periodic
  5. Create the timer callback functions to post the event every 5s and 200ms
    • temp_timer_callback
    • alarm_timer_callback
  6. 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_id as 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.c as
    #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

Or go back to navigation menu

WS00B - This article is part of a series.
Part 7: This Article

Related

ESP-IDF Adv. - Assign. 1.1

··8 mins
Create the alarm component and refactor the code to use it (guided)