Realizza una efficace rete mesh con ESP32 e visualizza i dati su Node-RED

Introduzione

Il progetto che andremo a realizzare si focalizza sulla creazione di una rete mesh utilizzando moduli ESP32, con l’obiettivo di monitorare parametri ambientali come temperatura e umidità, inviando i dati raccolti a un broker MQTT e visualizzandoli in tempo reale su una dashboard Node-RED. Questo sistema sfrutta diversi protocolli di comunicazione, come ESP-NOW per la rete mesh e MQTT per la trasmissione dei dati, garantendo una soluzione robusta e scalabile per l’Internet of Things (IoT).

ESP-NOW è un protocollo di comunicazione proprietario di Espressif che consente la comunicazione diretta tra dispositivi ESP32 senza la necessità di una rete Wi-Fi. Questo protocollo è particolarmente utile per creare reti mesh, dove i nodi possono comunicare tra loro in modo efficiente e a basso consumo energetico. Utilizzando ESP-NOW, i dati raccolti dai sensori di temperatura e umidità vengono trasmessi dai nodi periferici a un nodo centrale (gateway), che agisce come punto di raccolta.

Una volta che il gateway (costituito da un modulo ESP32) ha raccolto i dati dai nodi della rete mesh, li trasferisce per mezzo di una comunicazione seriale a due fili ad un altro modulo ESP32 che li invia, tramite WiFi e protocollo MQTT, ad un broker MQTT installato su una Raspberry. Questa soluzione si è resa necessaria in quanto sia ESP-NOW che MQTT utilizzano il canale WiFi e ciò li rende incompatibili. Quindi il “gateway” è stato sdoppiato: un modulo raccoglie i dati della rete mesh, li trasferisce via seriale all’altro modulo che tramite WiFi li trasferisce usando il protocollo MQTT al broker. MQTT (Message Queuing Telemetry Transport) è un protocollo di messaggistica leggero e aperto, progettato per ambienti con risorse limitate come dispositivi IoT. MQTT opera su un modello di pubblicazione/sottoscrizione, in cui i client pubblicano messaggi su specifici argomenti e si iscrivono per ricevere messaggi su altri argomenti. Questo metodo di comunicazione è ideale per scenari in cui è necessario raccogliere dati da numerosi dispositivi e distribuirli a vari destinatari in modo efficiente.

Infine, i dati inviati al broker MQTT vengono raccolti e visualizzati su una dashboard Node-RED installata sulla Raspberry. Node-RED è uno strumento di sviluppo basato su flussi che permette di collegare dispositivi hardware, API e servizi online in modo visivo. Grazie alla sua interfaccia intuitiva, Node-RED consente di creare facilmente flussi di dati complessi, monitorare i dati in tempo reale e visualizzarli in modo chiaro e strutturato.

Se vuoi approfondire le tematiche sui vari protocolli di comunicazione in maniera un po’ più dettagliata e teorica, ti consiglio la lettura dei due articoli Guida pratica ai protocolli di comunicazione dell’ESP32 e Introduzione all’ MQTT protocol in IoT: la piattaforma numero 1 per la comunicazione fra dispositivi.

Anche in questo progetto useremo l’ottimo IDE PlatformIO per la scrittura del software.

Obiettivi del progetto

L’obiettivo principale del progetto è creare una soluzione IoT completa che permetta di monitorare e visualizzare i dati ambientali provenienti da una rete di sensori distribuita. Realizzando questo progetto, l’utente otterrà una rete mesh affidabile e scalabile, in grado di raccogliere dati da vari punti di un’area e centralizzarli per un monitoraggio efficiente. Questo sistema può essere utilizzato in diverse applicazioni, come monitoraggio ambientale, automazione domestica, gestione agricola intelligente, e molto altro.

Una volta completato, l’utente avrà in mano una rete mesh funzionante, un sistema di raccolta dati basato su MQTT e una dashboard Node-RED per la visualizzazione in tempo reale. Il sistema è altamente estensibile: è possibile aggiungere nuovi nodi alla rete mesh, integrare ulteriori tipi di sensori o attuatori, e personalizzare la dashboard Node-RED per soddisfare specifiche esigenze.

Cosa si otterrà realizzando il progetto

Realizzando questo progetto, l’utente potrà:

  • Monitorare temperatura e umidità in tempo reale: grazie ai sensori distribuiti e alla rete mesh, sarà possibile raccogliere dati da diversi punti di un’area.
  • Visualizzare i dati su una dashboard interattiva: utilizzando Node-RED, i dati raccolti verranno visualizzati in modo chiaro e intuitivo, permettendo un monitoraggio continuo e dettagliato.
  • Scalare il sistema: la rete mesh basata su ESP-NOW e la trasmissione dei dati via MQTT permettono di aggiungere facilmente nuovi nodi e sensori, espandendo la copertura e le funzionalità del sistema.
  • Personalizzare il sistema: Node-RED offre infinite possibilità di personalizzazione, permettendo di adattare il sistema a diverse esigenze e scenari applicativi.

Questo progetto rappresenta un’ottima introduzione al mondo dell’IoT, combinando diverse tecnologie e protocolli per creare un sistema complesso ma gestibile. Inoltre, offre una base solida per futuri sviluppi e integrazioni, consentendo di esplorare ulteriormente le potenzialità dell’Internet of Things.

Cos’è una rete mesh?

Una rete mesh è una rete di comunicazione distribuita dove i nodi (dispositivi) si connettono tra loro in modo dinamico e non gerarchico, formando una rete ad hoc. Ogni nodo agisce sia come punto di trasmissione che come ripetitore, permettendo ai dati di viaggiare attraverso la rete utilizzando il percorso più efficiente. Questo tipo di rete è caratterizzata dalla capacità di auto-ripararsi e di ridistribuire automaticamente il traffico di rete, garantendo una connessione stabile anche se uno o più nodi si disconnettono o falliscono.

Come funziona una rete mesh?

In una rete mesh, ogni nodo è collegato a uno o più altri nodi, creando una rete che può espandersi e adattarsi facilmente. I dati inviati da un nodo possono passare attraverso diversi altri nodi per raggiungere la loro destinazione finale, riducendo la dipendenza da un singolo punto di accesso centrale. Questo permette una maggiore resilienza e affidabilità, poiché la rete può continuare a funzionare anche in presenza di guasti o disconnessioni di alcuni nodi.

Le reti mesh utilizzano protocolli specifici per la gestione delle connessioni e il routing dei dati. Ad esempio, i protocolli come ESP-NOW, utilizzato nella libreria painlessMesh per ESP32, facilitano la comunicazione peer-to-peer tra i dispositivi, permettendo una trasmissione rapida e a basso consumo energetico.

Vantaggi di una rete mesh

  • Scalabilità: le reti mesh possono facilmente espandersi aggiungendo nuovi nodi senza necessità di ristrutturazioni complesse.
  • Affidabilità: la capacità di auto-ripararsi e di ridistribuire il traffico di rete garantisce connessioni stabili anche in caso di guasti o disconnessioni.
  • Flessibilità: le reti mesh non richiedono una topologia predefinita, permettendo una configurazione dinamica dei nodi.
  • Copertura estesa: i nodi possono estendere la copertura della rete oltre i limiti di una singola connessione punto-punto.

Utilizzi delle reti mesh

Le reti mesh sono ampiamente utilizzate in diverse applicazioni, come:

  • Internet of Things (IoT): per collegare sensori e dispositivi in ambienti domestici, industriali o agricoli.
  • Comunicazioni d’emergenza: in scenari di disastri naturali, dove le infrastrutture di comunicazione tradizionali possono essere compromesse.
  • Automazione domestica: per integrare dispositivi smart home come luci, termostati e sistemi di sicurezza.
  • Reti wireless urbane: per fornire accesso a Internet in aree urbane estese attraverso una rete di nodi distribuiti.

