Fixo Controller

Reference for the Fixo controller, which compiles robot hardware configuration into purpose-built firmware via embedded LLVM/Clang.

Design Note

Fixo firmware contains zero runtime configuration overhead: no config parser, no dynamic arrays, no type dispatch. Every actuator type, pin number, and sensor configuration is a compile-time constant. The optimizer eliminates all dead code paths, producing the smallest and fastest possible binary for the exact hardware configuration.


Overview

Fixo is the controller in OctoMY™. It bakes the entire hardware configuration into firmware as C++ template parameters and compiles it via embedded LLVM/Clang. The earlier runtime-config path (Fluxo / ArduMY) and Servotor32 binding were retired in 2026.

The trade-off is a rebuild+reflash cycle on every configuration change — acceptable because LLVM compilation takes seconds and the flash cycle is approximately 5 seconds for a typical binary.

See Why Fixo for the design rationale, and the Fixo data model for the type vocabulary used below (FixoPreset, FixoStanza, FixoActuatorType, etc.).


Supported hardware

Board MCU Flash SRAM Pins PWM
Arduino Uno ATmega328P 32 KB 2 KB 14 digital, 6 analog 6
Arduino Mega 2560 ATmega2560 256 KB 8 KB 54 digital, 16 analog 15

Compilation backends

Backend Output Purpose
AVR ELF binary, flashed to MCU Production deployment on physical Arduino boards
Harness Native x64 shared library Host-side testing with virtual actuator/sensor UI — no physical hardware required

The harness backend compiles the same template code but links against Qt stubs instead of AVR register code. Virtual servos, motors, and sensors appear as widgets in the controller UI.


Actuator types

Fixo supports the same actuator types as Actuators:

Type Description Pins required
RC Servo PWM-driven position servo 1 (PWM)
DC Motor Speed-controlled motor with direction 2 (PWM + direction)
Stepper Motor Step/direction stepper driver 2 (step + direction)
Relay On/off digital output 1 (digital)

Each actuator can optionally declare additional pins for limit switches, incremental encoders, tachometers, and position feedback sensors.


Sensor types

Type Description Pins Resolution
Digital Input Polled digital pin 1 1-bit
Analog Input ADC reading 1 10-bit (configurable)
Encoder Quadrature incremental encoder 2 (A + B) Count-based
Counter Pulse counter (IRQ-driven) 1 Count-based
Limit Switch Active-low digital input 1 1-bit
Tachometer RPM from pulse frequency 1 Frequency-based

Lobes

Fixo compiles lobe logic directly into the firmware as C++ template code. Built-in lobes:

Lobe Description
identity Passthrough — actuator values map 1:1 to outputs
wheeled Differential-drive controller; outputs left_motor / right_motor
tracked Tracked-vehicle controller; outputs left_track / right_track
legged_gait Multi-leg gait engine (used by the hexapod template)
hovering, limb_placement, trim, blink_led Specialised controllers; see Lobes reference

Each FixoLobeType carries a placement capability — board-only, native-only, or both — that determines whether the lobe runs in the firmware (compiled into the AVR ELF) or on the node (loaded from the matched native .so via dlopen). See the Lobes reference for placement details and the per-lobe input/output port lists.


Capabilities

FixoController declares the following capability flags:

Capability Description
CAP_ADD_ACTUATORS Add actuators at configuration time
CAP_REMOVE_ACTUATORS Remove actuators at configuration time
CAP_ACTUATOR_TYPE_SELECTION Choose from RC Servo, DC Motor, Stepper, Relay
CAP_PIN_ASSIGNMENT Assign physical pins to actuators
CAP_RANGE_CONFIG Configure actuator value ranges
CAP_LIMIT_SWITCHES Attach limit switch sensors
CAP_POSITION_FEEDBACK Attach position feedback sensors
CAP_INCREMENTAL_ENCODER Attach quadrature encoder sensors
CAP_TACHOMETER Attach tachometer sensors
CAP_GEAR_RATIO Configure mechanical gear ratios
CAP_CONTINUOUS_MODE Support continuous-rotation servos
CAP_LINEAR_MODE Support linear actuators

Build workflow

FixoRuntime (see the FixoRuntime reference) drives the build / inject / start lifecycle. Triggers:

Trigger Description
Manual User clicks Build, then Inject, then Start
Auto-build Preemptive background compile on preset change (500 ms debounce)
Autonomous (future) Runtime decides it is safe to rebuild and reflash

Build pipeline

  1. Source generation — FirmwareGenerator::generateFromPreset() synthesises C++ source from the active FixoPreset.
  2. Compilation — FixoBuildManager invokes the embedded LLVM/Clang to produce both an AVR ELF (board-side) and a native .so (node-side).
  3. Size check — FirmwareSizeInfo validates that the AVR ELF fits in the target board's flash.
  4. Cache — FixoFirmwareCache content-addresses the compiled artefacts by BLAKE3 hash so unchanged presets don't recompile.
  5. Inject — FirmwareFlasher writes the AVR ELF to the board via stk500v2 over serial.
  6. Load — FirmwareRuntime dlopens the native .so (when present) into the Agent process; the runtime opens the numex serial link and starts ticking the firmware.

Firmware size visualization

The preset editor exposes a live size bar driven by FirmwareSizeInfo:

Colour Meaning
Default Usage below 80 % of available flash
Orange Usage between 80 % and 100 %
Red Firmware exceeds available flash — will not fit

Configuration persistence

Fixo configuration lives in fixo.json files, not in the legacy fixo/* agent settings. See the Fixo Books reference for how presets, types, and templates are stored, and the fixo.json schema reference for the file format.

A typical layout:

<personality>/fixo/
  presets/
    rc-car.fixo.json
    hexy-explorer.fixo.json
  actuator-types/   # custom types (built-ins live in libfixo's qrc)
  sensor-types/
  lobe-types/
  stanza-templates/
  preset-templates/

Files are discovered recursively by FixoLibrary::scan(); routing by fixoType field places each into the matching Fixo*Book.


UI surface

Fixo's UI lives in the Fixo Studio activity (see the Fixo Studio reference). The Studio is the entry point for:

The retired multi-screen "BoardSelect → Configure → Build → Dashboard" widget has been replaced by the Studio + a per-preset FixoRuntime view. The Studio runs on Hub, Agent, or Remote (each node can author and share presets); only "set as active runtime" is Agent-specific.


Source code

Component Library Path
FixoController libfixo fixo/controller/FixoController.hpp
FirmwareGenerator libfixo fixo/controller/FirmwareGenerator.hpp
FixoBuildManager libfixo fixo/controller/FixoBuildManager.hpp
FirmwareSizeInfo libfixo fixo/controller/FirmwareSizeInfo.hpp
FixoRuntime libfixo fixo/runtime/FixoRuntime.hpp
FirmwareRuntime libfixo fixo/runtime/FirmwareRuntime.hpp
FixoLibrary and Books libfixo fixo/library/Fixo*.hpp
Studio activities libfixo fixo/studio/Fixo*.hpp
Type editors libfixo fixo/type/Fixo*Editor*.hpp
Preset editor libfixo fixo/studio/FixoPresetEditorActivity.hpp
Control widgets libfixo fixo/widget/Fixo*.hpp