NOTES¶
Overview¶
Architecture 1 implements a Producer-Consumer pattern using a circular buffer for real-time sensor data acquisition and processing. This architecture is designed for moderate sampling frequencies (0.1 - 1000 Hz, recommended: 100 Hz - 1 kHz) and provides a simple, well-understood implementation pattern.
Architecture Principles¶
Producer-Consumer Pattern¶
The architecture uses a classic Producer-Consumer pattern with the following components:
- Producer Task (High Priority): Reads sensor data at fixed intervals and writes to a circular buffer
- Consumer Task (Medium Priority): Periodically reads from the circular buffer and processes data
- Circular Buffer: PSRAM-based buffer that allows overwriting old data when full (FIFO overwrite)
- Mutex Synchronization: Protects buffer access between producer and consumer tasks
Key Design Decisions¶
- Circular Buffer Instead of Queue: Uses a circular buffer for better memory efficiency and simpler implementation
- FIFO Overwrite: When buffer is full, oldest data is overwritten (no data loss tracking for producer, but consumer can detect overwrites)
- Periodic Consumer Processing: Consumer processes buffer every 50ms, reading up to 10 most recent samples
- Task Notification: ESP timer callback notifies producer task via
xTaskNotifyto avoid blocking timer callback
Implementation Details¶
Timer-Based Sampling¶
- ESP Timer: Creates a periodic timer with period =
1,000,000 / sampling_frequency_hzmicroseconds - Timer Callback: Executes in timer context, notifies producer task via
xTaskNotify - Immediate First Sample: Performs one sample immediately before starting periodic timer
Producer Task¶
Priority: 10 (High priority for timely acquisition)
Functionality:
- Waits for timer notification via
xTaskNotifyWait - Reads sensor data (acceleration and temperature)
- Prepares sample structure with timestamp
- Writes to circular buffer with mutex protection
- Updates acquisition statistics
Buffer Write Logic:
- Takes mutex with 10ms timeout
- Writes to current write pointer position
- Advances write pointer (circular)
- If buffer full (write pointer catches read pointer), advances read pointer (FIFO overwrite)
- Updates overwrite count for monitoring
Consumer Task¶
Priority: 8 (Medium priority, lower than producer to ensure acquisition priority)
Functionality:
- Waits for processing interval (50ms)
- Takes mutex to read buffer snapshot
- Reads up to 10 most recent samples from read pointer
- Advances read pointer
- Processes samples (feature extraction, detection, etc.)
- Updates processing statistics
- Outputs results (serial, MQTT, LCD)
Processing Logic:
- Calculates available samples (distance between read and write pointers)
- Reads up to
CONSUMER_PROCESS_SAMPLE_COUNT(10) samples - Handles circular buffer wrap-around
- Zero-fills if not enough samples available
Circular Buffer¶
Size: 512 samples (configurable via CIRCULAR_BUFFER_SIZE)
Memory: Allocated from PSRAM for large buffer support
Synchronization: Mutex-protected access
Behavior:
- FIFO overwrite when full
- Read pointer advances as consumer reads
- Write pointer advances as producer writes
- Overwrite count tracked for monitoring

Figure: Circular buffer operation showing producer writing and consumer reading with FIFO overwrite behavior
Acceleration Detection¶
When enabled, the consumer task performs acceleration detection on processed samples:
Conditions:
|x| > 0.5g OR |y| > 0.5g OR |z| < 0.5g
LCD Feedback:
- RED when conditions are met
- WHITE otherwise
- Color persistence: 0.3 seconds
- Minimum update interval: 100ms (to avoid SPI bus conflicts)
Data Flow¶
ESP Timer → Timer Callback → xTaskNotify → Producer Task
↓
Read Sensor (SPI)
↓
Circular Buffer (Mutex)
↓
Consumer Task (50ms interval)
↓
Process Samples
↓
┌───────────────────┴───────────────────┐
↓ ↓ ↓
Serial MQTT LCD
Configuration¶
Default Configuration¶
#define RT_PC_QUEUE_SIZE_DEFAULT 50
#define RT_PC_PRODUCER_PRIORITY 10
#define RT_PC_CONSUMER_PRIORITY 8
#define RT_PC_PRODUCER_STACK_SIZE 4096
#define RT_PC_CONSUMER_STACK_SIZE 8192
#define CIRCULAR_BUFFER_SIZE 512
#define CONSUMER_PROCESS_INTERVAL_MS 50
#define CONSUMER_PROCESS_SAMPLE_COUNT 10
Configuration Parameters¶
- Sampling Frequency: 0.1 - 1000 Hz (validated at initialization)
- Queue Size: Used for configuration structure compatibility (not used in circular buffer mode)
- Circular Buffer Size: 512 samples (fixed at compile time)
- Consumer Process Interval: 50ms (fixed at compile time)
- Consumer Process Sample Count: 10 samples (fixed at compile time)
Features¶
Advantages¶
- Simple Implementation: Well-understood Producer-Consumer pattern
- Decoupled Acquisition and Processing: Producer and consumer operate independently
- Memory Efficient: Circular buffer with overwrite capability
- Low Overhead: Minimal synchronization overhead
- Suitable for Medium Frequency: Optimized for 100 Hz - 1 kHz range
Limitations¶
- Buffer May Become Bottleneck: If consumer cannot keep up, data may be overwritten
- Fixed Processing Interval: Consumer processes at fixed 50ms intervals
- Limited to Recent Samples: Only processes last 10 samples per cycle
- No DMA Support: Uses standard SPI transfers (CPU-bound)
Performance Characteristics¶
Suitable Frequency Range¶
- Valid Range: 0.1 - 1000 Hz (validated at initialization)
- Recommended: 100 Hz - 1 kHz
- Maximum: Up to 1 kHz (validated limit)
- Minimum: 0.1 Hz (practical limit)
Resource Usage¶
- Memory: Circular buffer (512 samples × 32 bytes = 16 KB from PSRAM)
- CPU: Medium usage (producer and consumer tasks)
- Synchronization: Single mutex for buffer access
Usage Notes¶
- Initialization Order: Must call
arch_pc_init()beforearch_pc_set_sensor_handle() - Sensor Handle: Must be set before starting
- Buffer Monitoring: Monitor
overwrite_countin statistics to detect if consumer is falling behind - Processing Latency: Consumer processes every 50ms, so processing latency is up to 50ms
- Sample Count: Consumer processes up to 10 samples per cycle for real-time response
Error Handling¶
- Mutex Timeout: Producer drops sample if mutex cannot be acquired within 10ms
- Buffer Not Initialized: Sample dropped if buffer not ready
- Sensor Read Failure: Logged but does not stop processing
- Task Creation Failure: Returns error, cleans up resources
Thread Safety¶
- Mutex Protection: All buffer access is protected by mutex
- Task Isolation: Producer and consumer tasks are properly isolated
- Statistics: Updated atomically within mutex-protected sections