Una rete mesh offre una soluzione potente e flessibile per creare reti di comunicazione resilienti e scalabili. In questo progetto, sfrutteremo la capacità degli ESP32 di formare una rete mesh, raccogliendo dati da sensori di temperatura e umidità, e utilizzando MQTT e Node-RED per la visualizzazione dei dati. Questo approccio non solo migliora la robustezza del sistema, ma offre anche un’ampia gamma di possibilità per future estensioni e personalizzazioni.

Il broker MQTT utilizzato nel progetto: Mosquitto

Mosquitto è un’implementazione open-source del protocollo MQTT (Message Queuing Telemetry Transport), progettata per facilitare la comunicazione tra dispositivi IoT (Internet of Things). MQTT è un protocollo di messaggistica leggero, ideale per applicazioni con risorse limitate come sensori, attuatori e dispositivi mobili. Grazie alla sua efficienza e scalabilità, Mosquitto è ampiamente utilizzato in numerosi progetti IoT per la raccolta, l’elaborazione e la distribuzione dei dati.

Caratteristiche principali di Mosquitto

Leggerezza e efficienza: uno dei principali vantaggi di Mosquitto è la sua leggerezza. Il protocollo MQTT è stato progettato per minimizzare l’uso della larghezza di banda e delle risorse di sistema, rendendolo ideale per dispositivi con capacità computazionale e di memoria limitate. Questo è particolarmente utile in ambienti IoT, dove numerosi dispositivi devono comunicare tra loro in modo efficiente.

Pub/Sub Model: Mosquitto utilizza un modello di pubblicazione/sottoscrizione (pub/sub) per la trasmissione dei messaggi. In questo modello, i client possono pubblicare messaggi su specifici argomenti (topic) e sottoscriversi per ricevere messaggi su altri argomenti. Questo approccio disaccoppia i produttori di dati (publishers) dai consumatori (subscribers), migliorando la scalabilità e la flessibilità del sistema.

Qualità del servizio: Mosquitto supporta tre livelli di qualità del servizio (QoS) per la consegna dei messaggi:

  • QoS 0: at most once – il messaggio viene consegnato al massimo una volta senza garanzie di conferma.
  • QoS 1: at least once – il messaggio viene consegnato almeno una volta con conferma di ricezione.
  • QoS 2: exactly once – il messaggio viene consegnato esattamente una volta, garantendo la massima affidabilità.

Supporto per la sicurezza: Mosquitto include il supporto per la sicurezza tramite SSL/TLS, permettendo di cifrare le comunicazioni tra i client e il broker. Inoltre, supporta l’autenticazione mediante nome utente e password, assicurando che solo i client autorizzati possano connettersi e pubblicare/sottoscrivere i messaggi.

Applicazioni di Mosquitto nell’IoT

Mosquitto trova applicazione in una vasta gamma di scenari IoT. È utilizzato per raccogliere dati da sensori distribuiti, monitorare ambienti, automatizzare processi domestici e industriali, e molto altro. Ad esempio, in una casa intelligente, Mosquitto può gestire la comunicazione tra termostati, luci, sensori di movimento e sistemi di sicurezza, consentendo un controllo centralizzato e automatizzato dell’ambiente domestico.

In ambito industriale, Mosquitto facilita la gestione della manutenzione predittiva, raccogliendo dati in tempo reale da macchinari e attrezzature per identificare guasti imminenti e ottimizzare i processi di manutenzione. Inoltre, può essere integrato con piattaforme di analisi dei dati per fornire insights dettagliati sulle operazioni e migliorare l’efficienza operativa.

In definitiva, Mosquitto rappresenta una componente fondamentale per lo sviluppo di soluzioni IoT efficienti e scalabili. Grazie alla sua leggerezza, flessibilità e supporto per la sicurezza, Mosquitto consente di creare sistemi di comunicazione robusti tra dispositivi eterogenei. La sua implementazione open-source e la comunità attiva di sviluppatori garantiscono un continuo miglioramento e adattamento alle nuove esigenze dell’Internet of Things.

Cos’è Node-RED?

Node-RED è una piattaforma di sviluppo basata su flussi (flow-based) che consente di collegare dispositivi hardware, API e servizi online in nuovi e interessanti modi. Sviluppato originariamente da IBM per il suo programma di Internet of Things (IoT), Node-RED è ora un progetto open-source ampiamente utilizzato in tutto il mondo per creare applicazioni IoT e per l’automazione.

Cosa fa Node-RED?

Node-RED fornisce un’interfaccia utente grafica che permette agli utenti di creare flussi di lavoro componendo nodi che rappresentano fonti di dati, processi di elaborazione e destinazioni dei dati. Questi nodi possono essere configurati e collegati tra loro per creare flussi complessi di dati senza quasi dover scrivere codice manualmente. Ogni nodo rappresenta una specifica funzione, come la lettura di un sensore, l’invio di dati a un database, o l’attivazione di un dispositivo.

Come si usa Node-RED?

Node-RED è progettato per essere facile da usare e accessibile anche a chi non ha una vasta esperienza di programmazione. L’interfaccia utente grafica consente di creare flussi di lavoro semplicemente trascinando e collegando nodi sullo spazio di lavoro. Gli utenti possono configurare ciascun nodo attraverso una serie di opzioni e parametri, che vengono impostati tramite una finestra di dialogo accessibile cliccando sul nodo stesso.

Per iniziare a utilizzare Node-RED:

  1. Installazione: Node-RED può essere installato su una vasta gamma di piattaforme, inclusi Raspberry Pi, server cloud, e PC locali. L’installazione può essere effettuata tramite Node.js e npm (Node Package Manager).
  2. Avvio: dopo l’installazione, Node-RED viene avviato tramite un semplice comando nel terminale. Questo comando avvia un server web locale accessibile tramite un browser web.
  3. Creazione dei flussi: utilizzando l’interfaccia grafica, gli utenti possono creare e modificare flussi di lavoro, collegando nodi che rappresentano diverse operazioni di input, elaborazione e output.
  4. Deploy: una volta creati, i flussi possono essere distribuiti (deploy) e Node-RED inizierà a eseguire le operazioni configurate.

A cosa serve Node-RED?

Node-RED è utilizzato in una vasta gamma di applicazioni, incluse ma non limitate a:

  • Internet of Things (IoT): per collegare e coordinare sensori, attuatori e altri dispositivi IoT.
  • Automazione domestica: per creare sistemi di automazione domestica che controllano luci, riscaldamento, sicurezza e altri dispositivi.
  • Integrazione API: per connettere e coordinare diverse API e servizi web, facilitando l’integrazione tra sistemi diversi.
  • Analisi dei dati: per raccogliere, elaborare e visualizzare dati provenienti da diverse fonti in tempo reale.

Perché usare Node-RED?

Node-RED è preferito rispetto ad altre piattaforme per diversi motivi:

  • Facilità d’uso: l’interfaccia grafica consente anche ai non programmatori di creare flussi di lavoro complessi.
  • Flessibilità: grazie alla vasta libreria di nodi disponibili, Node-RED può essere utilizzato per una vasta gamma di applicazioni.
  • Comunità attiva: essendo un progetto open-source, Node-RED beneficia di una comunità attiva che sviluppa nuovi nodi e fornisce supporto.
  • Integrazione: Node-RED è eccellente per integrare diversi dispositivi e servizi, facilitando la creazione di soluzioni IoT e di automazione.
  • Estensibilità: gli utenti possono creare i propri nodi personalizzati utilizzando JavaScript, rendendo la piattaforma altamente estensibile.

