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");
}