IoT fai da te: ESP8266 e Raspberry Pi per monitorare temperatura e umidità tramite comunicazione MQTT

Introduzione

L’Internet delle cose (IoT) sta rivoluzionando il modo in cui interagiamo con il mondo che ci circonda. La comunicazione MQTT tra dispositivi diversi verrà illustrata in questo articolo tramite la realizzazione di un progetto impiegante delle ESP8266 e una Raspberry Pi utilizzate per monitorare temperatura e umidità ambientali.
Imparerai come costruire un sistema di monitoraggio della temperatura e dell’umidità che può essere facilmente adattato per soddisfare le tue esigenze specifiche.
Scoprirai come configurare e programmare un ESP8266 per raccogliere dati da dei sensori DHT22 e trasmetterli a un Raspberry Pi, che funge da broker MQTT. Inoltre, imparerai come sottoscrivere e gestire i messaggi MQTT ricevuti dal Raspberry Pi e come integrare ulteriori funzionalità nel tuo progetto IoT.
Se sei interessato a esplorare il mondo dell’IoT, a imparare a utilizzare l’ESP8266 e il Raspberry Pi, o semplicemente desideri creare un sistema di monitoraggio ambientale personalizzato, continua a leggere per scoprire come avviare questo emozionante progetto.

Cos’è MQTT?

MQTT (Message Queuing Telemetry Transport) è un protocollo di messaggistica leggero e flessibile progettato per la comunicazione tra dispositivi con restrizioni di larghezza di banda o potenza di calcolo. È particolarmente adatto per applicazioni IoT (Internet of Things) e M2M (Machine-to-Machine) in cui la conservazione di risorse è essenziale. MQTT è basato sul paradigma publish-subscribe, in cui i dispositivi possono pubblicare messaggi su topic specifici e sottoscriversi a topic per ricevere i messaggi pertinenti.

Se non sei confidente con questo argomento, potresti leggere l’articolo di introduzione al protocollo MQTT Introduzione all’ MQTT protocol in IoT: la piattaforma numero 1 per la comunicazione fra dispositivi.

Mosquitto come broker MQTT

  1. Definizione:
    • Mosquitto è un broker MQTT open-source sviluppato principalmente da Eclipse Foundation. È uno dei broker MQTT più popolari ed è disponibile per una vasta gamma di piattaforme, tra cui Linux, Windows e MacOS.
  2. Ruolo del Broker:
    • Un broker MQTT agisce come intermediario nella comunicazione tra i dispositivi che utilizzano MQTT. Accetta i messaggi pubblicati dai dispositivi e li instrada ai dispositivi sottoscritti ai rispettivi topic.
    • Mosquitto è il componente centrale in un sistema MQTT. I dispositivi client MQTT (chiamati anche publisher e subscriber) si connettono al broker per inviare e ricevere messaggi.
  3. Installazione e configurazione:
    • Mosquitto è facile da installare e configurare. Può essere eseguito su un Raspberry Pi, un server Linux, o qualsiasi altro dispositivo compatibile.
    • Il file di configurazione principale di Mosquitto è mosquitto.conf, in cui è possibile specificare impostazioni come la porta su cui ascoltare, i permessi di accesso, e altre opzioni di sicurezza.
  4. Sicurezza:
    • Mosquitto offre funzionalità di sicurezza per proteggere la comunicazione MQTT. Può essere configurato per richiedere autenticazione tramite nome utente e password e supporta il protocollo TLS/SSL per la crittografia dei dati.
  5. Utilizzo:
    • Mosquitto è ampiamente utilizzato nell’implementazione di sistemi IoT, automazione domestica, monitoraggio ambientale e molto altro ancora. È il cuore di molte applicazioni che richiedono una comunicazione affidabile tra dispositivi distribuiti.
  6. Comandi e strumenti:
    • Mosquitto fornisce una serie di strumenti da riga di comando per la gestione e il monitoraggio del broker MQTT, come mosquitto_pub per pubblicare messaggi e mosquitto_sub per sottoscrivere a topic MQTT.
  7. Community e supporto:
    • Mosquitto ha una comunità attiva di sviluppatori e utenti ed è supportato da documentazione dettagliata. È anche incluso nei repository di molte distribuzioni Linux, semplificando l’installazione e l’aggiornamento.

