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:
| Rust | MessagePack, 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"}} |
Option | the value, or nil |
Rgb(255, 0, 0) | [255, 0, 0] |
| JPEG bytes | MessagePack bin |
Connection
- 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. - One controller at a time. A new connection replaces the previous client, and its subscription starts empty.
- 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. AWatchdogevent reports both changes.
To the robot
| Message | Fields | Effect |
|---|---|---|
Hello | version: u32, client: string | Handshake (first message only) |
Heartbeat | none | Keeps the watchdog quiet |
Power | "Wake" or "Rest" | Stiff and ready, or limp |
Motion | see below | What the body does until the next command; falling and getting up always win |
Leds | Leds or nil | Overrides LED groups; nil hands all of them back. See LEDs |
Speaker | samples: [i16] | Mono 16-bit PCM at 16 kHz, queued and played back to back |
SpeakerClear | none | Drops the queued audio |
Volume | percent: 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 % |
Subscribe | state: bool, cameras: ["Top" \| "Bottom"], camera_fps: f32, microphone: bool | Which streams the robot sends |
Stop | none | Death 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
| Variant | Fields | Meaning |
|---|---|---|
Stand | head | Stand still |
WalkTo | x, y (m; x forward, y left), turn_to (rad, left positive, or nil), speed, head | Walk to a point relative to the robot, then stop there |
WalkToward | x, y, speed, head | Keep 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 |
SitDown | none | Sit down |
ArmsUp | none | Arms up |
Squat | none | Squat |
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.
| Field | Type |
|---|---|
left_eye, right_eye | 8 × Rgb: segment 0 at the top, then clockwise as seen from the front |
left_ear, right_ear | 10 × f32, brightness 0..1 |
chest, left_foot, right_foot | Rgb |
From the robot
| Message | Fields | When |
|---|---|---|
Hello | version, 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) |
State | see below | At 20 Hz while subscribed |
Event | Touch {sensor, pressed}, Fall {state}, Watchdog {active} | When it happens |
Image | camera, time (s since the Unix epoch), width, height, jpeg | Per subscribed camera, throttled to camera_fps |
Microphone | time, samples: [i16] | Mono 16-bit PCM at 16 kHz, all four microphones mixed |
State
| Field | Content |
|---|---|
time | Seconds since the Unix epoch |
primary_state | Unstiff, Animation, Initial, Playing or Finished |
fall_state | Upright, Falling, Fallen, StandingUp or Sitting |
motion | The motion being executed: Stand, Walk, SitDown, StandUpFront, … |
battery | charge, current, temperature, charging |
imu | linear_acceleration, angular_velocity, roll_pitch |
sonar | Distance to the nearest obstacle, left and right |
left_foot_pressure, right_foot_pressure | 4 values each |
touch | chest_button, head_front, head_middle, head_rear, left_hand, right_hand, left_foot, right_foot |
joint_positions, joint_temperatures, joint_currents | 26 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:
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.