Come si collega un ESP32 a un broker MQTT?
Come un ESP32 parla con un broker MQTT: ESP-MQTT e client Arduino, certificati TLS, struttura dei topic, riconnessione e discovery di Home Assistant.
Foto: Vishnu Mohanan / Unsplash
Dopo aver letto questo articolo saprai:
- Scegliere tra ESP-MQTT, una libreria MQTT per Arduino ed ESPHome per un progetto con ESP32
- Configurare TLS in modo che il dispositivo verifichi il broker, e decidere quando usare certificati client
- Progettare topic, disponibilità e messaggi di discovery per Home Assistant che reggono alle riconnessioni
In questa pagina
Un ESP32 si collega al Wi-Fi, apre una connessione TCP verso un broker MQTT, di solito protetta da TLS, e poi pubblica le letture su dei topic e sottoscrive i topic dei comandi. In ESP-IDF il client è il componente ESP-MQTT di Espressif; nel mondo Arduino è una delle varie librerie MQTT che girano sopra un client di rete; in ESPHome sono poche righe di YAML. Il protocollo è lo stesso in tutti e tre i casi. Cambia quanto controllo hai su TLS, buffer e riconnessione.
Questa guida dà per note le basi del publish-subscribe spiegate nella guida a MQTT e presuppone che tu abbia già scelto un chip (vedi Scegliere un ESP32). Tratta le opzioni di client, i certificati, la struttura dei topic, cosa succede quando la connessione cade e come far comparire il dispositivo in Home Assistant senza configurazione manuale.
Quale client MQTT usare?
ESP-MQTT (ESP-IDF). Il client di Espressif è l’opzione più completa. La sua documentazione indica il supporto a MQTT 5.0 oltre che a 3.1.1, il trasporto su TCP semplice (mqtt://), TLS (mqtts://), WebSocket (ws://) e WebSocket sicuro (wss://), e un’API a eventi: il tuo codice riceve eventi come connesso, disconnesso, dati e pubblicato invece di interrogare il client. Da ESP-IDF v6.0 il client è un componente separato, da aggiungere con idf.py add-dependency espressif/mqtt, come spiega la pagina MQTT di ESP-IDF.
Librerie Arduino. La scelta storica, PubSubClient, funziona sopra qualsiasi client di rete Arduino, ma il suo README fissa limiti chiari: pubblica solo con QoS 0, sottoscrive con QoS 0 o 1, limita per impostazione predefinita un messaggio a 256 byte compresa l’intestazione e riporta un avviso secondo cui la libreria non è più mantenuta. Per un piccolo sensore va bene; per qualcosa che dovrai mantenere per anni, preferisci una libreria mantenuta attivamente o passa a ESP-IDF.
ESPHome. Se il dispositivo esiste per alimentare Home Assistant, ESPHome genera il firmware da un file di configurazione. Il suo componente MQTT supporta TLS su ESP32 e la discovery di Home Assistant, attiva per impostazione predefinita. La stessa pagina osserva che con Home Assistant potresti preferire l’API nativa di ESPHome, e in quel caso MQTT non serve affatto.
Come si protegge la connessione con TLS?
Gli esempi di Espressif si collegano ai broker TLS sulla porta 8883, quella usata di consueto per MQTT su TLS. Su un ESP32 TLS ha due compiti: cifrare il traffico e dimostrare che il dispositivo sta parlando con il broker giusto. Il secondo è quello che spesso si salta.
In ESP-MQTT il broker si verifica in tre modi: un certificato PEM della CA del broker in broker.verification.certificate, il bundle di certificati di ESP-IDF collegato tramite broker.verification.crt_bundle_attach, oppure un archivio CA globale. Il bundle di certificati è costruito a partire dall’archivio di certificati radice NSS di Mozilla, si può includere per intero o come insieme ridotto delle radici più comuni e si può aggiornare con un aggiornamento OTA. Usa il tuo certificato CA per un broker privato e il bundle per un endpoint cloud pubblico. La documentazione avverte che skip_cert_common_name_check indebolisce TLS ed espone ad attacchi man-in-the-middle: lascialo disattivato.
Perché il dispositivo dimostri la propria identità, ESP-MQTT accetta un certificato client e una chiave in credentials.authentication, la configurazione TLS mutua che i servizi IoT in cloud richiedono di solito. Può anche tenere la chiave fuori dalla flash leggibile usando un secure element o la periferica di firma digitale del chip. Su Arduino la classe NetworkClientSecure offre gli stessi elementi: setCACert per la CA del broker, setCertificate e setPrivateKey per il TLS mutuo, oppure una chiave pre-condivisa.
Metti in conto che i certificati cambieranno. Un certificato del broker che passa a una nuova CA, o un certificato client che scade, tagliano fuori ogni dispositivo che non può essere aggiornato. Identità dei dispositivi, certificati e PKI per l’IoT spiega come distribuirli e rinnovarli.
Come progettare i topic?
Un albero di topic è un’API. Quando qualche centinaio di dispositivi pubblica su di esso, rinominarlo costa caro, quindi decidi la struttura prima della prima installazione. Uno schema che funziona per la maggior parte delle flotte mette prima il sito o il tenant, poi il dispositivo, poi il tipo di messaggio:
home/greenhouse-01/state JSON retained con tutte le letture
home/greenhouse-01/availability retained "online" o "offline"
home/greenhouse-01/cmd/pump comandi verso il dispositivo
home/greenhouse-01/config impostazioni retained lette all'avvio
Tre abitudini ripagano. Pubblica lo stato come un piccolo oggetto JSON per dispositivo invece di un topic per ogni numero, così la radio invia meno messaggi. Marca come retained i messaggi di stato e di configurazione, così una dashboard che sottoscrive più tardi vede subito l’ultimo valore. Tieni i comandi su topic separati che sottoscrive solo il dispositivo, così una lista di controllo degli accessi sul broker può permettere al dispositivo di leggere i comandi e scrivere lo stato, e nient’altro.
Cosa succede quando la connessione cade?
In una rete reale il Wi-Fi cade, il broker si riavvia e i router si riavviano di notte. Il client deve riprendersi senza che qualcuno prema il tasto di reset.
ESP-MQTT si riconnette da solo a meno che non sia impostato network.disable_auto_reconnect, aspettando network.reconnect_timeout_ms tra un tentativo e l’altro, 10 secondi per impostazione predefinita secondo la documentazione di ESP-MQTT. Il keep-alive predefinito è di 120 secondi. I messaggi inviati con QoS 1 o 2 restano in una coda di uscita in RAM finché il broker non li conferma, e questo regge a una connessione persa ma non a un riavvio o a un’interruzione di corrente. Se le letture non devono andare perse, salvale in flash e reinviale dopo la riconnessione.
Registra un Last Will sul topic di disponibilità con payload offline e flag retain, poi pubblica un online retained subito dopo ogni connessione. Il broker pubblica il Last Will solo se il dispositivo sparisce senza una disconnessione pulita. Un dispositivo che si sveglia dal deep sleep, pubblica e si disconnette in modo pulito non fa mai scattare il suo Last Will, quindi per lui non usare la disponibilità. Usa invece una scadenza lato consumatore: il sensore MQTT di Home Assistant ha un’opzione expire_after che segna un valore come non aggiornato quando non arrivano dati in tempo.
Anche le sottoscrizioni vanno gestite con cura. Con una sessione pulita (clean session) il broker le dimentica quando la connessione termina, quindi sottoscrivi di nuovo nel gestore dell’evento di connessione, non una sola volta all’avvio.
Come funziona la discovery MQTT di Home Assistant?
Home Assistant (vedi la guida a Home Assistant) può creare entità a partire da messaggi MQTT senza alcun YAML. Il dispositivo pubblica un messaggio di configurazione su un topic della forma <discovery_prefix>/<component>/[<node_id>/]<object_id>/config, dove il prefisso predefinito è homeassistant, come descritto nella documentazione dell’integrazione MQTT. Per una temperatura letta dal topic precedente:
{
"name": "Greenhouse temperature",
"unique_id": "greenhouse-01-temp",
"state_topic": "home/greenhouse-01/state",
"value_template": "{{ value_json.temp }}",
"device_class": "temperature",
"unit_of_measurement": "°C",
"availability_topic": "home/greenhouse-01/availability",
"device": { "identifiers": ["greenhouse-01"], "name": "Greenhouse node" }
}
pubblicato su homeassistant/sensor/greenhouse-01-temp/config. Lo unique_id permette di rinominare l’entità dall’interfaccia, e il blocco device raggruppa più entità sotto un unico dispositivo. Le versioni più recenti accettano anche un unico messaggio a livello di dispositivo che dichiara tutte le sue entità in una volta, metodo che la documentazione consiglia per i dispositivi con più sensori.
Home Assistant pubblica online su homeassistant/status quando la sua integrazione MQTT si avvia. Lo schema consigliato prevede che il dispositivo sottoscriva quel topic e invii di nuovo i messaggi di discovery quando vede il messaggio di nascita (birth message), meglio se dopo un breve ritardo casuale, così una flotta non inonda il broker. Anche rendere retained i messaggi di discovery funziona, ma la documentazione avverte che le configurazioni retained possono lasciare entità “fantasma”; un payload vuoto retained sul topic di configurazione rimuove l’entità.
Per continuare
- La guida a MQTT approfondisce QoS, messaggi retained e sessioni.
- MQTT o HTTP per l’IoT spiega quando una connessione MQTT persistente batte le semplici richieste HTTP.
- Identità dei dispositivi, certificati e PKI per l’IoT spiega come distribuire e rinnovare i certificati usati qui.
- Scegliere un ESP32 confronta i chip su cui girano questi client.
Argomenti: ESP32, MQTT, TLS, Home Assistant, hardware