MQTT Reference
Everything you can send to a Desk Buddy over MQTT — every action, every field, with copy-paste examples.
Overview
Desk Buddy firmware listens for JSON messages on an MQTT topic. Any MQTT client — the Desk Buddy web app, a Python script, Node-RED, or your own code — can control the arm using the same protocol.
Every command follows the same shape:
{
"action": "someAction", // which handler to run
"action_id": "unique-123", // your ID — echoed back in all replies
"sender": "my_app", // anything except "firmware" (firmware ignores itself)
... // action-specific fields
}Response pattern — for every command the firmware publishes two messages back:
// 1. Immediately on receipt
{"sender":"firmware","action_id":"unique-123","status":"in_progress"}
// 2. When done (or failed)
{"sender":"firmware","action_id":"unique-123","status":"completed"}Match replies to your commands using action_id. Use a unique value per command — a timestamp, UUID, or incrementing counter all work fine.
Topics
esp32_5/test
Command topic — publish here to send commands
esp32_5/test
Response topic — subscribe here to receive replies
The firmware publishes all in_progress / completed / failed replies on the same topic it listens on.
esp32_5/HEARTBEAT
Heartbeat topic — periodic telemetry
Current servo angles, gripper position, and a timestamp. Subscribe to keep your UI in sync without polling.
baseRotate — Base Rotation
Rotate the base using one of three control modes: magnet count, encoder ticks, or a stored origin magnet position (1–12).
// By magnet count
{"action":"baseRotate","action_id":"1","controlType":"MAGNET","direction":"RIGHT","speed":"slow","value":3,"sender":"ai_server"}
// By encoder ticks
{"action":"baseRotate","action_id":"2","controlType":"ENCODER","direction":"LEFT","speed":"fast","value":800,"sender":"ai_server"}
// To a stored origin magnet position (1–12)
{"action":"baseRotate","action_id":"3","controlType":"originmagnet","direction":"RIGHT","speed":"slow","value":4,"sender":"ai_server"}controlType—"MAGNET"|"ENCODER"|"originmagnet"direction—"LEFT"|"RIGHT"(motion direction and tie-break for originmagnet)speed—"veryslow"|"slow"|"regular"|"fast"|"superfast"value— magnet count, encoder ticks, or target magnet index (1–12)
The completed reply includes an origin_magnet field with the current magnet position.
gripper — Gripper
Open, close, or soft-hold the gripper, or move it to a specific angle.
{"action":"gripper","action_id":"10","command":"GRAB","sender":"ai_server"}
{"action":"gripper","action_id":"11","command":"DROP","sender":"ai_server"}
{"action":"gripper","action_id":"12","command":"SOFTHOLD","sender":"ai_server"}
// Direct angle (0–180)
{"action":"gripper","action_id":"13","position":120,"speed":10,"sender":"ai_server"}command—"GRAB"|"DROP"|"SOFTHOLD"(use instead of position for preset states)position— 0–180 degrees (use instead of command for direct control)speed— ms delay per degree (lower = faster)
servo — Servo Angles
Move a single named arm servo to an absolute angle.
{"action":"servo","action_id":"20","servoName":"ELBOW","position":135,"speed":10,"sender":"ai_server"}servoName—"ELBOW"|"WRIST"|"TWIST"position— 0–180 degreesspeed— ms delay per degree (lower = faster)
controlik — Inverse Kinematics
Move the arm to a target reach distance and optional height using stored hover calibrations. The firmware calculates the required servo angles — no manual math needed.
{"action":"controlik","action_id":"30","distance":85.0,"z_height":0.0,"sender":"ai_server"}distance— reach in mm from base center (required)z_height— height offset in mm (optional, default 0). Non-zero values requirehover_*_120calibrations.
This command requires hover calibrations to be saved first. Run calibrate with hover_over_min, hover_over_mid, and hover_over_max before using controlik.
perch — Perch Pose
Move the arm to the stored perch position — a safe resting pose you define during calibration.
{"action":"perch","action_id":"40","sender":"ai_server"}calibrate — Calibration
Save hover snapshots and perch angles to flash. Hover snapshots record the current servo angles at a given reach distance — controlik interpolates between them.
// Hover snapshots — position the arm first, then send with the matching distance
{"action":"calibrate","action_id":"50","calibration_type":"hover_over_min","distance":20,"sender":"ai_server"}
{"action":"calibrate","action_id":"51","calibration_type":"hover_over_mid","distance":60,"sender":"ai_server"}
{"action":"calibrate","action_id":"52","calibration_type":"hover_over_max","distance":110,"sender":"ai_server"}
// Hover snapshots at 120mm height (for controlik z_height support)
{"action":"calibrate","action_id":"53","calibration_type":"hover_min_120","distance":20,"sender":"ai_server"}
{"action":"calibrate","action_id":"54","calibration_type":"hover_mid_120","distance":60,"sender":"ai_server"}
{"action":"calibrate","action_id":"55","calibration_type":"hover_max_120","distance":110,"sender":"ai_server"}
// Perch angles
{"action":"calibrate","action_id":"56","calibration_type":"perch_elbow_angle","value":125,"sender":"ai_server"}
{"action":"calibrate","action_id":"57","calibration_type":"perch_wrist_angle","value":95,"sender":"ai_server"}
{"action":"calibrate","action_id":"58","calibration_type":"perch_twist_angle","value":90,"sender":"ai_server"}To read back all stored calibration values, send "action":"calibrationvalues" — the reply includes every saved key.
{"action":"calibrationvalues","action_id":"check","sender":"ai_server"}Camera & Vision
Capture a still photo or run on-device object or color detection using the ESP32-CAM.
// Take a photo
{"action":"photo","action_id":"60","sender":"ai_server"}
// Object detection — phrase is a list of things to look for
{"action":"detect_object","action_id":"61","phrase":["red cup","green bottle"],"sender":"ai_server"}
// Color detection
{"action":"detect_color","action_id":"62","sender":"ai_server"}phrase— array of strings describing what to detect (detect_object only)
Photo and detection replies stream image data back in the completed payload.
ota_update — OTA Firmware Update
Trigger a remote firmware update over MQTT. The ESP32 downloads the binary from a URL, verifies it, installs it, and reboots. No USB cable needed.
{
"action": "ota_update",
"action_id": "update_001",
"url": "https://github.com/.../releases/download/v1.0.0/firmware.bin",
"sha256": "abc123def456...",
"version": "v1.0.0",
"sender": "desk_buddy_web"
}url— direct link to compiled.bin(required)sha256— SHA256 hash of the binary for verification (recommended)token— GitHub personal access token for private repos (optional)version— version string persisted to flash for tracking (optional)
The desired version is saved to flash. If the board reboots mid-update it retries automatically on next boot until the correct version is running.
Heartbeat
When enabled, the firmware periodically publishes telemetry on esp32_5/HEARTBEAT — current servo and gripper angles plus a timestamp. Subscribe to this topic to keep your UI in sync with the physical arm state without sending a command.
// Example heartbeat payload
{
"sender": "firmware",
"type": "heartbeat",
"ELBOW": 90,
"WRIST": 90,
"TWIST": 90,
"GRIPPER": 180,
"ts": 1234567890
}