# MQTT robot telemetry with ESP32: topics, QoS and code

> Design MQTT topics for a robot, choose the right QoS, publish telemetry from an ESP32 and avoid the buffering and Wi-Fi traps that break it.

Canonical: https://roboticsengineering.in/mqtt-esp32-robot-telemetry/ | Published: 2026-10-27 | Updated: 2026-10-28 | Author: Jignesh Panchal

**MQTT is the cheapest way to get robot telemetry off the machine and onto a dashboard.** One broker, a few topics, and an ESP32 publishing battery, speed and fault data over Wi-Fi.

This guide covers the topic design, the QoS choices, a working ESP32 publisher, a subscriber that logs to a database, and the Wi-Fi and memory traps that break telemetry in the field.

## Why MQTT suits a robot

- Small messages over a small bandwidth link.
- Publish and subscribe, so many dashboards can read one robot.
- A broker decouples the robot from whoever is watching it.
- Last Will and Testament tells you when a robot drops off.
- Retained messages mean a new subscriber sees the last known state immediately.
- Runs comfortably on an ESP32 with a few KB of RAM per connection.

## Design the topic tree first

```
robots/<robot_id>/status
robots/<robot_id>/battery
robots/<robot_id>/velocity
robots/<robot_id>/fault
robots/<robot_id>/cmd/velocity
robots/<robot_id>/cmd/estop
```

| Topic | Direction | QoS | Retained |
| --- | --- | --- | --- |
| status | Robot to broker | 1 | Yes |
| battery | Robot to broker | 0 | Yes |
| velocity | Robot to broker | 0 | No |
| fault | Robot to broker | 1 | Yes |
| cmd/velocity | Operator to robot | 1 | No |
| cmd/estop | Operator to robot | 1 | No |

Keep commands and telemetry in separate subtrees. It makes broker access control simple: the dashboard can read everything under the robot, but write only under `cmd/`.

## QoS without overthinking it

| QoS | Guarantee | Use for |
| --- | --- | --- |
| 0 | At most once, fire and forget | Fast telemetry, velocity, battery |
| 1 | At least once, may duplicate | Commands, faults, status |
| 2 | Exactly once | Billing or records, rarely a robot |

QoS 1 can deliver duplicates, so make the receiving handler idempotent. A command with a sequence number that ignores repeats is safer than one that executes twice.

## A working ESP32 publisher

The PubSubClient library is the common choice. Connect, publish on a timer, and use a Last Will so the broker marks the robot offline when the link dies.

```
#include <WiFi.h>
#include <PubSubClient.h>

WiFiClient net;
PubSubClient mqtt(net);
const char* robot = "robot-01";

void connect() {
  while (!mqtt.connected()) {
    if (mqtt.connect(robot, nullptr, nullptr,
        "robots/robot-01/status", 1, true, "offline")) {
      mqtt.publish("robots/robot-01/status", "online", true);
    } else {
      delay(2000);
    }
  }
}

void setup() {
  WiFi.begin("SSID", "PASSWORD");
  mqtt.setServer("192.168.1.50", 1883);
  mqtt.setBufferSize(512);
}

void loop() {
  if (!mqtt.connected()) connect();
  mqtt.loop();
  static unsigned long last = 0;
  if (millis() - last > 2000) {
    last = millis();
    char buf[96];
    snprintf(buf, sizeof(buf),
      "{\"v\":%.1f,\"b\":%d}", 0.42, 87);
    mqtt.publish("robots/robot-01/telemetry", buf);
  }
}
```

Note the explicit buffer size. The default 256-byte packet limit is the most common reason a JSON payload silently fails to publish.

## The broker

Mosquitto on a small VPS or a Raspberry Pi handles a workshop full of robots. Open 1883 only inside your network, and 8883 with TLS if anything crosses the internet.

```
sudo apt install mosquitto mosquitto-clients
mosquitto_sub -h localhost -t 'robots/#' -v
```

