Reference

Robot API

The TCP API on port 7700 that the brain and other clients use today - framing, handshake, heartbeat and watchdog, every message and its fields.

hulk serves one API on TCP port 7700, on every network interface of the robot. A client (the "brain" on a computer, the simulator app, your own program) connects, says hello, subscribes to what it wants and sends commands. The message types are defined in the nao_rs_api crate; a client in another language needs only TCP and MessagePack.

No authentication

Anyone who can reach port 7700 can control the robot. Use it only on a network you trust. Authentication for this API is planned. The REST API on port 7702 already needs a token and answers the local network only. The death switch doesn't depend on this API: the UDP stop and naoctl work without it.

Framing

Each message is one frame: a big-endian u32 byte count, then one MessagePack value encoded with field names. Frames above 8 MiB are rejected.

Enums are externally tagged, as rmp-serde writes them:

RustMessagePack, shown as JSON
ToRobot::Heartbeat (unit variant)"Heartbeat"
ToRobot::Power(Power::Wake) (newtype variant){"Power": "Wake"}
ToRobot::Hello { version: 1, client: "me" } (struct variant){"Hello": {"version": 1, "client": "me"}}
Optionthe value, or nil
Rgb(255, 0, 0)[255, 0, 0]
JPEG bytesMessagePack bin

Connection

  1. Hello. Within 2 seconds of connecting, send {"Hello": {"version": 1, "client": "<your name>"}}. The robot answers {"Hello": {"version": 1, "robot": {"body_id", "head_id", "simulated"}}} and closes the connection if the versions differ.
  2. One controller at a time. A new connection replaces the previous client, and its subscription starts empty.
  3. Heartbeat. Send something at least every 100 ms; "Heartbeat" is enough. When the client goes quiet, the watchdog takes over: after 0.5 s the robot stands still, after 3 s it sits down. A Watchdog event reports both changes.

To the robot

MessageFieldsEffect
Helloversion: u32, client: stringHandshake (first message only)
HeartbeatnoneKeeps the watchdog quiet
Power"Wake" or "Rest"Stiff and ready, or limp
Motionsee belowWhat the body does until the next command; falling and getting up always win
LedsLeds or nilOverrides LED groups; nil hands all of them back. See LEDs
Speakersamples: [i16]Mono 16-bit PCM at 16 kHz, queued and played back to back
SpeakerClearnoneDrops the queued audio
Volumepercent: u8? (0 to 100, larger is clamped), muted: bool?Speaker volume. Fields left out keep their value; a percent above 0 also unmutes unless muted is given. Changes only the mixer, so it never pops. Not persistent: every boot starts at 65 %
Subscribestate: bool, cameras: ["Top" \| "Bottom"], camera_fps: f32, microphone: boolWhich streams the robot sends
StopnoneDeath switch. All motors off, latched until re-armed on the robot. The robot writes the switch page directly (reason Api) and also sends the guard's UDP stop, so it works even when the motion code is stuck or the guard is gone

Motors on nao-rs

Power and Motion act through hulk's motion code. On nao-rs the motor boards are not accepted yet (planned), so these commands change hulk's state but no joint moves.

Motion

VariantFieldsMeaning
StandheadStand still
WalkTox, y (m; x forward, y left), turn_to (rad, left positive, or nil), speed, headWalk to a point relative to the robot, then stop there
WalkTowardx, y, speed, headKeep walking toward a point that stays at this offset, like a joystick: (1, 0) straight on, (0.5, 0.5) a circle, (0.02, 0.5) turning on the spot. Send it again to steer, Stand to stop
SitDownnoneSit down
ArmsUpnoneArms up
SquatnoneSquat

speed is "Slow", "Normal" or "Fast". head is "Center", "LookAround" or {"LookAt": {"x", "y"}} (a point on the ground relative to the robot, in metres).

Leds

All fields are optional; a field set to nil keeps the robot's own status for that group.

FieldType
left_eye, right_eye8 × Rgb: segment 0 at the top, then clockwise as seen from the front
left_ear, right_ear10 × f32, brightness 0..1
chest, left_foot, right_footRgb

From the robot

MessageFieldsWhen
Helloversion, robot: {body_id, head_id, simulated, echo_cancellation}Answer to the client's hello. simulated is true in Webots, which has no microphones or speakers. echo_cancellation is true when the robot removes its own voice from the microphones, so a client can keep listening while it speaks; false (the default) means the robot's microphones are muted while it speaks (Its own voice)
Statesee belowAt 20 Hz while subscribed
EventTouch {sensor, pressed}, Fall {state}, Watchdog {active}When it happens
Imagecamera, time (s since the Unix epoch), width, height, jpegPer subscribed camera, throttled to camera_fps
Microphonetime, samples: [i16]Mono 16-bit PCM at 16 kHz, all four microphones mixed

State

FieldContent
timeSeconds since the Unix epoch
primary_stateUnstiff, Animation, Initial, Playing or Finished
fall_stateUpright, Falling, Fallen, StandingUp or Sitting
motionThe motion being executed: Stand, Walk, SitDown, StandUpFront, …
batterycharge, current, temperature, charging
imulinear_acceleration, angular_velocity, roll_pitch
sonarDistance to the nearest obstacle, left and right
left_foot_pressure, right_foot_pressure4 values each
touchchest_button, head_front, head_middle, head_rear, left_hand, right_hand, left_foot, right_foot
joint_positions, joint_temperatures, joint_currents26 values each, in the order below

Units are on Body sensors and units.

Joint order: head_yaw, head_pitch, left_shoulder_pitch, left_shoulder_roll, left_elbow_yaw, left_elbow_roll, left_wrist_yaw, left_hand, right_shoulder_pitch, right_shoulder_roll, right_elbow_yaw, right_elbow_roll, right_wrist_yaw, right_hand, left_hip_yaw_pitch, left_hip_roll, left_hip_pitch, left_knee_pitch, left_ankle_pitch, left_ankle_roll, right_hip_yaw_pitch, right_hip_roll, right_hip_pitch, right_knee_pitch, right_ankle_pitch, right_ankle_roll. The NAO's two hip yaw-pitch joints share one motor.

The Rust client

nao_rs_api::client::Client handles framing, the handshake and the heartbeat:

use nao_rs_api::{client::Client, Head, Motion, Power, Speed, Subscription, ToRobot};

let robot = Client::connect("nao.local:7700", "my-brain")?;
robot.send(&ToRobot::Subscribe(Subscription { state: true, ..Default::default() }))?;
robot.send(&ToRobot::Power(Power::Wake))?;
robot.send(&ToRobot::Motion(Motion::WalkTo {
    x: 1.0, y: 0.0, turn_to: None, speed: Speed::Normal, head: Head::Center,
}))?;
println!("{:?}", robot.state().map(|state| state.battery));

Security

Today's API binds all interfaces and has no authentication or encryption. The REST API's security table lists what exists and what is planned for both APIs.

On this page