// mqtt

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 degrees
  • speed — 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 require hover_*_120 calibrations.

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.

// over-the-air

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.

📡 No USB cable required

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
}