In definitiva, Node-RED è una potente piattaforma di sviluppo che consente di creare flussi di lavoro complessi in modo semplice e intuitivo. È ampiamente utilizzato in applicazioni IoT, automazione domestica, integrazione di API e analisi dei dati. Grazie alla sua facilità d’uso, flessibilità e capacità di integrazione, Node-RED è diventato uno strumento preferito per sviluppatori e hobbisti di tutto il mondo.

Cos’è il DHT22

Il DHT22 è un sensore digitale di temperatura e umidità progettato per misurare con precisione questi parametri ambientali. Esso utilizza un sensore di umidità capacitivo e un termistore per misurare rispettivamente l’umidità e la temperatura dell’ambiente circostante. Quando viene alimentato, il sensore acquisisce i dati e li converte in segnali digitali che possono essere letti da un microcontrollore o da un altro dispositivo elettronico.
Il DHT22 è caratterizzato da una buona precisione e stabilità nelle misurazioni di temperatura e umidità. Ha un’ampia gamma di temperatura operativa e una precisione dichiarata nelle misurazioni. Il range di temperature misurate va da -40°C a +80°C (con risoluzione di 0.1°C) mentre quello di umidità relativa va da 0% a 100% (con risoluzione dello 0.1%). Inoltre il DHT22 è in grado di effettuare una misura completa in circa 2 secondi.
Il sensore ha solitamente tre pin per l’interfacciamento: uno per l’alimentazione, uno per la massa e uno per la trasmissione dei dati digitali. Utilizza un protocollo seriale di comunicazione per trasmettere le letture al dispositivo di controllo. I dati sono direttamente digitali quindi non c’è necessità di acquisirli con un convertitore analogico-digitale.
Il DHT22 è ampiamente utilizzato in progetti di monitoraggio ambientale, sistemi di controllo del clima, stazioni meteo fai-da-te e altri dispositivi che richiedono la misurazione precisa della temperatura e dell’umidità.

Vantaggi: tra i vantaggi del DHT22 ci sono le sue dimensioni compatte, la facilità di utilizzo, la bassa potenza richiesta e la relativa economicità rispetto ad altri sensori simili.

Limitazioni: anche se preciso, il DHT22 può essere soggetto a leggere fluttuazioni di misurazione in ambienti con elevate variazioni di temperatura o umidità. Inoltre, la sua interfaccia digitale può richiedere una certa attenzione nella programmazione e nell’interpretazione dei dati letti.

Di che componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

  • due breadboard per connettere le NodeMCU ESP32 agli altri componenti
  • alcuni fili DuPont (maschio – maschio, maschio – femmina, femmina – femmina)
  • due sensori DHT22
  • due resistori da 4.7kΩ
  • una SD card di capienza da 64GB
  • un eventuale dongle WiFi USB per la Raspberry
  • una Raspberry PI 3
  • e, ovviamente, quattro NodeMCU ESP32 !

La SD card che ho usato per gli esperimenti è da 64GB. La Raspberry che ho utilizzato è la 3. Da prove fatte sul modello 1 ho constatato che è troppo lento per far girare Node-RED.

I moduli ESP32 utilizzati in questo progetto sono della azienda AZ-Delivery.

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio diamo un’occhiata al pinout della board:

Pinout della ESP32
Pinout della ESP32

Il pinout del sensore DHT22:

Pinout del DHT22
Pinout del DHT22

Useremo il DHT22 in ciascuno dei due nodi mesh generici. Come già anticipato, la rete mesh si compone di 4 ESP32. Due di esse saranno collegate ciascuna ad un DHT22 e costituiranno i due nodi generici della rete mesh (chiamati mn1 e mn2) che invieranno nella rete i valori rilevati periodicamente dal sensore. Le altre due, indipendenti dalle precedenti, costituiranno il nodo gateway (cioè il nodo facente parte della rete mesh che raccoglie le informazioni inviate dai sensori) e il nodo MQTT che preleverà via collegamento seriale i dati dal nodo gateway e li invierà via WiFi (e tramite protocollo MQTT) alla Raspberry.

Nella foto seguente puoi vedere i due nodi generici collegati ciascuno ad un DHT22:

Nodi generici della rete mesh
Nodi generici della rete mesh

Mentre nella foto seguente puoi vedere il nodo gateway della rete mesh a destra e il nodo MQTT a sinistra. I due nodi sono collegati via porta seriale tramite i due fili marrone e bianco mentre i fili rosso e nero servono ad alimentare la ESP32 di sinistra (che ricava la VCC e la GND dalla ESP32 di destra, l’unica ad essere alimentata tramite cavo USB). In questo modo si evita di avere due cavi USB per l’alimentazione:

Nodo gateway della rete mesh a destra e nodo MQTT a sinistra
Nodo gateway della rete mesh a destra e nodo MQTT a sinistra

Possiamo anche vedere gli schematici realizzati con Fritzing dei due sistemi:

Lo schema seguente mostra il generico nodo della rete mesh col suo DHT22:

Generico nodo della rete mesh
Generico nodo della rete mesh

Lo schema seguente mostra il nodo gateway della rete mesh a sinistra e il nodo MQTT a destra collegati assieme con la seriale e con le alimentazioni:

Nodo gateway e nodo MQTT
Nodo gateway e nodo MQTT

Per collegare il gateway ESP32 all’ ESP32 MQTT via seriale, segui questa tabella di collegamento dei GPIO e delle alimentazioni:

Gateway ESP32 ESP32 MQTT
GPIO 17 (TX)GPIO 16 (RX)
GPIO 16 (RX)GPIO 17 (TX)
GNDGND
5V5V

Gli sketch

Creiamo i progetti PlatformIO

Per questo progetto avremo bisogno di 3 sketch differenti: uno lo useremo per i due nodi mesh generici (dovremo solo cambiare l’identificativo del nodo in mn1 o mn2), uno lo useremo per il nodo gateway mesh e il terzo per il nodo MQTT. Quindi creeremo 3 progetti differenti con PlatformIO.

Abbiamo già visto la procedura di creazione di un progetto PlatformIO nell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO.

Anche se si riferisce alla board ESP8266, la procedura è simile.
Semplicemente, nella scelta della piattaforma, dovrai scegliere la AZ-Delivery ESP-32 Dev Kit C V4 per ciascuno.

Progetto nodo mesh generico

Delle librerie indicate nell’articolo citato installa solo la libreria DHT sensor library for ESPx by Bernd Giesecke come indicato nel tutorial.

Ora modifica il file platformio.ini per aggiungere queste due righe:

monitor_speed = 115200
upload_speed = 921600

in modo che il file abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
monitor_speed = 115200
upload_speed = 921600
framework = arduino

Installa anche la libreria painlessMesh in modo che alla fine il file platformio.ini abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	beegee-tokyo/DHT sensor library for ESPx@^1.19
	painlessmesh/painlessMesh @ ^1.5.0

Puoi scaricare il progetto dal link seguente:

decomprimerlo, prendere il file main.cpp e sostituirlo al posto di quello che hai nel progetto precedentemente creato.

Vediamo ora come funziona lo sketch.

Inizialmente vengono incluse le librerie richieste:

#include <painlessMesh.h>
#include <DHTesp.h>

Vengono poi definiti il GPIO4 per il collegamento dati col DHT22, il MESH_PREFIX che è un parametro utilizzato nella configurazione di una rete mesh con ESP32 per identificare e raggruppare nodi che appartengono alla stessa rete mesh, la MESH_PASSWORD cioè la password dei nodi della stessa rete e la MESH_PORT, la porta attraverso cui i nodi mesh comunicano:

#define DHTPIN 4
#define MESH_PREFIX "ESP_MESH"
#define MESH_PASSWORD "MESH_PASSWORD"
#define MESH_PORT 5555

Viene poi definito il nome del particolare nodo:

const char* nodeId = "mn1"; // mesh node number

I due nodi avranno caricato lo stesso sketch solo che in uno il nome del nodo sarà mn1 e nell’altro mn2.

Segue l’istanziazione degli oggetti dht (che gestisce il sensore DHT22), userScheduler che schedula l’invio dei messaggi e mesh che gestisce la rete mesh:

DHTesp dht;
Scheduler userScheduler;
painlessMesh mesh;

Incontriamo poi la funzione sendMessage che, quando viene chiamata dallo scheduler, legge i dati dal sensore, compone la stringa con i dati e la spedisce sulla rete mesh:

void sendMessage() {
  Serial.println("Executing sendMessage");
  TempAndHumidity data = dht.getTempAndHumidity();
  if (!isnan(data.humidity) && !isnan(data.temperature)) {
    String msg = String(nodeId) + ";T:" + String(data.temperature, 1) + ";H:" + String(data.humidity, 1);
    mesh.sendBroadcast(msg);
    Serial.println("Message sent: " + msg);
  } else {
    Serial.println("Failed to read from DHT sensor!");
  }
}

Segue poi il task manager che decide la frequenza con cui devono essere spediti i messaggi:

Task taskSendMessage(TASK_SECOND * 10, TASK_FOREVER, &sendMessage);

La funzione setup inizializza la porta seriale per visualizzare i messaggi sul Serial Monitor, inizializza il sensore DHT22, inizializza la rete mesh e lo scheduler:

void setup() {
  Serial.begin(115200);
  dht.setup(DHTPIN, DHTesp::DHT22);
  Serial.println("Setting up mesh...");
  
  mesh.setDebugMsgTypes(ERROR | DEBUG | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
  mesh.onReceive([](uint32_t from, String &msg) {
    Serial.printf("Received from %u: %s\n", from, msg.c_str());
  });

  userScheduler.addTask(taskSendMessage);
  taskSendMessage.enable();
  Serial.println("Mesh setup complete");
}

Infine, la funzione loop ciclicamente aggiorna la rete mesh ed eseguo lo scheduler:

void loop() {
  mesh.update();
  userScheduler.execute();
}

Progetto nodo mesh gateway

Delle librerie indicate nell’articolo citato non installare nessuna libreria.

Ora modifica il file platformio.ini per aggiungere queste due righe:

monitor_speed = 115200
upload_speed = 921600

in modo che il file abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
monitor_speed = 115200
upload_speed = 921600
framework = arduino

Installa anche la libreria painlessMesh in modo che alla fine il file platformio.ini abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
    painlessmesh/painlessMesh @ ^1.5.0

Puoi scaricare il progetto dal link seguente:

decomprimerlo, prendere il file main.cpp e sostituirlo al posto di quello che hai nel progetto precedentemente creato.

Vediamo ora come funziona lo sketch.

Inizialmente vengono incluse le librerie richieste:

#include <painlessMesh.h>

Poi vengono settati i parametri già visti nello sketch precedente:

#define MESH_PREFIX "ESP_MESH"
#define MESH_PASSWORD "MESH_PASSWORD"
#define MESH_PORT 5555

e vengono istanziati lo scheduler e la rete mesh:

Scheduler userScheduler;
painlessMesh mesh

La seguente funzione receivedCallback gestisce la ricezione dei messaggi dalla rete mesh e li inoltra all’ ESP32 MQTT grazie al collegamento seriale tramite la funzione Serial1.println(msg); sulla porta seriale 1;

void receivedCallback(uint32_t from, String &msg) {
  Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
  // Send data to the second ESP32 via Serial1
  Serial1.println(msg);
}

La funzione setup inizializza la porta seriale standard e la porta seriale 1 indicando i pin di comunicazione (GPIO16 e GPIO17) per trasferire i dati ricevuti verso la ESP32 MQTT. Inoltre setta la rete mesh inizializzandola e impostando la funzione receivedCallback come handler dei messaggi ricevuti:

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, 16, 17); // Initialize Serial1 with RX1=GPIO 16 and TX1=GPIO 17 for communication with the second ESP32
  Serial.println("Setting up mesh...");

  mesh.setDebugMsgTypes(ERROR | DEBUG | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
  mesh.onReceive(&receivedCallback);

  Serial.println("Mesh setup complete");
}

La funzione loop ciclicamente aggiorna la rete mesh:

void loop() {
  mesh.update();
}

Progetto nodo ESP32 MQTT

Delle librerie indicate nell’articolo citato non installare nessuna libreria.

Ora modifica il file platformio.ini per aggiungere queste due righe:

monitor_speed = 115200
upload_speed = 921600

in modo che il file abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
monitor_speed = 115200
upload_speed = 921600
framework = arduino

Installa anche la libreria PubSubClient in modo che alla fine il file platformio.ini abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps =
  knolleary/PubSubClient @ ^2.8

Puoi scaricare il progetto dal link seguente:

decomprimerlo, prendere il file main.cpp e sostituirlo al posto di quello che hai nel progetto precedentemente creato.

Vediamo ora come funziona lo sketch.

Inizialmente vengono incluse le librerie richieste:

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

Poi seguono i prototipi delle funzioni utilizzate in seguito:

// Prototipi delle funzioni
void setup_wifi();
void reconnect();

Vengono impostati i parametri della rete WiFi e l’IP della Raspberry che riceverà i messaggi MQTT:

#define WIFI_SSID "MY_WIFI_NETWORK_SSID"
#define WIFI_PASSWORD "MY_WIFI_NETWORK_PASSWORD"
#define MQTT_SERVER "192.168.1.190"

Dovrai quindi inserire l’SSID e la password della tua rete WiFi. L’IP della Raspberry sarà 192.168.1.190 se seguirai le istruzioni del paragrafo relativo alla preparazione della Raspberry.

Vengono poi istanziati il client WiFi e quello MQTT:

WiFiClient espClient;
PubSubClient client(espClient);

La funzione setup inizializza la porta seriale di default e la porta seriale 1 indicando i pin di comunicazione (GPIO16 e GPIO17) per ricevere i dati dalla ESP32 mesh gateway, inizializza il WiFi e il server MQTT sulla porta 1883:

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, 16, 17); // Initialize Serial1 with RX1=GPIO 16 and TX1=GPIO 17 for receiving data from the gateway

  setup_wifi();
  client.setServer(MQTT_SERVER, 1883);
}

La funzione setup_wifi chiamata dalla setup si occupa di stabilire la connessione WiFi:

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  int attempt = 0;
  while (WiFi.status() != WL_CONNECTED && attempt < 20) {
    delay(500);
    Serial.print(".");
    attempt++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("Failed to connect to WiFi");
  }
}

La funzione loop ciclicamente controlla lo stato della connessione MQTT e nel caso questa fosse interrotta la ristabilisce, riceve i dati dalla ESP32 mesh gateway tramite la seriale 1, pubblica verso l’endpoint MQTT (cioè la Raspberry) i dati raccolti:

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  if (Serial1.available()) {
    String data = Serial1.readStringUntil('\n');
    Serial.println("Received: " + data);

    // Publish the data to the MQTT broker
    if (client.publish("sensor/data", data.c_str())) {
      Serial.println("Data published successfully");
    } else {
      Serial.println("Failed to publish data");
    }
  }
}

La funzione reconnect si occupa di mantenere sempre in funzione il client MQTT:

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32Client")) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

Le librerie utilizzate: painlessMesh e PubSubClient

