Skip to content

CODE

Warning

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

arch_common.h

/**
 * @file arch_common.h
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief Common data structures and definitions for real-time processing architectures
 * @version 1.0
 * @date 2025-01-17
 * @copyright Copyright (c) 2025
 *
 */

#pragma once

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

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * COMMON DATA STRUCTURES
 * ============================================================================ */

/**
 * @brief Real-time processing sample data structure
 * @note Compatible with offline_sensing_sample_t for easy integration
 */
typedef struct
{
    float x;                    ///< X-axis acceleration (g)
    float y;                    ///< Y-axis acceleration (g)
    float z;                    ///< Z-axis acceleration (g)
    float temp;                 ///< Temperature (°C)
    uint64_t timestamp_us;      ///< Timestamp in microseconds
} rt_process_sample_t;

/**
 * @brief Real-time processing result structure
 */
typedef struct
{
    float processed_x;          ///< Processed X-axis acceleration
    float processed_y;          ///< Processed Y-axis acceleration
    float processed_z;          ///< Processed Z-axis acceleration
    float features[8];          ///< Extracted features (expandable)
    uint64_t process_time_us;   ///< Processing time in microseconds
} rt_process_result_t;

/**
 * @brief Architecture type enumeration
 */
typedef enum
{
    RT_ARCH_PRODUCER_CONSUMER = 0,  ///< Architecture 1: Producer-Consumer Queue
    RT_ARCH_DMA_DOUBLE_BUFFER,      ///< Architecture 2: DMA + Double Buffer + Multi-task
    RT_ARCH_DMA_DUAL_CORE           ///< Architecture 3: DMA + Dual Core Division
} rt_process_arch_type_t;

/**
 * @brief Performance statistics structure
 */
typedef struct
{
    uint32_t total_samples;         ///< Total number of samples acquired
    uint32_t processed_samples;     ///< Number of samples processed
    uint32_t dropped_samples;       ///< Number of samples dropped (queue/buffer full)
    float avg_acquisition_time_us;  ///< Average acquisition time per sample
    float avg_process_time_us;      ///< Average processing time per sample
    float cpu_usage_core0;          ///< CPU usage on Core 0 (%)
    float cpu_usage_core1;          ///< CPU usage on Core 1 (%) (for dual-core arch)
    uint64_t last_sample_time_us;   ///< Timestamp of last sample
} rt_process_stats_t;

/**
 * @brief Architecture status structure
 */
typedef struct
{
    bool is_running;                ///< Whether the architecture is running
    rt_process_arch_type_t arch_type; ///< Current architecture type
    float sampling_frequency_hz;     ///< Current sampling frequency
    uint32_t queue_usage;           ///< Queue usage (for producer-consumer)
    uint32_t buffer_usage;          ///< Buffer usage (for DMA architectures)
} rt_process_status_t;

/**
 * @brief Real-time processing configuration structure
 */
typedef struct
{
    float sampling_frequency_hz;     ///< Sampling frequency in Hz (default: 100.0)
    uint32_t queue_size;             ///< Queue size for producer-consumer (default: 50)
    bool enable_mqtt;                ///< Enable MQTT output (default: false)
    bool enable_serial;              ///< Enable serial output (default: true)
    bool enable_accel_detection;     ///< Enable acceleration detection with LCD feedback (default: false)
    const char *mqtt_topic;         ///< MQTT topic (default: NULL uses default topic)
} rt_process_config_t;

#ifdef __cplusplus
}
#endif

real-time-process-arch.h

/**
 * @file real-time-process-arch.h
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief Unified interface for real-time processing architectures
 * @version 1.0
 * @date 2025-01-17
 * @copyright Copyright (c) 2025
 *
 * @details
 * This header provides a unified interface for three real-time processing architectures:
 * 1. Producer-Consumer with Circular Buffer (default)
 * 2. DMA + Double Buffer + Multi-task
 * 3. DMA + Dual Core Division
 *
 * Architecture selection is done at compile time via RT_PROCESS_ARCH_TYPE macro.
 * Default: RT_ARCH_PRODUCER_CONSUMER
 */

#pragma once

#include "arch_common.h"  // Includes rt_process_config_t
#include "node_acc_adxl355.h"
#include "esp_err.h"

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * COMPILE-TIME ARCHITECTURE SELECTION
 * ============================================================================ */

/**
 * @brief Architecture type selection at compile time
 * @note Default: RT_ARCH_PRODUCER_CONSUMER
 * @note To change: Define RT_PROCESS_ARCH_TYPE before including this header
 */
#ifndef RT_PROCESS_ARCH_TYPE
#define RT_PROCESS_ARCH_TYPE RT_ARCH_PRODUCER_CONSUMER
#endif

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

/**
 * @brief Initialize real-time processing architecture
 * @param config Configuration structure (NULL for default configuration)
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_init(const rt_process_config_t *config);

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

/**
 * @brief Start real-time processing
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_start(void);

/**
 * @brief Stop real-time processing
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_stop(void);

/**
 * @brief Deinitialize real-time processing architecture
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_deinit(void);

/**
 * @brief Get current status
 * @param status Output parameter for status
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_get_status(rt_process_status_t *status);

/**
 * @brief Get performance statistics
 * @param stats Output parameter for statistics
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_get_stats(rt_process_stats_t *stats);

/**
 * @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 rt_process_set_frequency(float frequency_hz);

/**
 * @brief Check if real-time processing is running
 * @param is_running Output parameter: true if running, false otherwise
 * @return ESP_OK on success, error code on failure
 */
