ESPHome talks MQTT through one block of YAML. Add mqtt: with a broker address and every sensor, switch and light on the device publishes its state and accepts commands on predictable topics, with no C++ to write. It works with Home Assistant through MQTT discovery, and just as well without Home Assistant at all, feeding Node-RED, Grafana or your own backend from any broker.
This guide covers a complete configuration against an external broker, the topic layout ESPHome generates, discovery, birth and will messages, TLS, custom subscriptions, and how to verify every part from the browser.
A minimal working configuration
esphome:
name: livingroom
esp32:
board: esp32dev
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
logger:
ota:
- platform: esphome
password: !secret ota_password
mqtt:
broker: broker.connectmq.com
port: 1883
username: !secret mqtt_user
password: !secret mqtt_password
client_id: esphome-livingroom
topic_prefix: testmqtt/esphome/livingroom
discovery: false
sensor:
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 30s
- platform: uptime
name: "Uptime"
switch:
- platform: gpio
pin: GPIO2
name: "Relay"
id: relaywifi_ssid: "your-ssid"
wifi_password: "your-wifi-password"
ota_password: "change-me"
mqtt_user: "username"
mqtt_password: "password"Flash it with esphome run livingroom.yaml. In the log you should see Connecting to MQTT... followed by MQTT Connected!.
Three settings are worth setting explicitly instead of relying on defaults:
topic_prefixdefaults to the node name (livingroom). On a shared broker that collides with everyone else's living room. A unique prefix such astestmqtt/esphome/livingroomkeeps your topics yours.client_idmust be unique per device. Two devices with the same ID disconnect each other in a loop. ESPHome generates one from the name and MAC if you leave it out, which is fine; just never copy a fixed one between YAML files.discovery: falsewhile testing on a public or shared broker, so you do not publish Home Assistant config messages to a namespace other people read. Turn it on when you use your own broker.
The topics ESPHome generates
Each entity gets a state topic, and controllable entities a command topic. The object ID is the entity name in lowercase with spaces replaced by underscores:
| Entity | Topic | Direction |
|---|---|---|
| Availability | testmqtt/esphome/livingroom/status | device → broker (online / offline) |
| WiFi Signal sensor | testmqtt/esphome/livingroom/sensor/wifi_signal/state | device → broker |
| Uptime sensor | testmqtt/esphome/livingroom/sensor/uptime/state | device → broker |
| Relay switch state | testmqtt/esphome/livingroom/switch/relay/state | device → broker (ON / OFF) |
| Relay switch command | testmqtt/esphome/livingroom/switch/relay/command | broker → device (ON / OFF / TOGGLE) |
| Logs | testmqtt/esphome/livingroom/debug | device → broker |
Override any of them per entity with state_topic: and command_topic: when you need to fit an existing topic design. The log_topic: option controls the debug topic; set it to null to stop publishing logs over MQTT, which saves bandwidth on busy devices.
Birth, will and shutdown messages
ESPHome sends a birth message after connecting and registers a Last Will with the broker. By default both go to <topic_prefix>/status, retained, with the payloads online and offline. That is what Home Assistant uses for entity availability. You can customise them:
mqtt:
# ...
birth_message:
topic: testmqtt/esphome/livingroom/availability
payload: "connected"
retain: true
will_message:
topic: testmqtt/esphome/livingroom/availability
payload: "lost"
retain: true
shutdown_message:
topic: testmqtt/esphome/livingroom/availability
payload: "shutdown"
retain: true
keepalive: 30sThe will is published by the broker, not the device, roughly 1.5 × keepalive after the device goes silent. The shutdown message is sent by the device itself before a planned reboot or OTA update, so you can tell "rebooting on purpose" from "lost power". The timing is explained in MQTT Last Will, and retained flags in retained messages.
Home Assistant discovery
With discovery: true (the default), ESPHome publishes one retained config message per entity under <discovery_prefix>/<component>/<node>/<object_id>/config. Home Assistant's MQTT integration subscribes to that prefix and creates the entities automatically.
mqtt:
broker: broker.connectmq.com
port: 1883
username: !secret mqtt_user
password: !secret mqtt_password
discovery: true
discovery_prefix: homeassistant
discovery_retain: trueThings to know:
- The prefix must match. Home Assistant listens on
homeassistantunless you changed it in the MQTT integration options. If you change one, change both. - Discovery messages are retained. If you rename or delete an entity, the old config stays on the broker. Clear it by publishing an empty retained message to the old config topic.
- One broker for both. Home Assistant must connect to the same broker as the device. For the full Home Assistant side, read Home Assistant MQTT.
Subscribing and publishing from YAML
ESPHome can react to any topic, not just its own command topics, and publish arbitrary messages:
mqtt:
# ...
on_message:
- topic: testmqtt/esphome/livingroom/cmd/toggle
qos: 1
then:
- switch.toggle: relay
on_json_message:
topic: testmqtt/esphome/livingroom/cmd/json
then:
- if:
condition:
lambda: 'return x["relay"] == "on";'
then:
- switch.turn_on: relay
else:
- switch.turn_off: relay
interval:
- interval: 60s
then:
- mqtt.publish:
topic: testmqtt/esphome/livingroom/heartbeat
payload: "alive"
qos: 0
retain: falseon_json_message parses the payload with ArduinoJson and exposes it as x. Keep payloads small: very large messages may exceed the client's buffer on memory-constrained boards.
Using TLS with an external broker
On ESP32, add the broker's CA certificate and switch to port 8883:
mqtt:
broker: broker.connectmq.com
port: 8883
username: !secret mqtt_user
password: !secret mqtt_password
certificate_authority: |
-----BEGIN CERTIFICATE-----
... root CA of your broker's certificate chain ...
-----END CERTIFICATE-----Use the root CA of the chain the broker presents, not the server certificate itself. The ESP-IDF framework is the recommended choice for MQTT over TLS on ESP32. ESP8266 support is much more limited (fingerprints only on the Arduino framework), so for TLS-critical devices choose an ESP32, or keep ESP8266 devices on a trusted local network. Our ESP32 TLS guide explains how to extract the right certificate with openssl s_client.
Test every part from the browser
- Open the online MQTT client, connect to
broker.connectmq.comon port 8084 (path/mqtt) and subscribe totestmqtt/esphome/livingroom/#. - You should see
status = online(retained) and thewifi_signalanduptimestates every 30 seconds. - Publish
ONtotestmqtt/esphome/livingroom/switch/relay/command. The relay switches andswitch/relay/statereportsON. - Publish anything to
testmqtt/esphome/livingroom/cmd/toggleto exerciseon_message. - Power the board off. After about 1.5 × keepalive the will sets
statustooffline. Reboot it from the dashboard instead and you will see the shutdown message first.
Watch your ESPHome node in the online client
If an entity does not appear on the expected topic, paste the topic and your filter into the topic matcher to check for a typo in the prefix.
Troubleshooting quick reference
| Log message | Likely cause |
|---|---|
MQTT Disconnected: TCP disconnected. | Wrong host or port, firewall, TLS port used without certificate_authority |
MQTT Disconnected: Not Authorized. | Wrong username or password, or ACL denies the client |
MQTT Disconnected: Identifier Rejected. | Client ID rejected or duplicated |
| Device reboots after 15 minutes | reboot_timeout reached because the broker is unreachable |
For a step-by-step diagnosis, read ESPHome MQTT not connecting.
Configuring ESPHome for MQTT is mostly about naming: a unique topic prefix and client ID, discovery switched on only where Home Assistant lives, and availability topics you can trust. Get those right, verify state, command and will from the online client, and the same YAML moves from your test broker to production by changing a host name and a secret.
Frequently asked questions
Can I use ESPHome with MQTT and the native API at the same time?
Yes. The api: and mqtt: components are independent. Many setups keep the native API for Home Assistant and add MQTT so other systems (Node-RED, a data logger, a second site) can read the same sensors.
Why does my ESPHome device reboot every 15 minutes?
The mqtt: component has reboot_timeout, 15 minutes by default: if the broker cannot be reached for that long, the device restarts. Fix the connection, or set reboot_timeout: 0s to disable it on devices that must keep running offline.
What topics does ESPHome publish to?
By default <topic_prefix>/<component_type>/<object_id>/state, where topic_prefix is the node name and object_id is the entity name in lowercase with underscores. Controllable entities listen on the same path ending in /command.

