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 |