Skip to content

Data & Controls

UI destination

Every field below ends up in one of four places: the persistent banner (visible on every screen, no interaction needed — see Interface & Screens), a popup over the Main screen (quick, one thumb, without letting go of the controller with both hands), a Menu page (reached by stepping away from active piloting — dense data, fine-tuning, or infrequent configuration), or nowhere in the UI (internal-only, or genuinely not decided yet).

The rule applied below, mechanically: a field goes on the banner if it must be seen continuously with zero interaction; it goes in a popup if it must be read or changed in a few seconds, one thumb, without letting go of the controller with both hands; otherwise it goes on a Menu page.

See Interface & Screens for the Menu page list and Balance tuning page for the first one designed in detail. The tables below add a UI destination column next to the existing wire-protocol status — the two are independent: a field can be fully wired on the protocol and still have no UI, or the reverse.

Data to display (Pi → Controller)

Fields already live on Frame 2 (port 6004) are marked accordingly — see Robot ↔ Controller Link for implementation status. Everything else is target scope, not yet on any wire protocol.

Global

Field Description Status UI destination
robot_state Overall robot state Frame 2 (always unknown for now, state machine not implemented) Persistent banner — not built yet, would only show the fixed unknown placeholder today
status_flags (legs.status_flags) — armed, estop Robot-confirmed arm state / emergency stop Frame 2 Persistent banner
status_flags (legs.status_flags) — balance (robot-confirmed) Balance-loop-enabled, as reported by the robot Frame 2 None — the banner's BALANCE chip shows the commanded state instead (see Interface & Screens); the confirmed bit has no display anywhere today
status_flags (legs.status_flags) — motors_active Motors-driving state Frame 2 Persistent banner
Link status connected / lost Not on wire protocol — controller-side, derived from link timeout Full-screen overlay on Main (link_lost_overlay.py) — not the banner/popup/menu split, see Link loss
Raspberry Pi 5 temperature SoC temperature Not on wire protocol Diagnostics page (named, not designed yet)
Pico 2 status ok / fault Not on wire protocol (today only status_flags exists) Diagnostics page
XIAO status ok / fault Frame 2 (head.status) Diagnostics page
Nano Every status ok / fault Not on wire protocol — torso section of Frame 2 not built Diagnostics page

Legs & balance (legs)

Field Description Status UI destination
sequence Frame counter, detects loss/latency Frame 2 None — internal use only, no UI field planned
torso_inclination.x Pitch, deg Frame 2 Balance tuning page
torso_inclination.y Roll, deg Frame 2 (always 0, not computed yet) Balance tuning page, no chart — always 0, a flat-line chart would misrepresent a firmware placeholder as a real reading
torso_inclination.z Heading, deg Frame 2 (always 0, not computed yet) Balance tuning page, no chart — same reason as .y
forward_speed m/s, measured Frame 2 Balance tuning page
rotation_speed rad/s, measured Frame 2 Balance tuning page
height 0-1, echo of the commanded setpoint Frame 2 Balance tuning page — echo, colocated with the height control below
wheel_speed.left / .right rad/s, measured Frame 2 Balance tuning page

Head (head)

Field Description Status UI destination
sequence_echo Echo of the last sequence sent Frame 2 None — internal use only
distance_cm Distance sensor Frame 2 (always 0, sensor not read yet) Head popup — display only, no control. A Main-screen video overlay was discussed as an alternative and wasn't ruled out, but the popup is where it's actually implemented
imu.accel.x/y/z, imu.gyro.x/y/z Raw IMU Frame 2 (always 0, not read yet) Diagnostics page
status ok / error Frame 2 (reflects the 200 ms watchdog failsafe) Diagnostics page
Servo positions ×5 (antennas, neck 1-3) 0-1023 / clamped ranges Not telemetry — echo of the last commanded position, no physical readback on the wire (see Neck Control Firmware) Diagnostics page

Torso (torso)

Field Description Status UI destination
battery %, 0-100 Planned — Frame 2 torso.battery, section absent until the Nano Every exists on the code side Persistent banner — already implemented
pack_voltage, bus_current INA226 reading Planned — matches the Nano→Pi telemetry frame (see bus-pi-nano) Diagnostics page
power_switch_state Pololu 2813 open/closed Planned — idem Diagnostics page
Fan speed 0-255 Echo of the last commanded value, no fan sensor Torso popup — shows what the popup itself last commanded, not a telemetry echo (no torso telemetry exists yet)
LED ring brightness + per-LED RGB (7 LEDs) 0-255 each Echo of the last commanded value Torso popup, idem
Button illumination brightness 0-255 Echo of the last commanded value Torso popup, idem
shutdown_cause button press / low battery / Pi command Planned — matches the Nano→Pi telemetry frame Diagnostics page
touch.top, touch.back bool Planned — matches the Nano→Pi telemetry frame Diagnostics page

Camera

