Docs FTC Integration

SafeOpMode

Drop-in OpMode base class that wires the orchestrator and enforces the hardware-thread rule.

SafeOpMode

SafeOpMode extends the FTC SDK’s OpMode and does three things: it creates the orchestrator (and a HardwareActions + SafeHardwareMap) at init, it maps the SDK lifecycle onto five overridable hooks, and it asserts on every loop() that the OpMode thread has not wandered onto the hardware thread.

@TeleOp(name = "Demo", group = "Test")
public class Demo extends SafeOpMode {

    @Override
    protected void onSafeInit() {
        SafeDevice<DcMotorEx> intake = safeMap.device(DcMotorEx.class, "intake");
        orchestrator.registerNode("intake", new IntakeNode(orchestrator, intake));
        GamepadAdaptor.attach(orchestrator, gamepad1, "g1");
    }

    @Override
    protected void onSafeLoop() {
        telemetry.addData("power", orchestrator.getLatestValue("intake/power", Double.class).orElse(0.0));
        telemetry.update();
    }
}

What init() gives you

FieldTypePurpose
orchestratorOrchestratorthe bus — created via FtcOrchestrator.create()
hardwareHardwareActionsfacade over the hardware thread
safeMapSafeHardwareMapwraps hardwareMap so lookups return SafeDevices

Lifecycle hooks

SDK methodOverrideRequiredNotes
init()onSafeInit()yesCreate devices, register nodes, attach gamepads.
init_loop()onSafeInitLoop()noBetween init and start.
start()onSafeStart()noFires once when Start is pressed.
loop()onSafeLoop()noEvery iteration. Runs after the thread assertion + tick.
stop()onSafeStop()noRuns before the orchestrator is closed — your last chance to flush telemetry.

What loop() does before your code

@Override
public void loop() {
    hardware.assertNotHardwareThread();  // fail fast if the rule was broken
    hardware.tick();
    onSafeLoop();
}

The assertion throws IllegalStateException if loop() somehow runs on the hardware thread — this catches configuration bugs immediately instead of corrupting the bus.

If you need full control, override loop() yourself but call super.loop() to keep the assertion and tick.

Rules inside the hooks

  • onSafeInit runs on the OpMode thread. It may set up nodes and devices freely, because it runs before any hardware-thread work exists. Hardware writes here should go through safeMap.device(...).run(...) or hardware.run(...) — motor direction setup in a node’s constructor is the usual pattern.
  • onSafeLoop must not touch hardware directly. It is for telemetry and anything cheap. Hardware work goes through the layers described in Architecture.

Telemetry reads topics, not devices. onSafeLoop() pulls values with orchestrator.getLatestValue(...) — it never calls motor.getCurrentPosition(). That is the pattern that keeps the OpMode thread and the hardware thread from ever racing. The mecanum recipe shows it end to end.

Next steps