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
| Field | Type | Purpose |
|---|---|---|
orchestrator | Orchestrator | the bus — created via FtcOrchestrator.create() |
hardware | HardwareActions | facade over the hardware thread |
safeMap | SafeHardwareMap | wraps hardwareMap so lookups return SafeDevices |
Lifecycle hooks
| SDK method | Override | Required | Notes |
|---|---|---|---|
init() | onSafeInit() | yes | Create devices, register nodes, attach gamepads. |
init_loop() | onSafeInitLoop() | no | Between init and start. |
start() | onSafeStart() | no | Fires once when Start is pressed. |
loop() | onSafeLoop() | no | Every iteration. Runs after the thread assertion + tick. |
stop() | onSafeStop() | no | Runs 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
onSafeInitruns 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 throughsafeMap.device(...).run(...)orhardware.run(...)— motor direction setup in a node’s constructor is the usual pattern.onSafeLoopmust 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 withorchestrator.getLatestValue(...)— it never callsmotor.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
- HardwareActions — the
hardwarefield in detail. - SafeDevice — the devices you create from
safeMap.