painlessMesh è una libreria progettata per semplificare la creazione di reti mesh con moduli ESP8266 e ESP32. Utilizzando questa libreria, i dispositivi possono comunicare tra loro in una rete decentralizzata senza necessità di un punto di accesso centrale. La sua architettura si basa sul protocollo ESP-NOW di Espressif, che consente una comunicazione peer-to-peer affidabile e a basso consumo energetico. painlessMesh gestisce automaticamente la scoperta dei nodi, la formazione della topologia di rete e il mantenimento delle connessioni tra i nodi, permettendo agli sviluppatori di concentrarsi sulle funzionalità delle loro applicazioni piuttosto che sulla complessità della rete. Inoltre, la libreria supporta la sincronizzazione del tempo tra i nodi e la trasmissione di messaggi broadcast, rendendo semplice la creazione di applicazioni IoT distribuite e resilienti.

PubSubClient è una libreria client per MQTT (Message Queuing Telemetry Transport) pensata per dispositivi Arduino e compatibili come ESP8266 e ESP32. Questa libreria consente ai dispositivi di connettersi a un broker MQTT e di pubblicare e sottoscriversi a messaggi su vari topic. MQTT è un protocollo di messaggistica leggero ideale per applicazioni IoT, grazie alla sua efficienza nella gestione delle risorse e alla sua capacità di operare su reti instabili. PubSubClient gestisce le connessioni al broker, le riconnessioni automatiche in caso di disconnessione e offre un’interfaccia semplice per pubblicare e ricevere messaggi. La libreria supporta diverse qualità del servizio (QoS) per la consegna dei messaggi e può essere configurata per operare con o senza autenticazione e crittografia, rendendola flessibile e sicura per diverse applicazioni IoT.

Preparazione della Raspberry

Per poter utilizzare la Raspberry è necessario fare alcuni passi preliminari ed installare alcuni software.

Iniziamo subito con l’installazione del sistema operativo.

Il sistema operativo scelto è una distribuzione fatta apposta per funzionare su tutti i tipi di Raspberry, anche le più datate. I test sono stati fatti su una Raspberry Pi 1 Model B e su una Raspberry PI 3 Model B. Ho alla fine optato per la Raspberry PI 3 Model B in quanto la Raspberry Pi 1 Model B si è dimostrata troppo lenta nella installazione e gestione di Node-RED.

Se la Raspberry non ha connessione Wireless nativa puoi usare un dongle WiFi da inserire in una delle sue prese USB.

Scarichiamo e installiamo il sistema operativo su SD card

Scarica l’ultima versione del sistema operatico all’indirizzo https://www.raspberrypi.com/software/operating-systems/

e portati alla sezione Raspberry Pi OS (Legacy). Scaricherai una versione che non ha ambiente grafico in modo che sia la più leggera possibile:

La distribuzione scelta
La distribuzione scelta

Il file scaricato sarà compresso in formato xz. Per decomprimerlo su Linux dovrai prima installare il tool:

sudo dnf install xz           su CentOS/RHEL/Fedora Linux.
sudo apt install xz-utils     su Ubuntu/Debian

e poi dare la riga di comando:

xz -d -v filename.xz

dove filename.xz è il nome del file che hai appena scaricato contenente il sistema operativo.

Su Windows sarà sufficiente utilizzare uno fra questi tool: 7-Zip, winRAR, WinZip.

Il risultato sarà un file con estensione img che è l’immagine da flashare sulla SD card della Raspberry.

Per flashare l’immagine sulla SD card userai il tool Balena Etcher che funziona sia su Linux che su Windows che su MACOS.

Il suo utilizzo è molto semplice: è sufficiente selezionare l’immagine da flashare, la SD card di destinazione e premere il pulsante Flash.

Ecco come appare la sua interfaccia:

L'interfaccia del tool Balena Etcher
L’interfaccia del tool Balena Etcher

A sinistra viene impostata l’immagine da flashare, al centro la SD card da flashare, a destra il pulsante per iniziare l’operazione di flashing.

Alla fine della operazione la SD card conterrà due partizioni: boot e rootfs. Nel gestore dei dispositivi su Linux appare un menu del genere:

Menu dei dispositivi su Linux
Menu dei dispositivi su Linux

Anche Windows mostrerà un menu del genere: dal tuo file explorer, alla voce Questo computer vedrai le 2 partizioni.

Ora, con un editor di testo, crea sul tuo computer un file che chiamerai wpa_supplicant.conf e che editerai in questo modo:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=«your_ISO-3166-1_two-letter_country_code»
network={
     ssid="«your_SSID»"
     psk="«your_PSK»"
     key_mgmt=WPA-PSK
}

Dovrai sostituire le seguenti voci:

  • «your_ISO-3166-1_two-letter_country_code» con l’identificativo del tuo paese (per esempio per l’Italia è IT)
  • «your_SSID» con il nome SSID della tua rete WiFi
  • «your_PSK» con la password della rete WiFi

A questo punto, dovrai creare un file vuoto che chiamerai ssh (senza nessuna estensione).

Le nuove distribuzioni non hanno il classico utente pi con password raspberry quindi, per poter entrare in SSH, dobbiamo provvedere in altro modo.

Con una Raspberry funzionante dobbiamo creare un file di nome userconf che conterrà l’utente che vogliamo creare con una versione criptata della password che gli vogliamo attribuire. Il formato sara quindi username:password-hash.

Supponiamo di voler tenere l’utente pi, dobbiamo creare la password-hash. Supponiamo di voler creare l’hash della password raspberry, sempre nella Raspberry dove abbiamo creato il file userconf. Dobbiamo dare il comando, da shell, seguente:

echo "raspberry" | openssl passwd -6 -stdin

Questo comando restituirà l’hash della password raspberry. Per esempio potrebbe essere una stringa così:

$6$ROOQWZkD7gkLyZRg$GsKVikua2e1Eiz3UNlxy1jsUFec4j9wF.CQt12mta/6ODxYJEB6xuAZzVerM3FU2XQ27.1tp9qJsqqXtXalLY.

Questo è l’hash della password raspberry che ho calcolato sulla mia Raspberry.

Il nostro file userconf quindi conterrà la seguente stringa:

pi:$6$ROOQWZkD7gkLyZRg$GsKVikua2e1Eiz3UNlxy1jsUFec4j9wF.CQt12mta/6ODxYJEB6xuAZzVerM3FU2XQ27.1tp9qJsqqXtXalLY.

NOTA BENE: è necessario calcolare l’hash con una Raspberry perché l’hash calcolato col computer utilizza un altro algoritmo che non consentirebbe alla Raspberry che stiamo preparando di riconoscere la password.

Alternativamente puoi scaricare dal link qua sotto il file userconf che ho creato io per avere utente pi con password raspberry.

Ora apri la partizione boot sulla SD card e copia dentro i tre files wpa_supplicant.conf, ssh e userconf. Rimuovi in sicurezza la SD card dal computer e inseriscila nella Raspberry.

Accendi la Raspberry, aspetta qualche minuto. Per poterti loggare in ssh alla Raspberry, dovrai scoprire quale sia il suo IP (quello che il router le ha assegnato tramite DHCP).

Per fare questo è sufficiente dare il comando da una shell del pc:

ping raspberrypi.local 

valido sia su Linux che su Windows (previa installazione di Putty su Windows).

Nel mio PC la Raspberry risponde così:

Risposta della Raspberry al ping
Risposta della Raspberry al ping

Ciò mi fa capire che l’IP assegnato è 192.168.43.27.

Alternativamente puoi usare il tool Angry IP Scanner oppure puoi accedere alle impostazioni del tuo router per vedere i dispositivi collegati via WiFi e scoprire che IP ha la Raspberry.

Per poter loggarti sulla Raspberry in ssh dai il comando da shell (ovviamente nel tuo caso l’IP sarà diverso da questo):

