ESP32 MQTT Tutorial: Send Sensor Data to Home Assistant with Auto-Discovery (2026 Guide)

 

ESP32 MQTT Tutorial: Send Sensor Data to Home Assistant with Auto-Discovery (2026 Guide)



If you've already gotten your ESP32 talking to Wi-Fi, MQTT is the natural next step. It's the protocol that lets a $4 board push live sensor data into Home Assistant, trigger automations, and survive flaky networks without falling over — which is exactly why almost every serious smart home setup uses it under the hood.

Most tutorials on this stop at "publish a temperature reading and see it in a dashboard." That's a fine start, but it leaves out the parts that actually cause people problems: getting Home Assistant to detect the sensor automatically instead of hand-writing YAML, keeping the connection alive after a Wi-Fi hiccup, and knowing when a device has silently gone offline. This guide covers all three, using a plain ESP32, the Arduino IDE, and a real DHT22 sensor as the example.

Quick Answer

To connect an ESP32 to Home Assistant with MQTT: install the Mosquitto broker add-on in Home Assistant, flash the ESP32 with the PubSubClient library to publish sensor readings to an MQTT topic, and publish a matching MQTT Discovery message so Home Assistant creates the sensor entity automatically — no manual YAML required. The sections below walk through each part, plus the reconnect and Last Will handling that keeps the setup reliable long-term.

What MQTT Actually Does Here

MQTT (Message Queuing Telemetry Transport) is a lightweight publish/subscribe protocol built for devices with limited power and unreliable connections — which describes most ESP32 IoT projects. Instead of the ESP32 and Home Assistant talking to each other directly, both connect to a small always-on program called a broker (usually Mosquitto). The ESP32 publishes messages to a topic like home/livingroom/temperature, and anything subscribed to that topic — Home Assistant, a phone app, a script — receives it instantly.

The reason MQTT fits home automation better than a plain web server on the ESP32 is the broker itself. It handles reconnections, buffers messages briefly, and lets one device talk to many subscribers without the ESP32 needing to know who's listening. If you've used ESPHome, it's using MQTT (or a similar model) under the hood — this tutorial shows you the same mechanics without the abstraction layer, which matters if you want direct control over the code.

What You'll Need

  • An ESP32 development board (any variant — WROOM-32, DevKit V1, etc.)
  • A Home Assistant instance (Home Assistant OS, Supervised, or Container — any install that supports add-ons or has a broker reachable on your network)
  • A DHT22 (or BME280) temperature/humidity sensor
  • Arduino IDE with ESP32 board support installed
  • The PubSubClient library (by Nick O'Leary) and DHT sensor library (by Adafruit), both installable from the Arduino Library Manager
  • A breadboard, jumper wires, and a 4.7kΩ–10kΩ pull-up resistor for the DHT22 data line

If you haven't connected your ESP32 to Wi-Fi before, do that first — the Wi-Fi connection code below assumes you already know your SSID and password work with the board.

Step 1: Set Up an MQTT Broker in Home Assistant

Home Assistant needs a broker before it can talk MQTT to anything.

  1. Go to Settings → Add-ons → Add-on Store and search for Mosquitto broker.
  2. Install it, then turn on Start on boot and Watchdog in its configuration.
  3. Start the add-on.
  4. Go to Settings → People → Users and create a dedicated user for MQTT (don't reuse your admin login) — something like mqtt-esp32 with a strong password.
  5. Go to Settings → Devices & Services, and Home Assistant should auto-detect the Mosquitto broker and offer to set up the MQTT integration. If it doesn't appear automatically, add it manually with the broker address localhost (or your Home Assistant IP) on port 1883.

If you're running a broker outside Home Assistant (a standalone Mosquitto instance on a Raspberry Pi, for example), the steps are the same — you just point the MQTT integration at that machine's IP instead of localhost.

Step 2: Wire the DHT22 Sensor

The DHT22 needs three connections:

DHT22 PinESP32 Pin
VCC3.3V
DATAGPIO 4 (with pull-up resistor to 3.3V)
GNDGND

Most breakout boards for the DHT22 already include the pull-up resistor — check the back of the board before adding your own. If you're using a bare DHT22 sensor (not a module), the pull-up is required or your readings will be unreliable.

Step 3: The ESP32 Sketch

This is the part most tutorials rush. The code below does more than "connect and publish" — it includes a Last Will and Testament (LWT) message so Home Assistant knows immediately if the device drops offline, and a non-blocking reconnect loop so a temporary Wi-Fi or broker outage doesn't require a power cycle to recover from.

cpp
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

// --- Wi-Fi credentials ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// --- MQTT broker ---
const char* mqtt_server = "192.168.1.50";   // your Home Assistant IP
const int   mqtt_port = 1883;
const char* mqtt_user = "mqtt-esp32";
const char* mqtt_pass = "YOUR_MQTT_PASSWORD";
const char* client_id = "esp32-livingroom";

// --- Topics ---
const char* state_topic = "home/livingroom/sensor/state";
const char* availability_topic = "home/livingroom/sensor/status";

#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

WiFiClient espClient;
PubSubClient client(espClient);

unsigned long lastPublish = 0;
const unsigned long publishInterval = 30000; // 30 seconds

void connectWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWi-Fi connected: " + WiFi.localIP().toString());
}

void reconnectMQTT() {
  while (!client.connected()) {
    Serial.print("Connecting to MQTT...");
    // The LWT publishes "offline" automatically if the connection drops unexpectedly
    if (client.connect(client_id, mqtt_user, mqtt_pass,
                        availability_topic, 1, true, "offline")) {
      Serial.println("connected");
      client.publish(availability_topic, "online", true);
      publishDiscoveryConfig();
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" retrying in 5s");
      delay(5000);
    }
  }
}

