Skip to content

CODE

Warning

The following code should be based on the code in the release code, which may have been updated.

online_sensing.h

/**
 * @file online_sensing.h
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief Online sensing module - Continuous sensor data acquisition and transmission
 * @version 1.0
 * @date 2025-01-XX
 * @copyright Copyright (c) 2025
 *
 * @details
 * This module provides online sensor data acquisition with configurable:
 * - Sampling frequency (default: 20 Hz)
 * - MQTT transmission (enabled/disabled)
 * - Serial output (enabled/disabled)
 *
 * Features:
 * - Fixed-rate data acquisition using FreeRTOS tasks
 * - Automatic MQTT payload formatting (JSON)
 * - Configurable output channels
 * - Thread-safe operation
 */

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "node_acc_adxl355.h"
#include "tiny_measurement_config.h"

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * CONFIGURATION STRUCTURE
 * ============================================================================ */

/**
 * @brief Online sensing configuration
 */
typedef struct
{
    float sampling_frequency_hz;  ///< Sampling frequency in Hz (default: 20.0)
    bool enable_mqtt;              ///< Enable MQTT transmission (default: true)
    bool enable_serial;             ///< Enable serial output (default: true)
#ifdef TINY_MEASUREMENT_ENABLE_LCD
    bool enable_lcd;                ///< Enable LCD display output (default: false)
#endif
    const char *mqtt_topic;         ///< MQTT topic for publishing (default: NULL)
} online_sensing_config_t;

/* ============================================================================
 * FUNCTION DECLARATIONS
 * ============================================================================ */

/**
 * @brief Initialize online sensing module
 * @param config Configuration structure (NULL for default configuration)
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_init(const online_sensing_config_t *config);

/**
 * @brief Start online sensing task
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_start(void);

/**
 * @brief Stop online sensing task
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_stop(void);

/**
 * @brief Deinitialize online sensing module
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_deinit(void);

/**
 * @brief Update sampling frequency at runtime
 * @param frequency_hz New sampling frequency in Hz
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_set_frequency(float frequency_hz);

/**
 * @brief Enable or disable MQTT transmission
 * @param enable true to enable, false to disable
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_set_mqtt_enable(bool enable);

/**
 * @brief Enable or disable serial output
 * @param enable true to enable, false to disable
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_set_serial_enable(bool enable);

/**
 * @brief Get current configuration
 * @param config Output parameter for current configuration
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_get_config(online_sensing_config_t *config);

/**
 * @brief Check if online sensing is running
 * @param is_running Output parameter: true if running, false otherwise
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_is_running(bool *is_running);

/**
 * @brief Set ADXL355 sensor handle (must be called before starting)
 * @param handle ADXL355 handle pointer
 * @return ESP_OK on success, error code on failure
 */
esp_err_t online_sensing_set_sensor_handle(adxl355_handle_t *handle);

#ifdef __cplusplus
}
#endif

online_sensing.c

Key Implementation Details

Timer Callback

static void sampling_timer_callback(void *arg)
{
    if (!s_is_running || s_adxl355_handle == NULL)
    {
        return;
    }

    adxl355_accelerations_t accel;
    float temperature;
    char mqtt_pub_buff[128];

    // Read sensor data
    esp_err_t accel_ret = adxl355_read_accelerations(s_adxl355_handle, &accel);
    esp_err_t temp_ret = adxl355_read_temperature(s_adxl355_handle, &temperature);

    if (accel_ret == ESP_OK && temp_ret == ESP_OK)
    {
        // MQTT transmission - compact format
        if (s_config.enable_mqtt && s_is_mqtt_connected)
        {
            snprintf(mqtt_pub_buff, sizeof(mqtt_pub_buff), "%.6f,%.6f,%.6f,%.2f",
                     accel.x, accel.y, accel.z, temperature);
            const char *topic = s_config.mqtt_topic ? s_config.mqtt_topic : MQTT_PUBLISH_TOPIC;
            esp_mqtt_client_publish(s_mqtt_client, topic,
                                    mqtt_pub_buff, strlen(mqtt_pub_buff), 1, 0);
        }

        // Serial output
        if (s_config.enable_serial)
        {
            printf("%.6f,%.6f,%.6f,%.2f\n", accel.x, accel.y, accel.z, temperature);
            fflush(stdout);
        }

#ifdef TINY_MEASUREMENT_ENABLE_LCD
        // LCD display
        if (s_config.enable_lcd)
        {
            char lcd_str[48];
            snprintf(lcd_str, sizeof(lcd_str), "X:%.6f", accel.x);
            lcd_show_string(0, 0, lcd_self.width, 16, 16, lcd_str, BLACK);
            // ... (Y, Z, temperature)
        }
#endif
    }
}

