GroupLoop Firmware Architecture
The GroupLoop firmware is built on a modular, process-based architecture that enables easy extension and maintenance. This document describes the core architectural components and design patterns.
Architecture Overview
graph TB
subgraph "Main Application"
MAIN[main.cpp<br/>Setup & Loop]
CONFIG[Configuration<br/>NVS Storage]
end
subgraph "Core Framework"
PM[ProcessManager<br/>Process Lifecycle]
CR[CommandRegistry<br/>Command System]
WS[WebSocketManager<br/>Communication]
end
subgraph "Process Layer"
LED[LED Process<br/>NeoPixel Control]
VIB[Vibration Process<br/>Motor Control]
IMU[IMU Process<br/>Sensor Reading]
BLE[BLE Process<br/>Beacon Scanning]
WIFI[WiFi Process<br/>Network Management]
PUB[Publish Process<br/>Data Streaming]
REC[Receive Process<br/>Command Handling]
CFG[Configuration Process<br/>Settings Management]
end
subgraph "Hardware Layer"
PIXELS[NeoPixel LEDs]
MOTOR[Vibration Motor]
SENSOR[IMU Sensor]
RADIO[BLE Radio]
NET[WiFi Module]
end
MAIN --> PM
MAIN --> CONFIG
MAIN --> CR
MAIN --> WS
PM --> LED
PM --> VIB
PM --> IMU
PM --> BLE
PM --> WIFI
PM --> PUB
PM --> REC
PM --> CFG
LED --> PIXELS
VIB --> MOTOR
IMU --> SENSOR
BLE --> RADIO
WIFI --> NET
CR --> LED
CR --> VIB
CR --> IMU
CR --> BLE
CR --> WIFI
WS --> PUB
WS --> REC
Core Components
1. Process Manager
The ProcessManager class is the central orchestrator that manages the lifecycle of all processes.
Key Responsibilities: - Process registration and lifecycle management - Process state control (start/stop/halt) - Process updates and execution scheduling - Process cleanup and resource management
Key Methods:
void addProcess(const String& name, Process* process);
void startProcess(const String& name);
void haltProcess(const String& name);
void updateProcesses();
Process* getProcess(const String& name);
2. Process Base Class
All processes inherit from the Process base class, which provides a consistent interface and lifecycle management.
Virtual Methods:
- setup() - Initialize process resources
- update() - Main process logic (called continuously)
- getState() - Return process state information
State Management:
- isProcessRunning() - Check if process is active
- start() / halt() - Control process execution
- setProcessManager() - Reference to process manager
3. Command Registry
The CommandRegistry provides a centralized command system that allows processes to register command handlers.
Features: - Command registration with lambda functions - Parameter parsing and validation - Error handling and logging - Command discovery and listing
Usage Pattern:
4. WebSocket Manager
The WebSocketManager handles all network communication with the server.
Responsibilities: - WebSocket connection management - Message sending and receiving - Connection state monitoring - Device registration
Process Lifecycle
stateDiagram-v2
[*] --> Created
Created --> Setup: setup() called
Setup --> Running: start() called
Running --> Halted: halt() called
Halted --> Running: start() called
Running --> Running: update() called continuously
Halted --> [*]: Process destroyed
Running --> [*]: Process destroyed
Lifecycle Methods
- Construction: Process object created
- Setup:
setup()called once during initialization - Start:
start()called to begin execution - Update:
update()called continuously while running - Halt:
halt()called to stop execution - Destruction: Process object cleaned up
Process Dependencies
graph TD
subgraph "Startup Dependencies"
CONFIG[Configuration Process]
WIFI[WiFi Process]
BLE[BLE Process]
end
subgraph "Core Processes"
LED[LED Process]
VIB[Vibration Process]
IMU[IMU Process]
end
subgraph "Communication Processes"
PUB[Publish Process]
REC[Receive Process]
end
CONFIG --> WIFI
WIFI --> BLE
WIFI --> PUB
WIFI --> REC
LED --> PUB
VIB --> PUB
IMU --> PUB
BLE --> PUB
Dependency Rules
- Configuration Process: Must run first to load settings
- WiFi Process: Required for network communication
- BLE Process: Only starts after WiFi is connected
- Publish/Receive: Require WiFi connection
- Hardware Processes: Can run independently
Memory Management
Process Storage
- Processes stored in
std::map<String, Process*> - Automatic cleanup in ProcessManager destructor
- Stack-allocated process objects
Configuration Storage
- ESP32 NVS (Non-Volatile Storage) for persistence
- JSON format for human-readable configuration
- Automatic save/load on configuration changes
Dynamic Memory
- Minimal dynamic allocation
- String objects for configuration
- Lambda functions for command handlers
Error Handling
Process Errors
graph TD
ERROR[Process Error]
ERROR --> LOG[Log Error]
ERROR --> CONTINUE[Continue Operation]
ERROR --> HALT[Halt Process]
ERROR --> RESTART[Restart Process]
LOG --> CONTINUE
HALT --> RESTART
RESTART --> CONTINUE
Error Recovery Strategies
- Log and Continue: Non-critical errors
- Process Halt: Critical errors requiring intervention
- Automatic Restart: For recoverable processes
- Fallback Behavior: Default safe states
Performance Characteristics
Update Frequencies
- Main Loop: ~1000 Hz (1ms intervals)
- LED Updates: 50 Hz (20ms intervals)
- IMU Reading: 50 Hz
- BLE Scanning: 10 Hz
- WebSocket Updates: As needed
Memory Usage
- Flash: ~500KB (with libraries)
- RAM: ~200KB (runtime)
- NVS: ~4KB (configuration)
Power Consumption
- Active: ~100mA
- WiFi Connected: +50mA
- BLE Scanning: +20mA
- LEDs On: +30mA per LED
Design Patterns
1. Observer Pattern
- Processes observe configuration changes
- Command registry observes command execution
- WebSocket manager observes connection state
2. Strategy Pattern
- LED behaviors (breathing, solid, cycle, etc.)
- Vibration patterns
- IMU processing algorithms
3. Command Pattern
- Command registry with lambda handlers
- Parameterized command execution
- Undo/redo capability (future enhancement)
4. Singleton Pattern
- Global configuration instance
- Global command registry
- Global WebSocket manager
Extensibility
The architecture is designed for easy extension:
- New Processes: Inherit from Process base class
- New Commands: Register with CommandRegistry
- New Behaviors: Implement behavior interfaces
- New Hardware: Add hardware-specific processes
- New Protocols: Extend WebSocket manager
Threading Model
The firmware uses a single-threaded, cooperative multitasking model:
- Main Thread: Arduino loop() function
- Process Updates: Called sequentially
- Interrupts: Used for hardware events only
- No Preemption: Processes must yield voluntarily
This design ensures: - Predictable timing - No race conditions - Simple debugging - Low memory overhead