Skip to content

Tutorial

How to Use MQTT with ESP32 and Home Assistant — Complete Beginner Guide

MQTT is the backbone of most smart home systems. This guide shows you how to set up an MQTT broker, connect ESP32, and integrate with Home Assistant from scratch.

  • 5 min read
  • Updated October 31, 2026
How to Use MQTT with ESP32 and Home Assistant — Complete Beginner Guide

If you've blinked your LEDs, read data from a temperature sensor, and set up a simple web server on your microcontroller — you're ready for the next step. This guide takes you from basics to a fully networked, MQTT-based smart home system where dozens of sensors and relays communicate in real-time.

We'll connect the ESP32 to Home Assistant using MQTT: a password-protected Mosquitto broker, an ESP32 sketch that publishes a sensor and obeys commands, a test with the command-line tools, and two ways to make the entities appear in Home Assistant, by hand in YAML or automatically with MQTT Discovery.

Chapter 1: The Modern Smart Home Architecture

Before writing code, you need to understand how a modern, localized smart home is structured. A robust system doesn't rely on third-party cloud servers. It runs entirely on your local network.

The standard stack has three layers:

The Central Brain — The Server Typically a Raspberry Pi running a full Linux OS. It's always on, low-power, and hosts your smart home software.

The Ecosystem — The Software Home Assistant, Node-RED, and InfluxDB run here. These handle dashboards, automation logic (turn on a light when motion is detected), and historical data storage.

The Edge Nodes — The Hardware This is where your ESP32 and ESP8266 circuits live. They read sensors, toggle relays, and beam data back to the central brain over WiFi.

Local smart-home architecture: a Raspberry Pi running the broker and Home Assistant, with ESP32 and ESP8266 nodes on the same Wi-Fi

But how do these edge nodes talk to the brain? They need a protocol. HTTP works, but it's bulky and resource-hungry. This is where MQTT changes everything.


Chapter 2: Demystifying MQTT

MQTT stands for Message Queuing Telemetry Transport. It's a lightweight publish-subscribe protocol built for low-bandwidth, high-latency networks — the industry standard for IoT.

The Publish / Subscribe Model

Unlike HTTP (ask → receive), MQTT uses a Pub/Sub model. Think of it as a post office:

RoleDescriptionExample
BrokerThe post office. Routes all messages.Mosquitto on Raspberry Pi
PublisherDrops off a message to a topic.ESP32 sending temperature
SubscriberChecks their P.O. box for messages.Home Assistant reading sensor data
TopicThe address on the envelope.home/livingroom/temperature

The ESP32 drops off its message and immediately returns to work — saving processing time and power. Combined with deep sleep techniques, an ESP32 can run on batteries for months rather than days.

MQTT publish/subscribe: the ESP32 publishes to a topic on the broker, and Home Assistant subscribes to it

Chapter 3: Setting Up the Broker — Raspberry Pi and Mosquitto

You need a broker before anything else. We'll install Mosquitto on a Raspberry Pi.

Installing Mosquitto

Open your Raspberry Pi terminal and run:

sudo apt update
sudo apt install mosquitto mosquitto-clients

Enable it to auto-start on reboot:

sudo systemctl enable mosquitto

Securing the Broker — Create a User

Never leave your broker open to any device on your network. Add authentication:

sudo mosquitto_passwd -c /etc/mosquitto/passwd mqtt_user

Enter a strong password when prompted. Then edit the Mosquitto config:

sudo nano /etc/mosquitto/mosquitto.conf

Add these three lines at the bottom:

listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd

The listener 1883 line matters. Since version 2.0, Mosquitto only accepts connections from the Pi itself unless you declare a listener, so without it the ESP32 gets rc=-2 forever even though everything else is right.

Save, exit, and restart:

sudo systemctl restart mosquitto

Your broker is now password-protected and listening on port 1883. Check it from the Pi with two terminals:

# Terminal 1: watch everything under home/
mosquitto_sub -h localhost -u mqtt_user -P 'YOUR_SECURE_PASSWORD' -t 'home/#' -v

# Terminal 2: send a test message
mosquitto_pub -h localhost -u mqtt_user -P 'YOUR_SECURE_PASSWORD' -t home/test -m hello

If home/test hello appears in the first terminal, the broker works. Keep that subscriber open: it shows every message the ESP32 sends in the next chapter. For a fuller broker setup, see installing Mosquitto on a Raspberry Pi 5.

Running Home Assistant OS? Skip the manual install and use the official Mosquitto broker add-on from the Add-on Store. It creates the broker, and you add a user for the ESP32 under its configuration.


Chapter 4: Programming the ESP32 Node

Time to write the firmware. This ESP32 will act as a dual-purpose node — it publishes temperature data and subscribes to a command topic so Home Assistant can toggle an onboard LED (representing a relay).

Install the Library

In Arduino IDE: Sketch → Include Library → Manage Libraries → search PubSubClient by Nick O'Leary → Install.

C++

Understanding the Code

Publishing: The loop() uses a non-blocking millis() timer to publish simulated sensor data to home/livingroom/temperature every 10 seconds. Replace random() with real DHT22 or MQ135 readings when you're ready.

Subscribing: reconnect() subscribes to home/livingroom/light/set. The callback() function listens on that topic — when it receives "ON", it drives GPIO2 HIGH to turn the LED on, then publishes ON back to home/livingroom/light/status with the retained flag, so Home Assistant always knows the real state, even after it restarts.

Client ID: every device needs its own ID in client.connect(). Two ESP32s sharing ESP32_LivingRoom knock each other off the broker in an endless loop.