Initialization

esp_err_t online_sensing_init(const online_sensing_config_t *config)
{
    if (s_is_running)
    {
        ESP_LOGW(TAG, "Online sensing is already running, stop it first");
        return ESP_ERR_INVALID_STATE;
    }

    // Apply configuration
    if (config != NULL)
    {
        memcpy(&s_config, config, sizeof(online_sensing_config_t));
    }
    else
    {
        // Use default configuration
        s_config.sampling_frequency_hz = ONLINE_SENSING_DEFAULT_FREQ_HZ;
        s_config.enable_mqtt = ONLINE_SENSING_DEFAULT_ENABLE_MQTT;
        s_config.enable_serial = ONLINE_SENSING_DEFAULT_ENABLE_SERIAL;
        s_config.mqtt_topic = NULL;
    }

    // Validate configuration
    if (s_config.sampling_frequency_hz <= 0.0f || s_config.sampling_frequency_hz > 10000.0f)
    {
        ESP_LOGE(TAG, "Invalid sampling frequency: %.2f Hz (valid range: 0.1 - 10000 Hz)",
                 s_config.sampling_frequency_hz);
        return ESP_ERR_INVALID_ARG;
    }

    return ESP_OK;
}

Start Function

esp_err_t online_sensing_start(void)
{
    if (s_is_running)
    {
        ESP_LOGW(TAG, "Online sensing is already running");
        return ESP_ERR_INVALID_STATE;
    }

    if (s_adxl355_handle == NULL)
    {
        ESP_LOGE(TAG, "ADXL355 handle not set. Call online_sensing_set_sensor_handle() first");
        return ESP_ERR_INVALID_STATE;
    }

    // Calculate timer period in microseconds
    uint64_t period_us = (uint64_t)(1000000.0f / s_config.sampling_frequency_hz);

    // Validate period
    if (period_us < 100)
    {
        ESP_LOGE(TAG, "Sampling frequency too high: %.2f Hz (max: 10000 Hz)", 
                 s_config.sampling_frequency_hz);
        return ESP_ERR_INVALID_ARG;
    }

    // Create timer
    esp_timer_create_args_t timer_args = {
        .callback = sampling_timer_callback,
        .arg = NULL,
        .name = "online_sampling_timer"
    };

    esp_err_t ret = esp_timer_create(&timer_args, &s_sampling_timer);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to create timer: %s", esp_err_to_name(ret));
        return ret;
    }

    // Set running flag before starting timer
    s_is_running = true;

    // Perform immediate first sample (at t=0)
    sampling_timer_callback(NULL);

    // Start periodic timer
    ret = esp_timer_start_periodic(s_sampling_timer, period_us);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to start timer: %s", esp_err_to_name(ret));
        esp_timer_delete(s_sampling_timer);
        s_sampling_timer = NULL;
        s_is_running = false;
        return ret;
    }

    ESP_LOGI(TAG, "Online sensing started (frequency: %.2f Hz, period: %llu us)",
             s_config.sampling_frequency_hz, period_us);
    return ESP_OK;
}

Stop Function

esp_err_t online_sensing_stop(void)
{
    if (!s_is_running)
    {
        ESP_LOGW(TAG, "Online sensing is not running");
        return ESP_ERR_INVALID_STATE;
    }

    s_is_running = false;

    // Stop and delete timer
    if (s_sampling_timer != NULL)
    {
        esp_timer_stop(s_sampling_timer);
        esp_timer_delete(s_sampling_timer);
        s_sampling_timer = NULL;
    }

    ESP_LOGI(TAG, "Online sensing stopped");
    return ESP_OK;
}

Usage Example

#include "online_sensing.h"
#include "node_acc_adxl355.h"

// Initialize sensor
adxl355_handle_t adxl355_handle;
adxl355_init(&adxl355_handle, ADXL355_RANGE_2G, ADXL355_ODR_1000);

// Set sensor handle
online_sensing_set_sensor_handle(&adxl355_handle);

// Initialize with default configuration
online_sensing_init(NULL);

// Start online sensing
online_sensing_start();

// Runtime configuration update
online_sensing_set_frequency(50.0f);  // Change to 50 Hz
online_sensing_set_mqtt_enable(false); // Disable MQTT

// Stop when done
online_sensing_stop();

// Cleanup
online_sensing_deinit();