Skip to content

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 of event_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_t from FreeRTOS queue
  • Calls event_response_ok() to create event_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_t with 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:

  1. Memory Efficiency: Queued items are smaller (compact Layer 0 instead of expanded Layer 1)
  2. Symmetric Architecture: CommandQueue and EventQueue use identical pattern
  3. Clear Separation of Concerns: Buffering (Layer 0) vs. response generation (Layer 1)
  4. 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)