con password raspberry. Su Windows è necessario Putty.

Una volta dentro la Raspberry dai i seguenti comandi per fare l’update del software:

sudo apt update
sudo apt upgrade

La password è sempre raspberry.

Configuriamo la timezone

Per configurare la timezone dai il comando:

sudo raspi-config

alla shell della Raspberry. Supponiamo di voler impostare il fuso orario di Roma (io qui farò l’esempio del fuso orario di Roma dato che vivo in Italia, tu dovrai usare il fuso orario del tuo Paese).

Apparirà una schermata così:

Schermata iniziale del comando sudo raspi-config
Schermata iniziale del comando sudo raspi-config

Seleziona l’opzione sulla localizzazione e dai Ok:

Selezionata l'opzione sulla localizzazione
Selezionata l’opzione sulla localizzazione

Seleziona poi l’opzione sulla timezone e dai Ok:

Selezionata l'opzione della timezone
Selezionata l’opzione della timezone

Seleziona ora l’area geografica e dai Ok:

Selezionata l'area geografica
Selezionata l’area geografica

Infine seleziona la città e dai Ok:

Selezionata la città
Selezionata la città

Ecco fatto!

Riavvia la Raspberry dando il comando:

sudo reboot

e, dopo pochi minuti, rientra in ssh come hai fatto prima.

Dai il comando

date

La Raspberry dovrebbe ora mostrare data e ora corrette.

Impostiamo l’IP statico

Per fare in modo che la Raspberry abbia sempre lo stesso indirizzo IP, dobbiamo impostarlo in modo che sia statico. Nei miei test l’ho impostato a 192.168.1.190. Se non facessimo così, il router le assegnerebbe un IP diverso ad ogni riavvio il che ci costringerebbe ogni volta a cambiare l’indirizzo IP del server MQTT negli sketch dell’ESP32.

Procederemo in due passi:

  • imposteremo l’IP fisso nella Raspberry
  • imposteremo il router in modo che riservi quell’indirizzo alla nostra Raspberry

Per il primo punto, dai il comando:

nano /etc/dhcpcd.conf

per aprire il file dhcpcd.conf ed editarlo.

Alla fine del file dovrai aggiungere un blocco del genere:

interface [INTERFACE]
static_routers=[ROUTER IP]
static domain_name_servers=[DNS IP]
static ip_address=[STATIC IP ADDRESS YOU WANT]/24

dove:

  • [INTERFACE] è il nome dell’interfaccia WiFi (nel nostro caso sarà wlan0)
  • [ROUTER IP] è l’indirizzo del nostro router (in genere è qualcosa del tipo 192.168.0.1 oppure 192.168.1.1). Lo puoi trovare entrando nell’interfaccia di amministrazione del tuo modem/router
  • [DNS IP] è l’indirizzo del server DNS, che in genere coincide con il parametro [ROUTER IP] del modem/router
  • [STATIC IP ADDRESS YOU WANT] è l’indirizzo IP che vogliamo assegnare come IP fisso alla Raspberry

Quindi, supposto che [ROUTER IP] = [DNS IP] = 192.168.1.1 e che [STATIC IP ADDRESS YOU WANT] = 192.168.1.190, il blocco avrà un aspetto del genere:

interface wlan0
static_routers=192.168.1.1
static domain_name_servers=192.168.1.1
static ip_address=192.168.1.190/24

Riavvia la Raspberry sempre col comando

sudo reboot

e poi accedi nuovamente in ssh, questa volta con IP 192.168.1.190.

Come secondo passo imposteremo il router in modo che riservi l’indirizzo 192.168.1.190 alla nostra Raspberry. Ogni modem/router è diverso dagli altri ma più o meno si somigliano. Mostrerò qui come appare il mio.

Per entrare digito l’indirizzo 192.168.1.1 (perche il mio modem ha questo IP) sul browser e, dopo aver dato la password di amministratore, arrivo alla schermata principale. Da qui devo cercare la schermata relativa al controllo degli accessi.

Aggiunta di un IP statico per la Raspberry
Aggiunta di un IP statico per la Raspberry

Ci sarà un pulsante di aggiunta di un IP statico: aggiungi l’IP scelto abbinato al MAC address della scheda WiFi della Raspberry. Ti consiglio comunque di consultare il manuale di istruzioni del tuo modem/router per questa operazione.

Verifica ora che la Raspberry si connetta alla rete dando il comando:

ping www.google.com

Se ottieni la risposta al ping la rete è connessa. Se ottieni un messaggio del tipo “Network is unreachable” dai il comando

sudo route add default gw [ROUTER IP]  

dove [ROUTER IP] è il gateway che nel nostro caso è l’IP del router, cioè 192.168.1.1

Installiamo il broker MQTT

Per prima cosa installiamo un broker MQTT molto comune che si chiama Mosquitto:

sudo apt-get update
sudo apt-get install mosquitto mosquitto-clients

e poi ne abilitiamo e avviamo il relativo servizio:

sudo systemctl enable mosquitto
sudo systemctl start mosquitto

Per impostazione predefinita, il broker Mosquitto consente connessioni anonime. In questo progetto, per non complicare ulteriormente le cose, non useremo utente e password per configurare il broker Mosquitto. Per impostarlo in questo modo devi modificare il file di configurazione di Mosquitto.

Prima di tutto cambiamo il nostro utente da pi a root dando il comando alla shell:

sudo su

e, alla richiesta, digitiamo la password (che è raspberry se non è stata cambiata).

Dai il comando:

nano /etc/mosquitto/mosquitto.conf

e aggiungi alla fine del file queste due righe:

listener 1883
allow_anonymous true

Perché le modifiche diventino operative dai il comando:

systemctl restart mosquitto

In questo modo il servizio Mosquitto verrà riavviato.

Torna ora all’utente pi dando il comando:

exit

Per vedere i messaggi MQTT trasmessi dal gateway ESP32 (verso il topic sensor/data) sul tuo Raspberry Pi, puoi utilizzare il client MQTT mosquitto_sub fornito con il broker Mosquitto. Ecco come farlo:

  1. Apri una finestra del terminale sul tuo Raspberry Pi.
  2. Utilizza il seguente comando per sottoscriverti al topic MQTT e visualizzare i messaggi in arrivo:
mosquitto_sub -h IP_ADDRESS_RASPBERRY_PI -t sensor/data -v

dove

  • IP_ADDRESS_RASPBERRY_PI: sostituiscilo con l’indirizzo IP del tuo Raspberry Pi.
  • sensor/data: è il nome del topic MQTT a cui la tua ESP32 sta pubblicando i dati (la ESP32 che riceve via seriale i dati dalla ESP32 collegata alla rete mesh come gateway).

Per esempio, nel mio caso, avrò:

mosquitto_sub -h 192.168.1.190 -t sensor/data -v

Una volta eseguito il comando, dovresti vedere i messaggi MQTT in arrivo nel terminale ogni volta che la ESP32 pubblica dati sul topic specificato. I messaggi visualizzati includeranno la temperatura e l’umidità inviate dalle due ESP32 che costituiscono i due nodi generici (sensori) della rete mesh.

L’immagine seguente mostra una shell dove è stato dato il comando di sopra:

Shell Mosquitto
Shell Mosquitto

Come puoi notare, le sigle mn1 e mn2 identificano i due nodi mesh 1 e 2. Ciascuno è affiancato dai valori di temperatura e umidità da esso rilevati.

Installiamo Node-RED

Ora che abbiamo verificato che il broker MQTT instllato sulla Raspberry funziona, non ci rimane che installare e settare Node-RED. Entra in ssh sulla Raspberry con user pi e password raspberry:

Prima di tutto fai un update della Raspberry così:

sudo apt-get update
sudo apt-get upgrade

Poi dai il comando:

bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

e avvia l’applicazione:

node-red-start

Poi apri un browser e vai all’indirizzo http://<IP-della-tua-Raspberry-Pi>:1880 (se hai settato l’IP statico come ho fatto io l’URL sarebbe http://192.168.1.190:1880). Se tutto è andato a buon fine dovrebbe apparirti la dashboard di Node-RED, come nella figura qui sotto:

Dashboard di Node-RED
Dashboard di Node-RED

Noterai che alla sinistra c’è un vasto assortimento di nodi da utilizzare per comporre l’applicazione in maniera del tutto visuale.

Ora devi installare il modulo dashboard con i seguenti comandi:

cd ~/.node-red
npm install node-red-dashboard

e riavviare Node-RED:

node-red-stop
node-red-start

Sempre sulla dashboard, dal lato sinistro trascina i nodi “mqtt in”, “function” e “gauge” come mostrato nella figura seguente:

Disposizione dei nodi
Disposizione dei nodi

Fai doppio click sul nodo mqtt, si aprirà un menù:

Menu iniziale del nodo mqtt
Menu iniziale del nodo mqtt

Clicca sul simbolo “+” a fianco della matita, si aprirà un ulteriore menu:

Menu settaggio server
Menu settaggio server

Alla voce Name metti ESP32, alla voce Server metti 192.168.1.190. Dovrebbe apparirti una cosa del genere:

Menu del server mqtt
Menu del server mqtt

Premi il pulsante Update. Tornerai al menu precedente che riempirai in questo modo:

Ultimo settaggio del nodo mqtt
Ultimo settaggio del nodo mqtt

Premi il pulsante Done per salvare le modifiche.

Ora fai doppio click sul nodo function 1. Nel campo name scrivi data separator e nel tab Setup, alla voce Outputs, metti 2, come nella immagine seguente:

Menu del nodo separatore dei dati
Menu del nodo separatore dei dati

Portati poi sul tab On Message e copia lo script Javascript che vedi qui sotto:

var parts = msg.payload.split(";");
var nodeId = parts[0];
var temperature = parseFloat(parts[1].split(":")[1]);
var humidity = parseFloat(parts[2].split(":")[1]);

if (nodeId === "mn1") {
    var msg1 = { payload: temperature, topic: "mn1/temperature" };
    var msg2 = { payload: humidity, topic: "mn1/humidity" };
    return [msg1, msg2];
} else if (nodeId === "mn2") {
    var msg3 = { payload: temperature, topic: "mn2/temperature" };
    var msg4 = { payload: humidity, topic: "mn2/humidity" };
    return [msg3, msg4];
} else {
    return null;
}

Questo script serve a separare i messaggi che provengono dai due nodi mn1 e mn2:

Lo script per la separazione dei messaggi dei due nodi mn1 e mn2
Lo script per la separazione dei messaggi dei due nodi mn1 e mn2

Premi il tasto Done per rendere definitive le modifiche.

Ora lavora sui nodi function 2 e function 3. Apri il primo e metti nel campo Name la stringa function get temp 1 e nel tab On Message lo script:

if (msg.topic === "mn1/temperature") {
    return msg;
}

Apri il secondo dandogli il nome function get temp 2 e lo script:

if (msg.topic === "mn2/temperature") {
    return msg;
}

Analogamente con i blocchi function4 e function 5 darai i nomi function get hum 1 e function get hum 2 e gli script nel tab On Message:

if (msg.topic === "mn1/humidity") {
    return msg;
}

per il primo e

if (msg.topic === "mn2/humidity") {
    return msg;
}

per il secondo. Clicca sempre il pulsante Done per rendere effettive le modifiche.

Ora ti occuperai dei nodi gauge. I primi due mostreranno la temperatura proveniente dai due sensori e saranno collegati ai nodi function get temp 1 e function get temp 2. Gli altri due mostreranno la umidità proveniente dai due sensori e saranno collegati ai nodi function get hum 1 e function get hum 2.

Vediamo il primo. Cliccaci due volte e riempi il menu in questo modo:

Il menu del primo gauge relativo alla temperatura
Il menu del primo gauge relativo alla temperatura

Clicca il pulsante Done. Apri il secondo gauge per la temperatura 2 e compilalo nello stesso modo mettendo però Temp 2 nella Label. Clicca il pulsante Done.

Apri ora il primo gauge relativo all’umidità e riempi i suoi campi in questo modo:

Il menu del primo gauge relativo alla umidità
Il menu del primo gauge relativo alla umidità

Clicca il pulsante Done. Apri il secondo gauge per la umidità 2 e compilalo nello stesso modo mettendo però Hum 2 nella Label. Clicca il pulsante Done.

A questo punto collegherai tutti i nodi in questo modo:

Flowchart completo
Flowchart completo

Per far funzionare il sistema clicca sul pulsante Deploy. Se tutto è andato a buon fine, all’URL http://IP_RASPBERRY:1880/ui (quindi http://192.168.1.190:1880/ui) vedrai i gauge che mostrano la temperatura e l’umidità provenienti dai sensori come nelle immagini seguenti:

Gauge della temperatura
Gauge della temperatura
Gauge della umidità
Gauge della umidità

Se qualcosa dovesse andare storto o non avessi voglia di fare tutto questo lavoro non ti devi scoraggiare. È possibile importare l’intero flowchart in pochi, semplici passaggi. Nella dashboard vai nell’angolo in alto a destra, dove ci sono i tre trattini orizzontali:

Menu della dashboard
Menu della dashboard

Cliccalo e si aprirà un menu. Una delle voci è Import. Cliccala e si aprirà una finestra:

Finestra di importazione
Finestra di importazione

