AdvancedESP32ESP32MQTTHome Assistant

ESP32 Home Automation Node with MQTT & Home Assistant

Turn an ESP32 WROOM into a smart home node that publishes sensor data and receives relay commands via MQTT. Supports Home Assistant auto-discovery, OTA firmware updates, and reconnect logic.

Circuit Hub35 min read2 views
Source code

Overview

This project converts an ESP32 WROOM-32 into a multi-function home automation node:

  • Reads temperature & humidity from a DHT22 and publishes every 30 seconds
  • Controls a 5V relay (light/fan) via MQTT command topic
  • Supports Home Assistant MQTT Discovery — the device appears automatically in HA with correct entity names, icons, and device class
  • Includes Arduino OTA so you can push firmware updates over WiFi without plugging in a cable
  • Robust reconnect loop with exponential back-off so it recovers from broker restarts

⚠️ Warning

Use a **mosquitto broker running on your local network** (Raspberry Pi or a Home Assistant add-on) rather than a public broker for home automation. Public brokers are unreliable and expose your home data.

Components required

ESP32 WROOM-32 Dev Board
×1Buy
DHT22 Temperature & Humidity Sensor
×1Buy
10 kΩ Resistor (DHT22 pull-up)
×1
5V Single-Channel Relay Module
×1Buy
Breadboard + Jumper Wires
×1
C++
// ── ESP32 MQTT Home Automation Node ──────────────────────────────────────────
// Libraries: PubSubClient, DHT sensor library (Adafruit), ArduinoOTA
// ─────────────────────────────────────────────────────────────────────────────
#include <WiFi.h>
#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>

// ── Config ────────────────────────────────────────────────────────────────────
#define WIFI_SSID     "YOUR_WIFI"
#define WIFI_PASS     "YOUR_PASS"
#define MQTT_BROKER   "192.168.1.100"    // Your local broker IP
#define MQTT_PORT     1883
#define MQTT_USER     "mqtt_user"         // Leave "" if no auth
#define MQTT_PASS     "mqtt_pass"
#define DEVICE_ID     "esp32_node_01"
#define DHT_PIN       4
#define RELAY_PIN     26

#define TOPIC_TEMP    "homeassistant/sensor/"   DEVICE_ID "/temperature/state"
#define TOPIC_HUM     "homeassistant/sensor/"   DEVICE_ID "/humidity/state"
#define TOPIC_RELAY   "homeassistant/switch/"   DEVICE_ID "/relay/command"
#define TOPIC_RELAY_S "homeassistant/switch/"   DEVICE_ID "/relay/state"

// ── Home Assistant Discovery Topics ──────────────────────────────────────────
#define DISCO_TEMP    "homeassistant/sensor/"   DEVICE_ID "/temperature/config"
#define DISCO_HUM     "homeassistant/sensor/"   DEVICE_ID "/humidity/config"
#define DISCO_RELAY   "homeassistant/switch/"   DEVICE_ID "/relay/config"

DHT      dht(DHT_PIN, DHT22);
WiFiClient wifi;
PubSubClient mqtt(wifi);

bool relayOn = false;
unsigned long lastPublish = 0;

void publishDiscovery() {
  StaticJsonDocument<512> doc;

  // Temperature sensor
  doc.clear();
  doc["name"]           = "ESP32 Temperature";
  doc["unique_id"]      = DEVICE_ID "_temp";
  doc["state_topic"]    = TOPIC_TEMP;
  doc["unit_of_meas"]   = "°C";
  doc["device_class"]   = "temperature";
  doc["value_template"] = "{{ value }}";
  doc["device"]["ids"]  = DEVICE_ID;
  doc["device"]["name"] = "ESP32 Node 01";
  doc["device"]["model"]= "ESP32 WROOM-32";
  char buf[512];
  serializeJson(doc, buf);
  mqtt.publish(DISCO_TEMP, buf, true);  // retained

  // Relay switch
  doc.clear();
  doc["name"]             = "ESP32 Relay";
  doc["unique_id"]        = DEVICE_ID "_relay";
  doc["command_topic"]    = TOPIC_RELAY;
  doc["state_topic"]      = TOPIC_RELAY_S;
  doc["payload_on"]       = "ON";
  doc["payload_off"]      = "OFF";
  doc["device"]["ids"]    = DEVICE_ID;
  serializeJson(doc, buf);
  mqtt.publish(DISCO_RELAY, buf, true);
}

void onMessage(char* topic, byte* payload, unsigned int len) {
  String msg((char*)payload, len);
  if (String(topic) == TOPIC_RELAY) {
    relayOn = (msg == "ON");
    digitalWrite(RELAY_PIN, relayOn ? LOW : HIGH);  // Relay is active-LOW
    mqtt.publish(TOPIC_RELAY_S, relayOn ? "ON" : "OFF", true);
  }
}

void connectMQTT() {
  int delay_ms = 1000;
  while (!mqtt.connected()) {
    Serial.print("MQTT connecting...");
    if (mqtt.connect(DEVICE_ID, MQTT_USER, MQTT_PASS)) {
      Serial.println("OK");
      mqtt.subscribe(TOPIC_RELAY);
      publishDiscovery();
    } else {
      Serial.printf(" failed (%d), retry in %ds\n", mqtt.state(), delay_ms / 1000);
      delay(delay_ms);
      delay_ms = min(delay_ms * 2, 30000);  // Exponential back-off up to 30s
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH);  // Start relay OFF

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nWiFi: " + WiFi.localIP().toString());

  ArduinoOTA.setHostname(DEVICE_ID);
  ArduinoOTA.begin();

  mqtt.setServer(MQTT_BROKER, MQTT_PORT);
  mqtt.setCallback(onMessage);
  dht.begin();
  connectMQTT();
}

void loop() {
  ArduinoOTA.handle();
  if (!mqtt.connected()) connectMQTT();
  mqtt.loop();

  if (millis() - lastPublish >= 30000) {
    lastPublish = millis();
    float temp = dht.readTemperature();
    float hum  = dht.readHumidity();
    if (!isnan(temp)) mqtt.publish(TOPIC_TEMP, String(temp, 1).c_str());
    if (!isnan(hum))  mqtt.publish(TOPIC_HUM,  String(hum,  1).c_str());
    Serial.printf("Published: %.1f°C  %.1f%%\n", temp, hum);
  }
}

Steps

  1. 1Install Mosquitto on a Raspberry Pi or use the Home Assistant MQTT broker add-on
  2. 2Update MQTT_BROKER IP, credentials, and WiFi in the sketch
  3. 3Upload and open Serial Monitor — you should see 'MQTT connecting...OK'
  4. 4In Home Assistant → Settings → Devices, the ESP32 Node 01 device appears automatically
  5. 5Toggle the relay from the HA dashboard or create an automation
  6. 6For OTA updates: Arduino IDE → Sketch → Upload → select the ESP32's network port

Related projects