Chapter 5: Integrating with Home Assistant

Broker is running. ESP32 is publishing. Now give this data a proper UI inside Home Assistant.

Step 1: Add the MQTT Integration

Home Assistant has to be told where the broker is before any MQTT entity can work. Go to Settings → Devices & Services → Add Integration → MQTT, enter the broker's IP address, port 1883, and the mqtt_user username and password. (If you use the Mosquitto add-on, Home Assistant usually discovers it and only asks you to confirm.)

Step 2: Define the Entities in configuration.yaml

Add the following block to your configuration.yaml file:

YAML

Check it under Developer Tools → YAML → Check configuration, then restart Home Assistant. Later edits to these MQTT entities only need Developer Tools → YAML → Manually configured MQTT entities to reload.

What Happens Next

After HA reboots, navigate to your Lovelace dashboard and add an Entity card. You'll find two new entities:

  • sensor.living_room_temperature — updates every 10 seconds from the ESP32
  • switch.living_room_lamp — toggling it publishes ON or OFF to the broker, which routes it to the ESP32, which physically toggles the LED

You've just built a complete, end-to-end, local IoT network.

The Easier Way: MQTT Discovery

Writing YAML for every sensor gets tedious. With MQTT Discovery, the ESP32 announces its own entities: it publishes a retained JSON description to a topic under homeassistant/, and Home Assistant creates the entity automatically. Add this to the sketch and call it right after a successful client.connect():

void publishDiscovery() {
  const char* config =
    "{\"name\":\"Living Room Temperature\","
    "\"state_topic\":\"home/livingroom/temperature\","
    "\"unit_of_measurement\":\"°C\","
    "\"device_class\":\"temperature\","
    "\"state_class\":\"measurement\","
    "\"unique_id\":\"esp32_livingroom_temp\"}";
  client.publish("homeassistant/sensor/esp32_livingroom_temp/config", config, true);
}

One trap: PubSubClient's default packet buffer is 256 bytes, and discovery messages easily exceed it, in which case publish() silently returns false. Call client.setBufferSize(512); in setup() before connecting. The unique_id lets you rename the entity and change its icon from the Home Assistant UI later.

Show When the Node Goes Offline

If the ESP32 loses power, Home Assistant keeps showing the last temperature as if nothing happened. MQTT's Last Will and Testament fixes that: when connecting, the ESP32 asks the broker to publish offline on its behalf if it disappears.

// connect(clientId, user, pass, willTopic, willQos, willRetain, willMessage)
if (client.connect("ESP32_LivingRoom", mqtt_user, mqtt_password,
                   "home/livingroom/status", 0, true, "offline")) {
  client.publish("home/livingroom/status", "online", true);
  client.subscribe(led_command_topic);
}

Then add availability_topic: "home/livingroom/status" to each entity (or "availability_topic" to the discovery JSON). Home Assistant shows the entities as Unavailable within about a minute of the node dropping off. The retained messages and LWT guide explains the details.


Chapter 6: Expanding Your Ecosystem

MQTT scales effortlessly. To add a garage sensor, copy the same code, change the topic to home/garage/temperature, and power it on. The broker handles all routing automatically.

Where to go from here:

  • Long-term storage: Route MQTT topics into an InfluxDB database for historical graphs
  • Custom dashboards: Pipe MQTT data into Node-RED running alongside Home Assistant
  • Vision projects: An ESP32-CAM can publish parking/space1/status: occupied via MQTT after processing a camera frame locally
  • Battery-powered nodes: Use ESP32 deep sleep — wake, publish, sleep — and run on batteries for months

Common Issues & Fixes

ErrorCauseFix
rc=-2 in Serial MonitorBroker IP wrong, broker not running, or Mosquitto 2.x with no listener 1883 lineCheck the Pi's IP, sudo systemctl status mosquitto, and the listener line
rc=4 or rc=5Wrong username or password, or the user isn't in the password fileRecreate the user with mosquitto_passwd and restart Mosquitto
Two nodes keep disconnecting each otherBoth use the same client IDGive every device a unique ID in client.connect()
Discovery entity never appearsPayload bigger than PubSubClient's 256-byte bufferclient.setBufferSize(512) before connecting
rc=-4 timeoutFirewall blocking port 1883Allow inbound port 1883 on your Pi's firewall
ESP32 keeps reconnectingdelay() blocking client.loop()Use millis() for timing — never delay() in main loop
HA shows no dataMQTT integration not added, or a YAML indentation errorAdd the MQTT integration; run Developer Tools → YAML → Check configuration
Switch in HA doesn't reflect LED stateESP32 not publishing state backAdd client.publish("home/livingroom/light/status", "ON") inside the callback

Conclusion

Transitioning from simple Arduino scripts to a fully networked, MQTT-based smart home is the defining moment in any maker's journey. You now have:

  • A password-protected Mosquitto broker on a Raspberry Pi
  • An ESP32 that both publishes sensor data and receives commands
  • Home Assistant entities, defined in YAML or announced automatically with Discovery
  • Offline detection with Last Will

The same pattern scales to any number of nodes. Add temperature sensors, motion detectors, soil moisture sensors, door contacts — each one just needs a unique topic and the same core code structure.

Next reads:

Continue learning

Related articles

All articles

Signal In

Built from what makers search for

The lookups piling up in SolderHub's search bar every day shape what we publish next — new component pages, board guides, and firmware notes, sent out as they go live.

By subscribing you agree to our Privacy Policy. Unsubscribe anytime.