Copia lo script seguente in quella finestra e clicca il pulsante Import:

 [ { "id": "29c40c6f5b2f6e37", "type": "mqtt in", "z": "006480c71cd03416", "name": "ESP32-MQTT", "topic": "sensor/data", "qos": "2", "datatype": "auto-detect", "broker": "02d77ef8cb4a2e2b", "nl": false, "rap": true, "rh": 0, "inputs": 0, "x": 330, "y": 360, "wires": [ [ "b810943866e53c1d" ] ] }, { "id": "b810943866e53c1d", "type": "function", "z": "006480c71cd03416", "name": "data separator", "func": "var parts = msg.payload.split(\";\");\nvar nodeId = parts[0];\nvar temperature = parseFloat(parts[1].split(\":\")[1]);\nvar humidity = parseFloat(parts[2].split(\":\")[1]);\n\nif (nodeId === \"mn1\") {\n var msg1 = { payload: temperature, topic: \"mn1/temperature\" };\n var msg2 = { payload: humidity, topic: \"mn1/humidity\" };\n return [msg1, msg2];\n} else if (nodeId === \"mn2\") {\n var msg3 = { payload: temperature, topic: \"mn2/temperature\" };\n var msg4 = { payload: humidity, topic: \"mn2/humidity\" };\n return [msg3, msg4];\n} else {\n return null;\n}\n", "outputs": 2, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 380, "wires": [ [ "6bf61ebd5e651515", "ca283e214d38d2c4" ], [ "3a40207b4e0c6d1c", "5ac598492ea1e3ed" ] ] }, { "id": "7360f3682632eab9", "type": "ui_gauge", "z": "006480c71cd03416", "name": "", "group": "e9325c92ddd8054e", "order": 0, "width": 0, "height": 0, "gtype": "gage", "title": "Temp 1", "label": "°C", "format": "{{value}}", "min": "-20", "max": "80", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "diff": false, "className": "", "x": 1180, "y": 320, "wires": [] }, { "id": "176905eaeff4b23f", "type": "ui_gauge", "z": "006480c71cd03416", "name": "", "group": "01e25ac3e37c0f87", "order": 0, "width": 0, "height": 0, "gtype": "gage", "title": "Hum 1", "label": "%", "format": "{{value}}", "min": 0, "max": "100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "diff": false, "className": "", "x": 1170, "y": 440, "wires": [] }, { "id": "2cd6151c3abfaa8c", "type": "ui_gauge", "z": "006480c71cd03416", "name": "", "group": "e9325c92ddd8054e", "order": 1, "width": 0, "height": 0, "gtype": "gage", "title": "Temp 2", "label": "°C", "format": "{{value}}", "min": "-20", "max": "80", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "diff": false, "className": "", "x": 1180, "y": 360, "wires": [] }, { "id": "78b5a4fc97c30383", "type": "ui_gauge", "z": "006480c71cd03416", "name": "", "group": "01e25ac3e37c0f87", "order": 1, "width": 0, "height": 0, "gtype": "gage", "title": "Hum 2", "label": "%", "format": "{{value}}", "min": 0, "max": "100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "diff": false, "className": "", "x": 1170, "y": 480, "wires": [] }, { "id": "6bf61ebd5e651515", "type": "function", "z": "006480c71cd03416", "name": "function get temp 1", "func": "if (msg.topic === \"mn1/temperature\") {\n return msg;\n}\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 850, "y": 320, "wires": [ [ "7360f3682632eab9" ] ] }, { "id": "ca283e214d38d2c4", "type": "function", "z": "006480c71cd03416", "name": "function get temp 2", "func": "if (msg.topic === \"mn2/temperature\") {\n return msg;\n}\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 850, "y": 360, "wires": [ [ "2cd6151c3abfaa8c" ] ] }, { "id": "3a40207b4e0c6d1c", "type": "function", "z": "006480c71cd03416", "name": "function get hum 1", "func": "if (msg.topic === \"mn1/humidity\") {\n return msg;\n}\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 850, "y": 440, "wires": [ [ "176905eaeff4b23f" ] ] }, { "id": "5ac598492ea1e3ed", "type": "function", "z": "006480c71cd03416", "name": "function get hum 2", "func": "if (msg.topic === \"mn2/humidity\") {\n return msg;\n}\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 850, "y": 480, "wires": [ [ "78b5a4fc97c30383" ] ] }, { "id": "02d77ef8cb4a2e2b", "type": "mqtt-broker", "name": "ESP32", "broker": "192.168.1.190", "port": "1883", "clientid": "", "autoConnect": true, "usetls": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "autoUnsubscribe": true, "birthTopic": "", "birthQos": "0", "birthRetain": "false", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closeRetain": "false", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willRetain": "false", "willPayload": "", "willMsg": {}, "userProps": "", "sessionExpiry": "" }, { "id": "e9325c92ddd8054e", "type": "ui_group", "name": "Temperature", "tab": "a3448b1260c49b81", "order": 1, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "01e25ac3e37c0f87", "type": "ui_group", "name": "Humidity", "tab": "024f6f70ba317fb8", "order": 1, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "a3448b1260c49b81", "type": "ui_tab", "name": "Temperature", "icon": "dashboard", "disabled": false, "hidden": false }, { "id": "024f6f70ba317fb8", "type": "ui_tab", "name": "Humidity", "icon": "dashboard", "disabled": false, "hidden": false } ] 

Una volta terminata l’operazione avrai importato l’intero flowchart con tutti i suoi script e potrai andare all’URL http://IP_RASPBERRY:1880/ui (quindi http://192.168.1.190:1880/ui) per vedere i gauge che mostrano la temperatura e l’umidità provenienti dai sensori.

Conclusioni

In conclusione, con questo progetto hai realizzato una rete mesh utilizzando moduli ESP32, capace di monitorare temperatura e umidità tramite sensori DHT22. I dati raccolti dai nodi vengono centralizzati in un nodo gateway, che li trasmette via MQTT a una dashboard Node-RED per la visualizzazione in tempo reale. Questo sistema ti permette di monitorare efficacemente diversi punti di un’area, sfruttando la flessibilità e la scalabilità offerte dalla rete mesh.

Durante il percorso, hai imparato a:

  • Configurare una rete mesh con ESP32: hai compreso l’importanza e l’uso del MESH_PREFIX per creare una rete isolata e sicura.
  • Utilizzare la libreria painlessMesh: hai sfruttato le capacità di gestione automatica delle connessioni e della topologia di rete, semplificando la comunicazione tra nodi.
  • Implementare un sistema di messaggistica MQTT: hai configurato e utilizzato la libreria PubSubClient per trasmettere dati da un nodo gateway a un broker MQTT.
  • Visualizzare i dati con Node-RED: hai creato e configurato una dashboard interattiva per monitorare i dati in tempo reale, utilizzando flussi di lavoro grafici che semplificano l’integrazione dei vari componenti del sistema.

Inoltre, hai esplorato l’importanza dei protocolli di comunicazione leggeri ed efficienti per le applicazioni IoT, acquisendo competenze pratiche su come integrare diverse tecnologie per creare un sistema robusto e scalabile. Questo progetto non solo ti offre una soluzione pratica per il monitoraggio ambientale, ma serve anche come base per ulteriori sviluppi e personalizzazioni. Hai scoperto come l’Internet of Things possa essere applicato a una vasta gamma di scenari per migliorare la raccolta e l’analisi dei dati.

Ora che hai una solida base, ci sono molte strade che puoi esplorare per ampliare questo progetto:

  1. Aggiungere altri sensori:
    • Sensori di gas: puoi integrare sensori per rilevare gas come CO2, metano o fumo, per monitorare la qualità dell’aria.
    • Sensori di movimento: aggiungi sensori PIR per rilevare il movimento, utile per applicazioni di sicurezza.
  2. Controllo remoto e automazione:
    • Attuatori: integra attuatori come relè o motori per controllare luci, serrature o sistemi di irrigazione, creando un sistema di automazione domestica.
    • Comandi vocali: collegati a servizi di assistente vocale come Amazon Alexa o Google Assistant per controllare il sistema tramite comandi vocali.
  3. Estendere la rete mesh:
    • Nodi aggiuntivi: aggiungi più nodi alla rete mesh per coprire aree più ampie, assicurandoti che la comunicazione rimanga stabile.
    • Reti multi-hop: implementa funzionalità di routing avanzate per creare reti mesh multi-hop, dove i dati passano attraverso più nodi prima di raggiungere il gateway.
  4. Visualizzazione e analisi avanzata dei dati:
    • Grafici e storici: utilizza Node-RED insieme a database come InfluxDB e strumenti di visualizzazione come Grafana per creare grafici storici e analisi dettagliate dei dati.
    • Notifiche e alert: configura Node-RED per inviare notifiche via email o SMS in caso di anomalie nei dati rilevati dai sensori.
  5. Integrazione con altri servizi IoT:
    • Piattaforme IoT: collegati a piattaforme IoT come ThingSpeak, Blynk o AWS IoT per sfruttare ulteriori funzionalità di elaborazione e analisi dei dati.
    • Interfacce web e mobile: sviluppa interfacce web o applicazioni mobile per monitorare e controllare il sistema da qualsiasi luogo.

Questi suggerimenti ti permetteranno di espandere le capacità del tuo sistema e di esplorare nuove applicazioni e funzionalità. La flessibilità di ESP32, insieme alla potenza di MQTT e Node-RED, ti offre un’ampia gamma di possibilità per innovare e creare soluzioni IoT sempre più avanzate e personalizzate. Buon lavoro!

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
0 0 votes
Valutazione articolo
guest
0 Commenti
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Torna in alto