esp_err_t rt_process_is_running(bool *is_running);

/**
 * @brief Enable or disable acceleration detection with LCD feedback
 * @param enable true to enable detection, false to disable
 * @return ESP_OK on success, error code on failure
 * @note Available for all three architectures
 */
esp_err_t rt_process_set_accel_detection(bool enable);

#ifdef __cplusplus
}
#endif

real-time-process-arch.cpp

/**
 * @file real-time-process-arch.cpp
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief Unified interface implementation for real-time processing architectures
 * @version 1.0
 * @date 2025-01-17
 * @copyright Copyright (c) 2025
 *
 * @details
 * This file provides a unified interface that routes calls to the selected
 * architecture implementation based on RT_PROCESS_ARCH_TYPE compile-time macro.
 */

#include "real-time-process-arch.h"

// Include the selected architecture implementation
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    #include "arch_producer_consumer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    #include "arch_dma_double_buffer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    #include "arch_dma_dual_core.h"
#else
    #error "Unknown RT_PROCESS_ARCH_TYPE"
#endif

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * UNIFIED INTERFACE IMPLEMENTATION
 * ============================================================================ */

esp_err_t rt_process_init(const rt_process_config_t *config)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_init(config);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_init(config);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_init(config);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_set_sensor_handle(adxl355_handle_t *handle)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_set_sensor_handle(handle);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_set_sensor_handle(handle);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_set_sensor_handle(handle);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_start(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_start();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_stop(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_stop();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_stop();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_stop();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_deinit(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_deinit();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_deinit();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_deinit();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_get_status(rt_process_status_t *status)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_get_status(status);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_get_status(status);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_get_status(status);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_get_stats(rt_process_stats_t *stats)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_get_stats(stats);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_get_stats(stats);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_get_stats(stats);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_set_frequency(float frequency_hz)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_set_frequency(frequency_hz);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_set_frequency(frequency_hz);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_set_frequency(frequency_hz);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_is_running(bool *is_running)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_is_running(is_running);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_is_running(is_running);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_is_running(is_running);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_set_accel_detection(bool enable)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_set_accel_detection(enable);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_set_accel_detection(enable);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_set_accel_detection(enable);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

#ifdef __cplusplus
}
#endif

Key Implementation

Compile-Time Architecture Selection

The unified interface uses compile-time macro RT_PROCESS_ARCH_TYPE to select which architecture implementation to use. This approach:

  • Zero Runtime Overhead: No function pointer indirection or runtime checks
  • Type Safety: Compile-time validation of architecture selection
  • Code Optimization: Compiler can optimize for the selected architecture
  • Single Binary: One binary per architecture (selected at compile time)

Unified Interface Routing

Each API function uses preprocessor conditionals to route calls to the appropriate architecture-specific function:

esp_err_t rt_process_start(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_start();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

Architecture-Specific Headers

The implementation file includes the appropriate architecture header based on the selected type:

#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    #include "arch_producer_consumer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    #include "arch_dma_double_buffer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    #include "arch_dma_dual_core.h"
#else
    #error "Unknown RT_PROCESS_ARCH_TYPE"
#endif

Usage Example

Basic Usage

#include "real-time-process-arch.h"
#include "node_acc_adxl355.h"

// Initialize sensor
adxl355_handle_t *adxl355_handle = adxl355_init(...);

// Configure real-time processing
rt_process_config_t config = {
    .sampling_frequency_hz = 100.0f,
    .queue_size = 50,
    .enable_mqtt = false,
    .enable_serial = true,
    .enable_accel_detection = false,
    .mqtt_topic = NULL
};

// Initialize architecture
esp_err_t ret = rt_process_init(&config);
if (ret != ESP_OK) {
    ESP_LOGE("App", "Failed to initialize RT processing");
    return;
}

// Set sensor handle
ret = rt_process_set_sensor_handle(adxl355_handle);
if (ret != ESP_OK) {
    ESP_LOGE("App", "Failed to set sensor handle");
    return;
}

// Start processing
ret = rt_process_start();
if (ret != ESP_OK) {
    ESP_LOGE("App", "Failed to start RT processing");
    return;
}

// ... application code ...

// Monitor statistics
rt_process_stats_t stats;
rt_process_get_stats(&stats);
ESP_LOGI("App", "Total samples: %lu, Processed: %lu, Dropped: %lu",
         stats.total_samples, stats.processed_samples, stats.dropped_samples);

// Stop processing
rt_process_stop();

// Deinitialize
rt_process_deinit();

Selecting Different Architecture

// Select Architecture 2: DMA + Double Buffer
#define RT_PROCESS_ARCH_TYPE RT_ARCH_DMA_DOUBLE_BUFFER
#include "real-time-process-arch.h"

// Rest of the code remains the same
rt_process_init(&config);
rt_process_set_sensor_handle(adxl355_handle);
rt_process_start();

Runtime Frequency Update

// Update sampling frequency at runtime
esp_err_t ret = rt_process_set_frequency(200.0f);
if (ret != ESP_OK) {
    ESP_LOGE("App", "Failed to update frequency");
}

Status Monitoring

rt_process_status_t status;
rt_process_get_status(&status);

ESP_LOGI("App", "Running: %s, Frequency: %.2f Hz, Queue usage: %lu",
         status.is_running ? "Yes" : "No",
         status.sampling_frequency_hz,
         status.queue_usage);