GamepadAdaptor
GamepadAdaptor reflects an SDK Gamepad onto the bus. One line gives every button and axis a topic:
GamepadAdaptor.attach(orchestrator, gamepad1, "g1");
After this, gamepad 1’s state flows onto the bus at 60 Hz — no polling code, no edge-detection code, nothing else.
Topics published
Every public non-static boolean field becomes a button with three topics:
| Topic | Type | Meaning |
|---|---|---|
g1/<button> | Boolean | current state, published every poll |
g1/<button>/rising | Boolean | fires (value true) on press (0→1) |
g1/<button>/falling | Boolean | fires (value true) on release (1→0) |
Every public non-static float field becomes an axis topic:
| Topic | Type | Meaning |
|---|---|---|
g1/<axis> | Float | current value, published every poll |
The real SDK Gamepad (v11.2) yields 27 button fields — including a, b, x, y, circle, cross, square, triangle, the four dpad_* keys, left_bumper, right_bumper, start, back, guide, options, share, ps, the stick buttons, touchpad, touchpad_finger_1/2, and left_trigger_pressed / right_trigger_pressed — and 10 axis fields: left_stick_x, left_stick_y, right_stick_x, right_stick_y, left_trigger, right_trigger, and the four touchpad coordinates. Non-boolean/float fields (id, timestamp, nextRumbleApproxFinishTime) are skipped, and any field a future SDK adds is picked up automatically by the reflection.
Reading the values
Edges — subscribe (fires once per press):
@SubscribedTo(topic = "g1/right_bumper/rising")
@OnHardwareThread
public void grab(Boolean v) {
claw.run(s -> s.setPosition(GRAB));
}
Axes — poll (inside a loop):
double y = -orchestrator.getLatestValue("g1/left_stick_y", Float.class)
.map(Float::doubleValue).orElse(0.0);
Axes are
Float. The SDK stores them asfloat, sogetLatestValue(name, Double.class)returns an emptyOptional— always fetch withFloat.classand map todoublefor math.
Details worth knowing
attachregisters a node namedGamepadAdaptor:g1and returns that name; it is cleaned up automatically when the orchestrator closes.- All topics are pre-created at attach time, so subscriptions made right after
attach(in the sameonSafeInit) never miss values. - The adaptor polls on the scheduler pool — gamepad reads never need the hardware thread.
- The base
<button>topic publishes current state every tick, so it also works as a “held” input;/risingand/fallingpublish only on the transition.
Two gamepads
GamepadAdaptor.attach(orchestrator, gamepad1, "g1");
GamepadAdaptor.attach(orchestrator, gamepad2, "g2");
Each adaptor is independent; the parent prefix keeps their topics apart. See the two-controller recipe for the robot-validated driver/operator split.
Next steps
- Mecanum drive — gamepad topics driving a real drivetrain.
- Subscriptions — events vs. polling.