Field Description Status UI destination
Video feed Live stream Planned, port 6001, no code yet Main screen background — already implemented
Resolution — Planned None — fixed at 1200×800, the Legion Go's own screen resolution (see Interface & Screens), not drivable
Effective fps / stream latency — Planned Persistent banner — affects piloting safety directly, unlike most diagnostic numbers

Audio

Field Description Status UI destination
Robot audio (speaker monitor) — Planned, port 6002 — internal tap (audio-io → gateway-controller) not wired yet None — passive playback, no visual widget needed
Volume level — Planned Sound popup
Microphone level — Planned Sound popup

Controller (device itself)

Field Description Status UI destination
Battery level — Local to the Controller hardware, not carried by the Pi link Persistent banner — already implemented
Charging state — Local Persistent banner, same widget as battery level
Wi-Fi connection quality — Local Diagnostics page

Controls to send (Controller → Pi)

Fields already live on Frame 1 (port 6000) are marked accordingly — see Robot ↔ Controller Link for implementation status.

Global

Field Description Status UI destination
armed Enables motor actuation, whole robot Frame 1 Persistent banner + physical Y-hold gesture — already implemented
estop Emergency stop, whole robot, OR'd between sources on the arbiter side Frame 1 Persistent banner — already implemented

Legs (legs)

Field Description Status UI destination
v Forward/backward speed, -1..1 Frame 1 Main, physical stick — already implemented
yaw Turn rate, -1..1 Frame 1 Main, physical stick — already implemented
height Leg height, 0..1 Frame 1 Balance tuning page — no live drive control exists for this today
armed Per-module actuation Frame 1 None — covered by the global armed, no per-module control designed
balance Enables the balance loop Frame 1 Balance tuning page
estop Per-module emergency stop Frame 1 None — covered by the global estop, no per-module control designed
tilt_setpoint_deg Target balance angle Frame 1 Balance tuning page
kp, ki, kd Tilt PID gains Frame 1 Balance tuning page
kvel_p, kv_outer Speed damping / position hold Frame 1 Balance tuning page
kyaw_p, kyaw_i Yaw damping / heading hold Frame 1 Balance tuning page
motor_balance Left/right motor power balance Frame 1 Balance tuning page
yaw_trim Constant rotation offset Frame 1 Balance tuning page
roll_cmd Torso roll Reserved on the Pico UART frame, not exposed in Frame 1 yet, not used by firmware None — reserved, not exploited by firmware

Head (head)

Field Description Status UI destination
antenna_right, antenna_left 0-1023 Frame 1 Head popup — one slider each. Not on the physical stick; no joystick axis was ever mapped to either antenna (see Joystick mapping)
neck_1 490-680 (clamped in firmware) Frame 1 Main, physical stick — already implemented
neck_2 420-570, pitch Frame 1 Main, physical stick — already implemented
neck_3 297-673, rotation Frame 1 Main, physical stick — already implemented
eye_mode 0=Off, 1=On, 2=Blink, 3=Expression Frame 1 Head popup — renamed from the earlier "Eye mode" idea
vibrator 0-255 Frame 1 Head popup — a plain on/off toggle sending 0 or 1, not the field's full range (this session's requested behavior)

Torso (torso)

Field Description Status UI destination
Fan speed 0-255 Planned — matches the Pi→Nano command frame (fan_speed) Torso popup — implemented (widgets/torso_popup.py), moved here from a Menu page on the user's decision
LED ring brightness + per-LED RGB (7 LEDs) 0-255 each Planned — matches the Pi→Nano command frame Torso popup, idem
Button illumination brightness 0-255 Planned — matches the Pi→Nano command frame (button_led_brightness) Torso popup, idem

Camera

Field Description Status UI destination
Zoom — Planned Main, inline slider — already implemented
fps — Planned Main, inline slider — already implemented
Resolution — Planned None — fixed at 1200×800, not drivable despite appearing in this table (see zig-pi/CONTROLLER-PROTOCOL.md, "vision")
Compression quality — Planned Main, inline slider — already implemented (the "Quality" parameter)

Audio

Field Description Status UI destination
Microphone / push-to-talk — Planned, port 6003 Sound popup
Volume — Planned Sound popup
Trigger sound from local bank — Routed via arbiter → audio-io (internal channel audio.trigger, see Main Computer Software) Sound popup
AI sound mode (autonomous voice) — Not defined yet — some setup still needed on the robot side Sound popup — placeholder entry, not functional yet

Mode

Field Description Status UI destination
Operating mode selection — Not defined yet Mode popup — already implemented (Sitting/Standing/Moving), not yet wired to Frame 1
Pre-recorded action selection choreography_id Reserved on the Pico UART frame, not exposed in Frame 1 yet, not used by firmware Mode popup
AI enable/disable — Not defined yet Not decided
Sound enable/disable — Not defined yet Not decided — could belong to the Mode popup or the Sound popup, not resolved