void publishDiscoveryConfig() {
  // Tells Home Assistant how to create the entity automatically — no YAML needed
  String payload = "{";
  payload += "\"name\":\"Living Room Temperature\",";
  payload += "\"state_topic\":\"" + String(state_topic) + "\",";
  payload += "\"availability_topic\":\"" + String(availability_topic) + "\",";
  payload += "\"unit_of_measurement\":\"°C\",";
  payload += "\"value_template\":\"{{ value_json.temperature }}\",";
  payload += "\"unique_id\":\"esp32_livingroom_temp\",";
  payload += "\"device\":{\"identifiers\":[\"esp32_livingroom\"],\"name\":\"ESP32 Living Room\"}";
  payload += "}";
  client.publish("homeassistant/sensor/esp32_livingroom_temp/config", payload.c_str(), true);
}

void setup() {
  Serial.begin(115200);
  dht.begin();
  connectWiFi();
  client.setServer(mqtt_server, mqtt_port);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWiFi();
  if (!client.connected()) reconnectMQTT();
  client.loop();

  unsigned long now = millis();
  if (now - lastPublish > publishInterval) {
    lastPublish = now;
    float temp = dht.readTemperature();
    float hum = dht.readHumidity();

    if (isnan(temp) || isnan(hum)) {
      Serial.println("Sensor read failed — skipping this cycle");
      return;
    }

    String payload = "{\"temperature\":" + String(temp, 1) +
                      ",\"humidity\":" + String(hum, 1) + "}";
    client.publish(state_topic, payload.c_str());
  }
}

Upload this, open the Serial Monitor at 115200 baud, and you should see the Wi-Fi and MQTT connection messages, followed by periodic publishes. Within a few seconds, check Settings → Devices & Services → MQTT in Home Assistant — a new device called "ESP32 Living Room" should appear with a Temperature entity, created without you writing a single line of YAML.

Why the Discovery Config Matters

This is the step most beginner tutorials skip, and it's the one that saves the most time. Without it, you'd need to manually define the sensor in configuration.yaml:

yaml
mqtt:
  sensor:
    - name: "Living Room Temperature"
      state_topic: "home/livingroom/sensor/state"
      value_template: "{{ value_json.temperature }}"
      unit_of_measurement: "°C"

That works, but it means editing YAML and restarting Home Assistant every time you add a sensor. The discovery payload published in publishDiscoveryConfig() does the same job automatically, the moment the device connects — which is also how ESPHome and most commercial smart devices integrate with Home Assistant behind the scenes.

Reliability: What Happens When Wi-Fi Drops

If you've read the deep sleep power draw troubleshooting guide on this site, you already know ESP32 projects that run unattended eventually hit edge cases nobody planned for. MQTT is no exception. Two things are worth doing before you consider this project "done":

  • The reconnect loop above is non-blocking on the publish interval but blocking on the MQTT connect itself. For most home setups this is fine — a broker on your own network reconnects in under a second. If you're on a flaky connection, consider adding a timeout and a watchdog reset (the same esp_task_wdt approach covered in this site's watchdog timer guide) so the device reboots itself if it's stuck reconnecting for more than a couple of minutes.
  • The availability topic is doing real work, not just cosmetic status. Because it's set as the LWT with retain=true, Home Assistant will mark the device "unavailable" within its keep-alive window if the ESP32 loses power or crashes — not just if it disconnects cleanly. Build automations against this (e.g., notify if a sensor's been offline for 10+ minutes) rather than assuming "no new data" means the same thing as "device is down."

