State Machine Architecture
The OSSM firmware uses a declarative state machine to manage device behavior. This ensures predictable transitions between operating modes and prevents invalid states that could cause unexpected behavior.State Diagram
The following diagram shows all states and transitions in the OSSM state machine:Design Overview
The state machine is implemented using Boost.SML (State Machine Language), a header-only C++14 library that provides a domain-specific language for defining state machines.Why Boost.SML?
Boost.SML was chosen for OSSM because it offers:- Compile-time verification - Invalid transitions are caught at compile time, not runtime
- Zero runtime overhead - Performance equivalent to hand-written switch/case
- Declarative syntax - The transition table reads like documentation
- Thread safety - Built-in support for concurrent access via policies
- Small footprint - Single header, ~2000 lines of code, no dependencies
Transition Table Syntax
Each row in the transition table follows this pattern:menu.idle state and a buttonPress event occurs, if the guard isOption(Menu::SimplePenetration) returns true, transition to simplePenetration state.”
For a complete tutorial on transition table syntax, see the Boost.SML Tutorial.
Events
Events trigger state transitions. They are simple structs defined inEvents.h and can be dispatched from anywhere in the codebase.
| Event | Description | Typical Source |
|---|---|---|
ButtonPress | Single button click | Rotary encoder button |
LongPress | Button held for extended duration | Rotary encoder button (held) |
DoublePress | Two rapid button clicks | Rotary encoder button |
Done | Async operation completed | Homing task, preflight check |
Error | Operation failed | Homing failure, stroke too short |
EmergencyStop | Immediate halt required | Safety systems |
Home | Request homing sequence | External command |
Dispatching Events
Events are dispatched using the globalstateMachine instance:
state.cpp and exposed as a global pointer.
Guards
Guards are conditional checks that determine whether a transition should occur. They returntrue to allow the transition or false to block it.
| Guard | Description | Returns true when… |
|---|---|---|
isOnline | Check WiFi connection | Device is connected to WiFi |
isUpdateAvailable | Check for firmware updates | Server reports newer version available |
isStrokeTooShort | Validate homing result | Measured stroke is below minimum threshold |
isOption(Menu) | Check selected menu item | Current menu selection matches the specified option |
isPreflightSafe | Validate speed knob position | Speed potentiometer is in the dead zone (safe to start) |
isFirstHomed | One-time initial homing check | This is the first successful homing since boot |
isNotHomed | Check homing status | Device has not been homed or homing was invalidated |
Guard Implementation
Guards are defined asconstexpr lambdas in the guards namespace. They call forward-declared implementation functions to keep the header lightweight:
guards.cpp, keeping compile times fast and allowing implementation changes without recompiling the transition table.
For more on guard patterns, see the Boost.SML Guards documentation.
Actions
Actions are functions executed during state transitions. They perform side effects like updating the display, starting motors, or resetting settings.Display Actions
| Action | Description |
|---|---|
drawHello | Show welcome/boot screen |
drawMenu | Render the main menu |
drawPlayControls | Show speed/stroke/depth controls |
drawPatternControls | Show pattern selection UI |
drawPreflight | Show “reduce speed to start” warning |
drawHelp | Display help/support information |
drawWiFi | Show WiFi configuration screen |
drawUpdate | Show “checking for updates” screen |
drawNoUpdate | Show “firmware is up to date” message |
drawUpdating | Show update progress |
drawError | Display error state with message |
Motion Actions
| Action | Description |
|---|---|
startHoming | Begin the homing sequence |
clearHoming | Reset homing state variables |
startSimplePenetration | Start Simple Penetration mode task |
startStrokeEngine | Start Stroke Engine mode task |
startStreaming | Start Streaming mode task |
emergencyStop | Force stop motor and disable outputs |
Settings Actions
| Action | Description |
|---|---|
resetSettingsStrokeEngine | Initialize defaults for Stroke Engine (speed=0, stroke=50, depth=10, sensation=50) |
resetSettingsSimplePen | Initialize defaults for Simple Penetration (speed=0, stroke=0, depth=50) |
incrementControl | Cycle through control parameters (stroke → depth → sensation) |
setHomed | Mark device as successfully homed |
setNotHomed | Invalidate homing (requires re-home before play) |
System Actions
| Action | Description |
|---|---|
restart | Restart the ESP32 |
resetWiFi | Clear saved WiFi credentials |
updateOSSM | Download and install firmware update |
Action Implementation
Actions are defined asconstexpr lambdas in the actions namespace. Like guards, they delegate to forward-declared implementation functions:
actions.cpp call into the appropriate feature modules:
For more on action patterns, see the Boost.SML Actions documentation.
Thread Safety
The OSSM state machine is configured with thread-safe policies to handle events from multiple FreeRTOS tasks:- The main loop
- Button interrupt handlers
- BLE command handlers
- Background tasks
state.cpp:
For more on thread safety policies, see the Boost.SML Policies documentation.
State Logging
The firmware includes aStateLogger that logs all state transitions for debugging:
Architecture Notes
The state machine follows a decoupled modular architecture:- State machine definition (
machine.h) contains only the transition table - Actions and guards are
constexprlambdas that delegate to implementation functions - Feature modules (like
pages::,simple_penetration::,stroke_engine::) contain the actual logic - Global state structs manage application state instead of class members
The
OSSM class in ossm/OSSM.h is retained for backward compatibility with BLE command handling. New features should use stateless namespace functions that operate on global state.Further Reading
Folder Structure
Understand the source code organization and design philosophy.
Boost.SML Tutorial
Step-by-step guide to building state machines with Boost.SML.
Boost.SML User Guide
Complete reference for all Boost.SML features.
Operating Modes
Learn about Simple Penetration, Stroke Engine, and Streaming modes.