Create a username and password per robot, and give the dashboard a separate read-only account. An open broker on a factory network is a remote-control hole.

## From broker to dashboard

- **Node-RED:** fastest path to charts and alerts, runs on the same Pi.
- **InfluxDB plus Grafana:** better for history and long-term trends.
- **Home Assistant:** useful for small service robots and lab setups.
- **A Python subscriber:** writes to Postgres when you need custom logic.

A simple Python subscriber that stores every message with a timestamp is often all a first deployment needs. Build the dashboard after the data is reliable.

## Field problems and what causes them

| Symptom | Likely cause |
| --- | --- |
| Messages stop after a few minutes | Keepalive too short for Wi-Fi latency |
| Publish returns false | Payload larger than the buffer |
| Reconnect loop | Duplicate client ID from a cloned device |
| Dashboard shows stale data | Retained message never updated, or LWT not set |
| Commands arrive twice | QoS 1 duplicate without an idempotency check |
| Robot busy or slow | Publishing too often from the main control loop |

## What not to put on MQTT

- Safety stop commands over Wi-Fi. Hardwire the E-stop.
- High-rate control loops. Use a local bus or micro-ROS.
- Large images or point clouds. Send a link or a summary.
- Credentials in plain text on port 1883 across the internet.
- Anything you cannot afford to lose without an acknowledgement path.

Related: [ESP32 pinout for robotics](https://roboticsengineering.in/esp32-pinout-for-robotics/), [micro-ROS tutorial](https://roboticsengineering.in/micro-ros-tutorial/), [Raspberry Pi 5 robot guide](https://roboticsengineering.in/raspberry-pi-5-robot-guide/), [robot battery guide](https://roboticsengineering.in/robot-battery-guide/), [mobile robot charging and docking](https://roboticsengineering.in/mobile-robot-charging-docking/).

**Is MQTT good for robot telemetry?**

Yes for low-rate telemetry such as battery, status, faults and position. It is not suitable for safety stops or high-rate control loops, which should stay on hardware or a local bus.

**Which QoS should I use for robot data?**

QoS 0 for fast telemetry like velocity and battery, QoS 1 for commands and faults. QoS 2 is rarely worth the overhead on a robot.

**Why does my ESP32 stop publishing to MQTT?**

The usual causes are a payload bigger than the 256-byte default buffer, a keepalive too short for Wi-Fi latency, or a duplicate client ID after cloning a device.

**Can I send emergency stop over MQTT?**

No. A safety stop must be hardwired and independent of Wi-Fi, the broker and the network. MQTT can notify people that a stop happened, not perform it.

**Do I need TLS for MQTT on a robot?**

If traffic crosses the internet, yes, use port 8883 with certificates. Inside an isolated workshop network, 1883 with per-device credentials is common but still needs access control.

For driving rather than telemetry, [a phone app over WebSocket](https://roboticsengineering.in/android-app-control-robot-esp32/) is simpler.

Streaming attitude data is easier once the [MPU6050 angle estimation](https://roboticsengineering.in/mpu6050-balancing-robot-arduino/) loop is stable and calibrated.

Telemetry should never block the motor loop, which is why the [task and queue design](https://roboticsengineering.in/esp32-freertos-robot-tasks/) separates comms from control.

For telemetry rather than driving, the same network foundation is covered in [this control guide](https://roboticsengineering.in/wifi-controlled-robot-esp32/).

MQTT is one of the transport options in a factory deployment, described alongside OPC UA in the [IIoT monitoring guide](https://roboticsengineering.in/iiot-machine-monitoring-india/).

## Sources and further reading

- [MQTT.org](https://mqtt.org/) — protocol specification and overview.
- [Eclipse Mosquitto](https://mosquitto.org/) — broker documentation and configuration.
- [PubSubClient](https://pubsubclient.knolleary.net/) — Arduino client library reference.
- [Node-RED](https://nodered.org/) — dashboards and flows for MQTT data.