Common Problems and Mistakes

MQTT connects but no entity shows up in Home Assistant. Almost always a discovery topic mismatch. The topic must follow homeassistant/<component>/<node_id>/config exactly, and the JSON payload must be valid — a single unescaped quote will silently fail. Test the payload with an MQTT client like MQTT Explorer before assuming the ESP32 code is wrong.

Readings freeze or the board stops publishing after a few hours. This is usually memory fragmentation from heavy use of the Arduino String class inside loop(). The DHT22 read frequency in the example (30 seconds) keeps this manageable, but if you're publishing every second or building larger JSON payloads, switch to char buffers with snprintf() or use ArduinoJson instead of string concatenation.

Sensor returns NaN intermittently. DHT22 sensors need at least 2 seconds between reads and are sensitive to long or unshielded wiring. If NaN reads persist, shorten the data line or add a 100nF capacitor across VCC and GND close to the sensor.

Authentication keeps failing even with correct credentials. Check that the MQTT username was created as a Home Assistant user, not just added to mosquitto.conf directly — recent Mosquitto add-on versions expect broker auth to route through Home Assistant's own user system by default.

ESP32 + MQTT vs. ESPHome vs. Node-RED

ESP32 + Arduino + MQTT (this guide)ESPHomeNode-RED + MQTT
Setup effortModerate — you write the C++Low — YAML config, auto-generates firmwareModerate — visual flows, still needs a broker
Code controlFull control over every lineLimited to what ESPHome exposesFull control over logic, not firmware
Best forCustom sensors, learning MQTT, non-standard hardwareFast standard integrations (lights, switches, common sensors)Complex automation logic across many devices
Home Assistant integrationManual discovery config (shown above) or auto via ESPHome add-onNative, automaticVia MQTT nodes, manual entity setup

If your goal is just to get a standard sensor into Home Assistant fast, ESPHome will get you there quicker. This guide is for when you need custom logic on the device itself, want to understand what's actually happening over the wire, or are working with a sensor ESPHome doesn't support out of the box.

FAQs

Does the ESP32 need to stay connected to my computer after uploading the code? No. Once flashed, power the ESP32 from any 5V USB supply — it runs independently and reconnects to Wi-Fi and MQTT on its own after a power cycle.

Can I use this with a BME280 instead of a DHT22? Yes — swap the DHT library calls for the Adafruit_BME280 library's readTemperature(), readHumidity(), and readPressure() calls, and add a pressure field to the JSON payload and a second discovery config.

Is MQTT secure over my local network? By default this setup uses unencrypted MQTT on port 1883, which is acceptable for most home networks but not for anything exposed to the internet. For TLS, switch to port 8883, load a CA certificate onto the ESP32 with WiFiClientSecure, and reference Mosquitto's TLS documentation for broker-side certificate setup.

Why use MQTT instead of just having the ESP32 host a web server? A web server means Home Assistant has to poll the ESP32 repeatedly to check for updates, and the ESP32 has to stay responsive to incoming requests. MQTT flips that — the ESP32 pushes data only when it changes, and the broker handles delivery, which is lighter on both the ESP32 and your network.

Do I need Home Assistant OS specifically, or does this work with Home Assistant Container? Any Home Assistant installation works, as long as an MQTT broker is reachable from it. Home Assistant Container and Core users typically run Mosquitto as a separate Docker container rather than an add-on, but the MQTT integration setup is identical.

What happens if two ESP32 devices use the same client ID? The broker will disconnect one of them — MQTT client IDs must be unique per device. Always give each board its own client_id and unique topic prefix, as shown in the code above.

Final Takeaway

The publish-a-reading part of ESP32-to-Home-Assistant MQTT is genuinely simple — most of it is fifteen lines of PubSubClient code. The part that separates a demo from something you'd actually trust running in your house is the discovery config, the Last Will topic, and a reconnect strategy that doesn't need a manual reboot. Get those three right once, and you can copy this same pattern to every sensor you add afterward.

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.