ESP32 + MQTT: invia i dati a Home Assistant con auto-discovery
Nella stazione ambientale del tutorial precedente i dati restavano solo sul display OLED. Ora colleghiamo l’ESP32 al Wi-Fi e inviamo le misure via MQTT verso Home Assistant, così da costruire uno storico, dei grafici e delle automazioni (es. “se l’umidità supera il 70% accendi il deumidificatore”).
Cos’è MQTT in due minuti
MQTT è un protocollo publish/subscribe pensato per l’IoT: leggero, pensato per reti instabili e dispositivi con poche risorse. I concetti chiave:
- Broker: il server centrale a cui tutti i dispositivi si collegano (es. Mosquitto)
- Topic: una specie di “canale” con nome gerarchico, es.
casa/soggiorno/temperatura - Publish: un dispositivo pubblica un valore su un topic
- Subscribe: un altro dispositivo (o Home Assistant) si iscrive a quel topic e riceve i valori in tempo reale
Prerequisiti
- Home Assistant già installato e funzionante
- Add-on Mosquitto broker installato e avviato (Impostazioni → Add-on → Add-on Store → Mosquitto broker)
- Integrazione MQTT configurata in Home Assistant (Impostazioni → Dispositivi e servizi → Aggiungi integrazione → MQTT, puntando a localhost se Mosquitto gira come add-on)
- Un utente Home Assistant dedicato per i dispositivi MQTT (consigliato, non usare l’utente admin principale)
Librerie necessarie
Nel platformio.ini, aggiungi alle dipendenze del tutorial precedente:
lib_deps =
adafruit/Adafruit BME280 Library@^2.2.4
adafruit/Adafruit SSD1306@^2.5.13
adafruit/Adafruit GFX Library@^1.11.10
adafruit/Adafruit BusIO@^1.16.1
knolleary/PubSubClient@^2.8
bblanchon/ArduinoJson@^7.0.4
Il codice
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
const char* WIFI_SSID = "IL_TUO_SSID";
const char* WIFI_PASSWORD = "LA_TUA_PASSWORD";
const char* MQTT_BROKER = "192.168.1.10"; // IP di Home Assistant
const int MQTT_PORT = 1883;
const char* MQTT_USER = "mqtt_esp32";
const char* MQTT_PASSWORD = "PASSWORD_MQTT";
const char* DEVICE_ID = "esp32-stazione-01";
const char* STATE_TOPIC = "casa/stazione01/state";
WiFiClient espClient;
PubSubClient mqtt(espClient);
Adafruit_BME280 bme;
void connectWiFi() {
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connessione Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connesso, IP: ");
Serial.println(WiFi.localIP());
}
void publishDiscovery() {
// Home Assistant MQTT discovery: fa comparire i sensori automaticamente
// senza doverli configurare a mano in configuration.yaml
struct SensorConfig { const char* key; const char* name; const char* unit; const char* deviceClass; };
SensorConfig sensors[] = {
{"temperature", "Temperatura", "°C", "temperature"},
{"humidity", "Umidita", "%", "humidity"},
{"pressure", "Pressione", "hPa", "pressure"},
};
for (auto &s : sensors) {
String configTopic = String("homeassistant/sensor/") + DEVICE_ID + "_" + s.key + "/config";
JsonDocument doc;
doc["name"] = s.name;
doc["state_topic"] = STATE_TOPIC;
doc["unit_of_measurement"] = s.unit;
doc["device_class"] = s.deviceClass;
doc["value_template"] = String("{{ value_json.") + s.key + " }}";
doc["unique_id"] = String(DEVICE_ID) + "_" + s.key;
doc["device"]["identifiers"][0] = DEVICE_ID;
doc["device"]["name"] = "Stazione ambientale ESP32";
doc["device"]["manufacturer"] = "WebAppPortal DIY";
String payload;
serializeJson(doc, payload);
mqtt.publish(configTopic.c_str(), payload.c_str(), true); // retained
}
}
void connectMQTT() {
while (!mqtt.connected()) {
Serial.print("Connessione MQTT...");
if (mqtt.connect(DEVICE_ID, MQTT_USER, MQTT_PASSWORD)) {
Serial.println("connesso");
publishDiscovery();
} else {
Serial.printf("fallita, rc=%d, riprovo tra 5s\n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
if (!bme.begin(0x76, &Wire)) {
Serial.println("BME280 non trovato");
while (1) delay(10);
}
connectWiFi();
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
connectMQTT();
}
void loop() {
if (!mqtt.connected()) {
connectMQTT();
}
mqtt.loop();
JsonDocument doc;
doc["temperature"] = bme.readTemperature();
doc["humidity"] = bme.readHumidity();
doc["pressure"] = bme.readPressure() / 100.0F;
String payload;
serializeJson(doc, payload);
mqtt.publish(STATE_TOPIC, payload.c_str());
Serial.println("Pubblicato: " + payload);
delay(30000); // ogni 30 secondi
}
Come funziona
- Il dispositivo pubblica un unico messaggio JSON su
casa/stazione01/statecon tutti e tre i valori, invece di tre topic separati: è più efficiente e più facile da gestire publishDiscovery()pubblica dei messaggi speciali su topichomeassistant/sensor/.../configconretained=true: Home Assistant li legge automaticamente e crea le entità sensore senza bisogno di configurazione manuale in YAMLvalue_templatedice a Home Assistant come estrarre il singolo valore dal JSON ricevuto sullostate_topic- Il ciclo
loop()ripubblica lo stato ogni 30 secondi; per un nodo a batteria questo intervallo andrà allungato e combinato con il deep sleep, argomento del prossimo tutorial
Verifica
Dopo il primo avvio, in Home Assistant vai su Impostazioni → Dispositivi e servizi → MQTT: dovresti vedere comparire il dispositivo “Stazione ambientale ESP32” con le tre entità già pronte da aggiungere a una dashboard. Per debug avanzato puoi anche usare uno strumento come MQTT Explorer per vedere i messaggi grezzi che transitano sul broker.
Va oltre
Il nodo ora funziona, ma resta sempre acceso e consuma corrente in continuazione — va bene su USB, ma non su batteria. Nel prossimo tutorial vediamo come usare il deep sleep per costruire un nodo a batteria che dura mesi con una singola carica.