In sintesi, Mosquitto è un broker MQTT affidabile, scalabile e open-source che svolge un ruolo chiave nell’abilitare la comunicazione tra dispositivi nell’ambito dell’Internet delle cose e di altre applicazioni M2M (machine to machine). È una scelta popolare per implementare infrastrutture MQTT in progetti IoT e offre flessibilità e sicurezza per soddisfare una vasta gamma di esigenze di comunicazione.

Descrizione del sistema di monitoraggio ambientale IoT con comunicazione MQTT

Questo sistema di monitoraggio ambientale IoT combina diversi dispositivi e tecnologie per raccogliere, trasmettere e visualizzare dati in tempo reale sulla temperatura e l’umidità ambientale. Il sistema è composto da tre ESP8266, due sensori DHT22, un Raspberry Pi che funge da broker MQTT e un display LCD 16×2 per la visualizzazione dei dati. Ecco come funziona:

  1. ESP8266 con DHT22 (Sensori):
    • Ci sono due di questi dispositivi, ciascuno dotato di un sensore DHT22 per la misurazione della temperatura e dell’umidità. Essi sono posti in ambienti diversi per raccogliere i relativi dati.
    • Ogni ESP8266 è programmato per leggere i dati dal proprio sensore DHT22 a intervalli regolari.
    • I dati rilevati (temperatura e umidità) vengono pubblicati su due diversi topic MQTT separati, uno per ciascun ESP8266. Ad esempio, il primo ESP8266 può pubblicare i dati sul topic temp_hum_sens_1 mentre il secondo ESP8266 può pubblicare i dati sul topic temp_hum_sens_2
  2. Raspberry Pi (Broker MQTT):
    • Il Raspberry Pi funge da broker MQTT e svolge un ruolo centrale nel sistema.
    • Riceve i dati pubblicati dai due ESP8266 sui rispettivi topic MQTT.
    • Conserva i dati ricevuti in un buffer temporaneo prima di trasmetterli al terzo ESP8266.
    • Mantiene una connessione stabile con i due ESP8266 per garantire la ricezione continua dei dati.
  3. ESP8266 (Visualizzatore LCD):
    • Questo terzo ESP8266 è programmato per sottoscriversi ai due topic MQTT (“temp_hum_sens_1” e “temp_hum_sens_2”) per ricevere i dati di temperatura e umidità dai sensori.
    • Utilizza una libreria per il controllo di un display LCD 16×2 per visualizzare i dati in tempo reale.
    • Aggiorna il display con i dati di temperatura e umidità ogni volta che riceve un messaggio MQTT dai topic corrispondenti.

In questo sistema, i dati di temperatura e umidità vengono letti dai sensori DHT22 sugli ESP8266 e trasmessi tramite MQTT al Raspberry Pi, che funge da intermediario tra i sensori e il dispositivo di visualizzazione. Il terzo ESP8266 è in grado di visualizzare in tempo reale i dati ricevuti dai due sensori DHT22 su un display LCD 16×2, offrendo un’interfaccia di monitoraggio ambientale in tempo reale. Questo sistema è altamente personalizzabile e può essere espanso aggiungendo ulteriori sensori o funzionalità in base alle esigenze specifiche del progetto.

L’immagine seguente illustra il sistema completo:

Schema di funzionamento del sistema di monitoraggio ambientale basato su comunicazione MQTT
Schema di funzionamento del sistema di monitoraggio ambientale basato su comunicazione MQTT

Normalmente il display LCD 16×2 non viene fornito col connettore già saldato quindi, se non sai come procedere alla sua saldatura, ti rimando alla lettura dell’articolo Un altro tutorial su come saldare.

Come al solito, per la scrittura del firmware ci serviremo dell’ottimo IDE PlatformIO.

Di che componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

  • tre breadboard per connettere le NodeMCU ESP8266 agli altri componenti
  • alcuni fili DuPont (maschio – maschio, maschio – femmina, femmina – femmina)
  • due sensori DHT22
  • due resistori da 4.7kΩ
  • un potenziometro da 10kΩ
  • una SD card di capienza da 8GB a 32GB
  • un eventuale dongle WiFi USB per la Raspberry
  • un display LCD 16×2
  • una Raspberry PI
  • e, ovviamente, tre NodeMCU ESP8266 !

La SD card che ho usato per gli esperimenti è da 16GB.

