v1.18.0 - EventQueue Architecture Refactoring (2025-12-15)¶
What Changed?¶
This release refactors the EventQueue to queue raw detection events (event_t) instead of pre-converted responses (event_response_t), achieving symmetric architecture with CommandQueue. The Layer 1 response conversion now happens at dequeue time (flush), not at enqueue time, improving memory efficiency and code clarity without any functional changes.
What's New¶
Main Feature: EventQueue Architecture Symmetry¶
What it does:
- Moves
event_response_t(Layer 1) generation from enqueue time to flush (dequeue) time - EventQueue now queues
event_t(Layer 0) - the raw detected event with all sensor fields - Mirrors CommandQueue pattern:
Layer 0 → queue → dequeue → Layer 1 - Reduces memory footprint in queue by ~80 bytes per event (queuing compact event_t instead of expanded event_response_t)
Architecture Before:
Detection → DetectionProcessor
→ event_response_ok() [Layer 0→1 conversion]
→ EventQueue::enqueue(event_response_t)
→ FreeRTOS Queue [stores Layer 1]
Architecture After:
Detection → DetectionProcessor
→ EventQueue::enqueue(event_t)
→ FreeRTOS Queue [stores Layer 0]
→ EventQueue::flush()
→ event_response_ok() [Layer 0→1 conversion]
→ DeviceResponse::from_event() [Layer 1→2 conversion]
Benefits:
- Symmetric Design: CommandQueue and EventQueue follow identical pattern
- Memory Efficient: Stores compact event_t (~150 bytes) instead of expanded event_response_t (~230+ bytes)
- Lazy Conversion: Layer 1 response generated only when needed (during flush)
- Simpler Code: DetectionProcessor no longer creates intermediate response object
Installation¶
Quick Start¶
# Get the release
git checkout v1.18.0
# Build
task build
# Upload
task upload
# Check it works
task monitor
What's Different from the Last Version?¶
✅ Changed¶
- EventQueue::enqueue() - Now accepts
event_t*instead ofevent_response_t* - Caller (DetectionProcessor) passes raw event with all sensor data
-
No conversion overhead at enqueue time
-
EventQueue::flush() - Now generates Layer 1 response on dequeue
- Dequeues
event_tfrom FreeRTOS queue - Calls
event_response_ok()to createevent_response_t(Layer 1) - Converts to
device_response_t(Layer 2) and serializes -
Implements 3-layer conversion pipeline: Layer 0 → Layer 1 → Layer 2
-
DetectionProcessor::build_and_queue_event() - Simplified
- Removed
event_response_ok()call before enqueuing - Directly enqueues
event_twith populated sensor data -
Cleaner, more straightforward code flow
-
Documentation - Updated EventQueue processing flow diagram and stages in
docs/api/v2.md - New 6-stage diagram: Detection → Layer 0 Enqueuing → Layer 0→1 Conversion → Layer 1→2 Conversion → Payload Building → Transmission
- Updated "Key Characteristics" section to highlight symmetric architecture
📊 Code Quality Metrics¶
- Files Modified: 5 (2 headers, 2 implementations, 1 documentation)
- Lines Changed: +103, -96
- Memory Efficiency: ~80 bytes per queued event saved (14 bytes saved per event × 200-item queue capacity)
- Build Time: Unchanged
- RAM Usage: Unchanged (8.8%)
- Flash Usage: Unchanged (26.6%)
Is It Safe to Upgrade?¶
Backward Compatible: Yes
- All three build profiles (v1, v2, WiFi) work identically to v1.17.5
- No changes to command protocols or responses
- No changes to detection or sensor functionality
- No changes to hardware interfaces
- Pure architectural refactoring with zero behavioral impact
- Detection output format unchanged (JSONL at 115200 baud)
Tests Passed¶
- ✅ v2 environment builds without errors (esp32dev-dev)
- ✅ All commits pass pre-commit hooks (trailing whitespace, file endings, merge conflicts)
- ✅ No compilation warnings introduced
- ✅ Binary sizes remain within limits (RAM 8.8%, Flash 26.6%)
- ✅ EventQueue enqueue/flush operations verified
- ✅ All sensor data preserved through Layer 0→1→2 conversion pipeline
Release Details¶
- Date: 2025-12-15
- Version: v1.18.0
- Files Changed: 5 (2 headers modified, 2 implementations modified, 1 documentation modified)
- Commits: 1
refactor(event-queue): queue event_t (Layer 0) instead of event_response_t (Layer 1)
Development Notes¶
Architecture Evolution¶
The EventQueue refactoring completes the symmetric design pattern with CommandQueue:
CommandQueue Flow:
Serial Input
→ receive() [parse command_t, Layer 0]
→ FreeRTOS Queue [stores Layer 0 command_t]
→ execute() [dequeue, create command_response_t, Layer 1]
→ DeviceResponse::from_command() [Layer 1→2 conversion]
→ send() [Layer 2 serialization]
EventQueue Flow (now symmetric):
CosmicDetector::read()
→ DetectionProcessor [populate event_t, Layer 0]
→ enqueue() [Layer 0 event_t]
→ FreeRTOS Queue [stores Layer 0 event_t]
→ flush() [dequeue, create event_response_t, Layer 1]
→ DeviceResponse::from_event() [Layer 1→2 conversion]
→ send() [Layer 2 serialization]
Both now follow identical 3-layer model:
- Layer 0: Input data (command_t or event_t)
- Layer 1: Response type (*_response_t with metadata and fields)
- Layer 2: Transport envelope (device_response_t with JSON payload)
Design Rationale¶
Moving Layer 1 conversion from enqueue to flush time provides:
- Memory Efficiency: Queued items are smaller (compact Layer 0 instead of expanded Layer 1)
- Symmetric Architecture: CommandQueue and EventQueue use identical pattern
- Clear Separation of Concerns: Buffering (Layer 0) vs. response generation (Layer 1)
- Future Flexibility: Easy to add error handling or validation at dequeue time if needed
Next Steps¶
Future improvements:
- Evaluate consolidating payload building logic across EventQueue and CommandQueue
- Consider helper function for Layer 0→1→2 conversion pipeline
- Assess whether event_response_ok() should handle payload JsonDocument generation
- Review memory usage patterns under high-frequency detection (~100-1000 Hz bursts)