Realizzazione del progetto

Lo schema elettrico

Riferendoci allo schema proposto prima, dovrai realizzare due circuiti uguali composti da una ESP8266 e un DHT22 ciascuno che fungeranno da sensori remoti e un terzo circuito composto da una ESP8266 e un display LCD 16×2 che utilizzerai per ricevere e visualizzare i dati collezionati dai sensori remoti. Dovrai poi mettere a punto una Raspberry PI seguendo le indicazioni date ai paragrafi successivi in modo da tirare su un broker MQTT.

Prima di realizzare i circuiti veri e propri diamo un’occhiata ai pinout della board e dei sensori:

Il pinout del NodeMCU ESP8266
Il pinout del NodeMCU ESP8266

In questo progetto sono utilizzati vari GPIO, alcuni per leggere i sensori altri per comandare il display LCD.

Useremo il GPIO D5 per leggere il sensore DHT22 (temperatura e umidità ambientali) in entrambi i circuiti sensori e i GPIO D2, D3, D5, D6, D7, D8 nel circuito ricevitore per pilotare il display.

Vediamo quindi i pinout del sensore DHT22 e del display LCD 16×2:

Pinout del DHT22
Pinout del DHT22
Pinout del display LCD 16x2

Pinout del display LCD 16×2

Il display si chiama 16×2 perché ha 2 righe da 16 caratteri ciascuna.

I suoi pin sono:

  • 1 VSS (collegato alla massa del circuito GND)
  • 2 VDD collegato all’alimentazione
  • 3 V0/VEE controllo del contrasto tramite potenziometro
  • 4 RS (Register Select) è un pin di controllo
  • 5 RW (Read/Write) è un pin di controllo
  • 6 E (Enable) è un pin di controllo
  • da 7 a 14 (da D0 a D7) pin di dati
  • 15 A (Anode) è l’anodo del LED interno per la retroilluminazione
  • 16 K (Katode) è il catodo del LED interno per la retroilluminazione

Vediamo ora lo schema di realizzazione dei due sensori realizzato con Fritzing:

Schema di realizzazione delle due board sensori
Schema di realizzazione delle due board sensori

Dovrai quindi realizzare due di questi circuiti identici.

Lo schema di realizzazione del ricevitore è qui sotto:

Schema di realizzazione della board ricevitore
Schema di realizzazione della board ricevitore

Anche se lo schema sembra complesso in realtà è abbastanza semplice. Per maggiore comprensione allego la tabella dei collegamenti del display:

DISPLAY LCD 16×2ESP8266
1 – VSSGND
2 – VDD3V3
3 – V0/VEEcursore potenziometro
4 – RSD2
5 – RWGND
6 – ED3
7 – D0non collegato
8 – D1non collegato
9 – D2non collegato
10 – D3non collegato
11 – D4D5
12 – D5D6
13 – D6D7
14 – D7D8
15 – AVin
16 – KGND
Tabella collegamenti del display

Il potenziometro è usato per poter regolare il contrasto del display.

Gli sketch

Creiamo il progetto PlatformIO per i due trasmettitori-sensori

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

Delle librerie indicate installa solo la libreria DHT sensor library for ESPx by Bernd Giesecke.

Installa poi la libreria PubSubClient by Nick O’Leary come indicato nella foto seguente:

Installa la libreria PubSubClient
Installa la libreria PubSubClient

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

monitor_speed = 115200
upload_speed = 921600

in modo che abbia un aspetto del genere:

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	knolleary/PubSubClient@^2.8
	beegee-tokyo/DHT sensor library for ESPx@^1.19

Ovviamente puoi scaricare il progetto dal link seguente:

Sostituisci il file main.cpp del progetto che hai creato con quello presente nel file zip.

Questo stesso sketch lo userai per i due dispositivi sensori (cioè i due col DHT22) e per questo motivo lo dovrai adattare leggermente.

In particolare dovrai adattare per ciascuno il topic e il nome del client.

Supponendo di numerare i due dispositivi sensori rispettivamente con 1 e 2:

  • per il dispositivo 1 il topic (alla riga 16) sarà temp_hum_sens_1 mentre il client (alla riga 65) sarà ESP8266Client_1
  • per il dispositivo 2 il topic (alla riga 16) sarà temp_hum_sens_2 mentre il client (alla riga 65) sarà ESP8266Client_2

Carica quindi il firmware nel dispositivo 1 e nel dispositivo 2 con le modifiche indicate qui sopra.

Vediamo ora come funziona lo sketch.

Inizialmente vengono incluse le librerie necessarie:

#include <Arduino.h>

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "DHTesp.h"

Successivamente impostiamo le credenziali per la nostra rete WiFi:

// Set up your Wi-Fi credentials
const char* ssid = "my_wifi_SSID";
const char* password = "my_wifi_password";

Sostituisci quindi le stringhe my_wifi_SSID e my_wifi_password con, rispettivamente, l’SSID e la password della tua rete wifi.

A questo punto vengono settati i parametri per il collegamento MQTT:

// Set up your MQTT information
const char* mqtt_server = "192.168.1.190";      // set the IP address of the Raspberry 
const int mqtt_port = 1883;
const char* mqtt_user = "pippo";                // set the user of the mqtt server 
const char* mqtt_password = "pluto";            // set the password of the mqtt server 
const char* mqtt_topic = "temp_hum_sens_1";     // set the topic

Il primo parametro, mqtt_server, è l’indirizzo IP del broker MQTT che gira sulla Raspberry. Vedremo nei paragrafi sulla Raspberry come settare questo parametro.

Il secondo, mqtt_port, è la porta di comunicazione dove il broker MQTT “ascolta” i messaggi.

Il terzo e il quarto parametro, mqtt_user e mqtt_password, sono le credenziali di accesso all’MQTT. Se un dispositivo non dispone di queste esatte credenziali non si può registrare sul broker MQTT e gli viene inibito l’accesso. Vedremo nel paragrafo riguardante la Raspberry come impostare questi parametri di accesso sul broker MQTT.

L’ultimo parametro, mqtt_topic, è il topic a cui si registra il dispositivo e sarà, come detto sopra, temp_hum_sens_1 per il dispositivo 1 e temp_hum_sens_2 per il dispositivo 2.

Nelle righe successive viene definito l’oggetto dht che gestisce il DHT22, viene definito il GPIO di comunicazione per il DHT22 e vengono definiti i client per la connessione WiFi e per la connessione all’MQTT. Segue poi il prototipo della funzione setup_wifi():

// Set the DHT22 sensor pin
DHTesp dht;
#define  DHT22_PIN 14 

WiFiClient espClient;
PubSubClient client(espClient);

void setup_wifi();

Incontramo poi una funzione che rimuove l’ultimo carattere di una stringa data e che qui usiamo per eliminare la seconda cifra decimale nei valori di temperatura e umidità:

String removeLastCharacter(String input) {
  // Checks whether the string has at least one character
  if (input.length() > 0) {
    // Use substring() to get a substring excluding the last character
    return input.substring(0, input.length() - 1);
  } else {
    // The string is empty, return an empty string
    return "";
  }
}

Troviamo poi la funzione setup che inizializza la porta seriale, attiva il WiFi con la chiamata alla funzione setup_wifi(), connette il client MQTT al server e connette il sensore DHT22 al GPIO 14:

void setup() {
  Serial.begin(115200);
  delay(2000);  
  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);  
  dht.setup(DHT22_PIN, DHTesp::DHT22); // Connect DHT sensor to GPIO 14 
}

Segue poi la succitata funzione setup_wifi() che si occupa di gestire la connessione alla rete wireless:

void setup_wifi() {
  delay(10);
  // Connect to Wi-Fi
  Serial.println();
  Serial.print("Connection to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
}

La funzione successiva controlla se la connessione MQTT è in piedi e, nel caso non lo fosse, cerca di ristabilirla. Se non le è possibile stabilire la connessione MQTT restituisce un messaggio di errore. Nota che, come argomento del blocco if , è presente il nome del client che, come già accennato sopra, sarà ESP8266Client_1 per il dispositivo 1 e ESP8266Client_2 per il dispositivo 2.

void reconnect() {
  while (!client.connected()) {
    Serial.print("Connecting to MQTT...");
    if (client.connect("ESP8266Client_1", mqtt_user, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("error, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

Conclude lo sketch la funzione loop che esaminiamo nel dettaglio.

Inizialmente viene controllato lo stato della connessione MQTT e, se è il caso, viene ripristinato:

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

Vengono poi letti i valori di temperatura e umidità dal DHT22 e controllato che siano validi:

// Read data from DHT22
float temperature = dht.getTemperature();
float humidity = dht.getHumidity();

// Check if the reading is valid
if (isnan(temperature) || isnan(humidity)) {
Serial.println("Error reading the DHT22 sensor!");
return;
}

Viene eliminata la seconda cifra decimale dai valori appena letti:

String temperature_mod = removeLastCharacter(String(temperature));
String humidity_mod = removeLastCharacter(String(humidity));

Infine viene creato il messaggio da inviare (payload) concatenando i due valori di temperatura e umidità con una virgola, viene stampato sul Serial Monitor (a solo scopo di debug) e viene poi spedito al server. Segue una pausa di 5 secondi prima di ricominciare il loop.

Modificando il valore di tale pausa si può regolare la tempistica di invio del messaggio. Tale valore non può comunque essere inferiore ai 2 secondi (che è il tempo necessario al DHT22 per fare una rilevazione):

String payload = temperature_mod + "," + humidity_mod;
Serial.println(payload);
client.publish(mqtt_topic, payload.c_str());

// Please wait before reading again
delay(5000);  // Please wait 5 seconds before your next reading

Creiamo il progetto PlatformIO per il dispositivo ricevitore

Crea un progetto PlatformIO come descritto nell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO.

Installa la libreria PubSubClient by Nick O’Leary come già fatto precedentemente.

Poi installa la libreria LiquidCrystal by Arduino come indicato nella foto seguente:

Installa la libreria LiquidCrystal
Installa la libreria LiquidCrystal

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

monitor_speed = 115200
upload_speed = 921600

in modo che abbia un aspetto del genere:

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	knolleary/PubSubClient@^2.8
	arduino-libraries/LiquidCrystal@^1.0.7

Ovviamente puoi scaricare il progetto dal link seguente:

Sostituisci il file main.cpp del progetto che hai creato con quello presente nel file zip.

Vediamo ora come funziona lo sketch.

Inizialmente vengono incluse le librerie necessarie:

#include <Arduino.h>

#include <LiquidCrystal.h>

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

La parte successiva esegue le stesse impostazioni dell’altro sketch:

// Set up your Wi-Fi credentials
const char* ssid = "my_wifi_SSID";
const char* password = "my_wifi_password";

// Set up your MQTT information
const char* mqtt_server = "192.168.1.190";
const int mqtt_port = 1883;
const char* mqtt_user = "pippo";
const char* mqtt_password = "pluto";

Vengono poi definite le variabili che conterranno i dati ricevuti:

String temperature_topic_1 = "";
String humidity_topic_1 = "";
String temperature_topic_2 = "";
String humidity_topic_2 = "";

Vengono definiti i due topic a cui il ricevitore si registrerà:

#define TOPIC1 "temp_hum_sens_1"
#define TOPIC2 "temp_hum_sens_2"

Infatti il ricevitore, dovendo ricevere i dati inviati da entrambi i sensori, dovrà registrarsi ad entrambi i topic.

Viene poi collegato il display alla ESP8266 e creato l’oggetto lcd che lo gestisce. Successivamente vengono creati i client per la gestione del WiFi e dell’MQTT:

const int RS = D2, EN = D3, d4 = D5, d5 = D6, d6 = D7, d7 = D8;   
LiquidCrystal lcd(RS, EN, d4, d5, d6, d7);

WiFiClient espClient;
PubSubClient client(espClient);

Vengono poi definite le icone del termometro e della goccia d’acqua che compariranno sul display:

byte temp[8] = 						//icon for thermometer
{
    B00100,
    B01010,
    B01010,
    B01110,
    B01110,
    B11111,
    B11111,
    B01110
};

byte drop[8] = 						//icon for water droplet
{
    B00100,
    B00100,
    B01010,
    B01010,
    B10001,
    B10001,
    B10001,
    B01110,
};

Viene poi implementata una funzione che serve a splittare una data stringa su un carattere separatore (ci servirà per splittare il messaggio ricevuto usando come carattere la “,” per separare il valore della temperatura da quello dell’umidità):

void splitString(const String &input, char delimiter, String *output, int &outputSize) {
  outputSize = 0; // Initialize the size of the output array to zero

  int start = 0; // Starting index of each substring

  // Scan the input string
  for (int i = 0; i < input.length(); i++) {
    if (input.charAt(i) == delimiter) {
      // Once you find a delimiter character, add the substring to the output array
      output[outputSize++] = input.substring(start, i);
      start = i + 1; // Sets the starting index for the next substring
    }
  }

  // Add last substring (or whole string if there is no delimiter at the end)
  if (start < input.length()) {
    output[outputSize++] = input.substring(start);
  }
}

C’è poi la funzione setup_wifi che gestisce la connessione WiFi:

void setup_wifi() {
  delay(10);
  // Connect to Wi-Fi
  Serial.println();
  Serial.print("Connection to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  lcd.print("Connecting WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
}

e la funzione reconnect che si occupa della connessione o riconnessione al broker MQTT e della sottoscrizione ai due topic:

void reconnect() {
  while (!client.connected()) {
    Serial.print("I try to connect to MQTT...");
    if (client.connect("ESP8266Client_3", mqtt_user, mqtt_password)) {
      Serial.println("connected");
      client.subscribe(TOPIC1);
      client.subscribe(TOPIC2);
    } else {
      Serial.print("error, rc=");
      Serial.print(client.state());
      Serial.println(" I'll try again in 5 seconds");
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("MQTT not");
      lcd.setCursor(0, 1);
      lcd.print("connected...");
      delay(5000);
    }
  }
}

Segue poi la funzione callback che viene settata nella funzione setup e che viene automaticamente chiamata dalle funzioni della libreria PubSubClient ogni volta che arriva un messaggio da uno dei due topic. Questa funzione estrae i messaggi (payload) provenienti dai due topic e li splitta per riempire le variabili temperature_topic_1, humidity_topic_1, temperature_topic_2 e humidity_topic_2 che poi verranno visualizzate sul display:

void callback(char* topic, byte* payload, unsigned int length) {
  if (strcmp(topic, TOPIC1) == 0) {
      String message_topic_1 = "";
      for (unsigned int i = 0; i < length; i++) {
        message_topic_1 += (char)payload[i];
      }

      String outputArray[2]; 
      int outputSize = 0;
      char delimiter = ',';
      splitString(message_topic_1, delimiter, outputArray, outputSize);
      temperature_topic_1 = outputArray[0];
      humidity_topic_1 = outputArray[1];

  } else if (strcmp(topic, TOPIC2) == 0) {
    String message_topic_2 = "";
    for (unsigned int i = 0; i < length; i++) {
      message_topic_2 += (char)payload[i];
    }

    String outputArray[2]; 
    int outputSize = 0;
    char delimiter = ',';
    splitString(message_topic_2, delimiter, outputArray, outputSize);
    temperature_topic_2 = outputArray[0];
    humidity_topic_2 = outputArray[1];

  }   
}

La funzione setup inizializza la porta seriale, le icone del termometro e della goccia d’acqua, il display e chiama la funzione setup_wifi per connettersi al Wifi. Inoltre collega il client al server e registra (come già accennato) la funzione callback. Poi stampa un messaggio sul display:

void setup() {
  Serial.begin(115200);
  delay(2000); 
  lcd.createChar(1,temp);				// create a new character labeled 1
  lcd.createChar(2,drop);				// create a new character labeled 2
  lcd.begin(16, 2);
  setup_wifi();

  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);

  // Print a message to the LCD.
  lcd.clear();
  lcd.print("Device connected");
  delay(5000); 
  lcd.clear();
}

Lo sketch termina con la funzione loop che vediamo in particolare.

Inizialmente controlla se la board è connessa all’MQTT e, in caso contrario, cerca di riconnetterla:

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

Poi punta il cursore del display sulla posizione (0,0) cioè nell’angolo in alto a sinistra e comincia a scrivere i dati e i simboli relativi al sensore 1:

lcd.setCursor(0, 0);

lcd.print("1");
lcd.print(" ");
lcd.write(1);
lcd.print(temperature_topic_1);
lcd.print((char)223);
lcd.print("C ");
lcd.write(2);						// Print the new character labeled 2
lcd.print(humidity_topic_1);
lcd.print("%");

Infine punta il cursore del display sulla posizione (0,1) cioè nell’angolo in basso a sinistra e comincia a scrivere i dati e i simboli relativi al sensore 2:

lcd.setCursor(0, 1);

lcd.print("2");
lcd.print(" ");
lcd.write(1);
lcd.print(temperature_topic_2);
lcd.print((char)223);
lcd.print("C ");
lcd.write(2);						// Print the new character labeled 2
lcd.print(humidity_topic_2);
lcd.print("%");

Vediamo nella foto successiva una visione d’insieme dei dispositivi:

Visione d'insieme dei dispositivi
Visione d’insieme dei dispositivi

Alla sinistra possiamo vedere la Raspberry col dongle per il WiFi dotato di antenna (è collegata anche una telecamera che però non è usata in questo progetto), al centro vediamo, a partire dall’alto, i due dispositivi trasmittenti dotati di sensore DHT22 e il dispositivo ricevente dotato di display LCD 16×2 e potenziometro per la regolazione del contrasto.

Prima di passare alla Raspberry, carica gli sketch sulle 3 board con ESP8266 e tienile alimentate (magari con dei power bank). Se tutto è andato a buon fine, i tre dispositivi si collegheranno alla rete WiFi e cercheranno di registrarsi al broker MQTT che, però, ancora non esiste. Quindi per il momento non potranno ancora comunicare. Questo fatto ci verrà segnalato anche sul display del dispositivo ricevitore con un messaggio che ci dice che l’MQTT non è connesso.

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.

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

Puoi scaricare l’immagine che ho usato io dal link qui sotto:

Altrimenti, se desideri l’ultima versione, vai 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’ESP8266.

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. Il broker Mosquitto può essere configurato per richiedere nome utente e password quando il client si connette al broker. Questo paragrafo mostra come impostare l’autenticazione con nome utente e password per il broker Mosquitto su Raspberry Pi. Per esempio useremo la parola pippo come nome utente e la parola pluto come password.

Possiamo creare il file della password utilizzando lo strumento mosquitto_passwd.

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).

Una volta acquisiti i privilegi di root digita il seguente comando:

mosquitto_passwd -c /etc/mosquitto/credentials pippo

Ti verrà chiesta due volte la password da associare all’utente pippo che, nel nostro caso, è pluto.

Il comando ha creato nella cartella /etc/mosquitto/ un file di nome credentials contenente il nome utente (pippo) e la password (pluto) criptata. A questo punto devi modificare il file di configurazione di Mosquitto in questo modo. Dai il comando:

nano /etc/mosquitto/mosquitto.conf

e aggiungi alla fine queste tre righe:

listener 1883
allow_anonymous false
password_file /etc/mosquitto/credentials

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 da una delle ESP8266 (verso il topic temp_hum_sens_1 o il topic temp_hum_sens_2) 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 a un topic MQTT specifico e visualizzare i messaggi in arrivo:
mosquitto_sub -h IP_ADDRESS_RASPBERRY_PI -t temp_hum_sens_1 -u mqtt_user -P mqtt_password

dove

  • IP_ADDRESS_RASPBERRY_PI: sostituiscilo con l’indirizzo IP del tuo Raspberry Pi.
  • temp_hum_sens_1: è il nome del topic MQTT a cui la tua ESP8266 sta pubblicando i dati (una delle due ESP8266).
  • mqtt_user e mqtt_password: sostituiscili con le credenziali MQTT del tuo broker se sono necessarie per la sottoscrizione.

Una volta eseguito il comando, dovresti vedere i messaggi MQTT in arrivo nel terminale ogni volta che la tua ESP8266 pubblica dati sul topic specificato. I messaggi visualizzati includeranno la temperatura e l’umidità inviate dalla ESP8266.

Se provi a dare il comando senza inserire mqtt_user e/o mqtt_password o inserendole sbagliate, Mosquitto ti risponde col messaggio “Connection Refused: not authorised.

Vediamo il progetto in funzione: ecco un video del ricevitore in funzione

Siamo arrivati praticamente alla fine. Le tre ESP8266 sono connesse alla rete WiFi e cercano di registrarsi sul broker MQTT che gira sulla Raspberry. Appena il broker inizia a funzionare, i tre dispositivi si registreranno ed inizieranno a trasmettere e ricevere i dati.

Il video seguente ti mostrerà il mio dispositivo ricevitore in funzione:

Nell’esperimento il sensore 1 si trovava nel cortile della casa mentre il sensore 2 si trovava all’interno.

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
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Torna in alto