Playing with MQTT, Esp8266 and Rasberry
In questo articolo ti presento un po di metodologie diverse per utilizzare MQTT al fine di far comunicare device completamente diversi tra di loro.
Il codice sorgente relativo a questo articolo è pubblicato sul mio GitHub al seguente indirizzo: giemma/PlayWithMqtt (github.com)
Tutto nasce dal progetto 'DOMOTIZZARE' CASA CON ESP-01, RASPBERRY PI PICO W E SHELLY. In questo caso il server è un Raspberry Pi 2 sul quale è installato Window iot Core e quindi l'applicazione è di tipo "Windows Universal App".
Lo scopo principale del broker è consentire ai miei device di comunicare tra di loro. Cosa intendo per devices?
I devices sono:
- Esp8266 (in particolare esp01) i quali trasmettono dati come umidità, presenza e temperatura
- Raspberry Pi Pico W che invia lo stream di una videocamera
- Shelly Switch e Shelly Dimmer che fanno da attuatori per l'illuminazione
- Una mobile app (MAUI) per gestire sensori e attuatori
- Un sito web Blazor che potrebbe sostituire la mobile app
- Un Raspberry Pi 2 che fa da server ed è connesso al televisore che fa da dashboard
Vediamo quindi come realizzare un server MQTT in C# per Raspberry Pi 2 con Windows IOT Core.
UWP MQTT Server (Broker)
Il primo step è la creazione della solution e quindi del progetto UWP in Visual Studio.
Poi è necessario aggiungere la reference al pacchetto NuGet "MQTTNet":

Altra necessità è quella di modificare il "Manifest" della UWP in modo da dichiarare che l'applicazione può implementare un server:

Nella "MainPage" ho aggiunto semplicemente una ListBox che ha il compito di visualizzare tutti gli eventi che la classe MqttBroker.
Tale classe ha i seguenti metodi:
- Start_Server()
- StopServer()
e lancia i seguenti eventi:
- public event EventHandler<string> Error
- public event EventHandler<string> ServerStarted
- public event EventHandler<string> ClientConnected
Il metodo "Start" in questo caso non ha funzionalità particolari, ma è possibile aggiungere l'autenitcazione per i client, cambiare porta ecc .
Appena avviato il Broker si presenta come di seguito:

Installazione del broker MQTT su Raspberry Pi
Adsesso che l'app UWP è stata creata possiamo installarla sul Raspberry Pi.
In casa ho un Raspberry Pi 2 su cui ho installato Windows iot Core e un Raspberry Pi 4 con su Windows 11 ARM.
Eseguirò l'installazione su entrambi i dispositivi.
MQTT su Raspberry Pi 2 e Windows IOT Core
In questo caso ho un display da 3.5 pollici senza Touchscreen. L'ingresso del display è HDMI e per gestire l'app UWP ho la necessità di usare mouse e tastiera.
Il primo step è la creazione del pacchetto dell'app da installare.
Ciò è possibile cliccando con il tasto destro sul progetto "UWP_MQTT_Broker" -> "Publish" -> "Create App Packages"

Sarà poi necessario selezionare il certificato. Nel mio caso ho selezionato il certificato delle "Store".

Selezionare quindi l'architettura di destinazione e la configurazione da applicare:


Se tutto è andato per il verso giusto il pacchetto viene creato correttamente nel percorso indicato.
L'installazione dell'app sul Raspberry Pi 2 la eseguirò tramite "Windows Device Portal". Do per scontato che l'istallazione di Windows IOT Core sia già stata eseguita.
L'ip del Rasberry Pi 2 nel mio caso è 192.168.100.130 quindi Windows Device Portal è raggiungibile sull' url 192.168.100.130:8080.
La seziona relativa alle app è raggiungibile su 192.168.100.130:8080/#Apps%20manager
Clicca sul tasto "Choose file" e seleziona il pacchetto "UWP_MQTT_Broker_1.0.0.0_arm.msixbundle". Poi "Install".
Una piccola nota. Se è la prima volta che carichi un'applicazione siul tuo Raspberry dovrai importare anche i files contenuti nelle cartelle "Dependencies" -> "ARM".
Dopo qualche secondo, probabilmente anche un minuto, l'app dovrebbe comparire nell'elenco delle app installate:

Adesso è possibile avviare l'app sul Raspberry e testare se la comunicazione via MQTT funziona correttamente.
NOTA: Ricorda di cambiare gli IP del broker sia sull'ESP01 che sul Raspberry Pi Pico W.
Ecco il prototipo funzionante:


MQTT su Raspberry Pi 4 e Windows 11 ARM
In questo caso ho un display da 7 pollici con TouchScreen. L'ingresso del display è HDMI e per abilitare il touchscreen è necessario connettere il display anche via USB. Il display che ho utilizzato è della SUNFOUNDER.
Do per scontato che sia gia stata eseguita l' installazione di Windows 11 su Raspberry Pi 4.
Il primo step è naturalmente installare l'app.
Prima di tutto è necessario copiare tutto il pacchetto creato nel paragrafo precedente sul Raspberry Pi 4. Io ho utilizzato una chiavetta usb per far prima e ho copiato tutto il pacchetto nella cartella C:\Temp del Windows 11 ARM.
Basta un doppio click sul file "UWP_MQTT_Broker_1.0.0.0_arm.msixbundle" e partirà l'installazione. Il certificato sicuramente non potrà essere verificato quindi dovrà essere aggiunta un'eccezione.
La cosa più semplice che mi viene in mente è connettere un mouse e una tastiera al Raspberry pi 4 e agire direttamente su power shell remoto da avviare come amministratore.
Lanciare un cd <nome della cartella che contiene il pacchetto>
Poi .\install.ps1
Sembrerà tutto bloccato ma dopo un po di tempo dovremmo vedere qualcosa del tipo:

Sarà necessario attendere il completamento dell'operazione ma alla fine l'app sarà installata:

Nel mio caso, l'ip del Raspberry Pi 4 è 192.168.100.49.
NOTA: Ricorda di cambiare gli IP del broker sia sull'ESP01 che sul Raspberry Pi Pico W.
MQTT e ESP8266
Parto dal primo tipo di device in elenco ovvero l'ESp8266. In particolare ho deciso di utilizzare degli ESp01 per via del loro basso cosso e delle ristrette necessità che ho. Come noto, l'ESp8266 ha divesi pin di ingresso/uscita, ma a me ne serve praticamente uno solo di ingresso e zero di uscita (a meno che non voglia inserire un led per indicarmi lo stato).
Nella versione finale ho utilizzato un WiFiManager che mi consente di impostare la rete al primo avvio o di cambiare le impostazioni se l'ESP8266 non riesce a connettersi al WiFi, ma in questo articolo presento la versione con le impostazioni (nome rete e password) cablate nel codice).
A scopo didattico e al fine di dimostrare come l'ESP8266 contenuto nel modulo ESP01 possa sottoscriversi e pubblicare dei messaggi MQTT, mi pongo come obbiettivo un device che:
- All'accensione si connette al WiFi
- Si mette in ascolto sul topic "esp8266/device1/fromserver/" e:
- accende il led se riceve come payload "ON"
- spegne il led se riceve come payload "OFF"
- fa lampeggiare il led 3 volte se non comprende il comando
- Appena connesso invia un pacchetto sul topic "esp8266/device1/toserver/" con payload "OK" e lo fa ogni 30 secondi
Il codice è in "C" e come IDE utilizzo "Arduino IDE".
Per la configurazione di Arduino IDE per l'esp8266 ti consiglio questi articoli:
Collegamenti breadboard ESP01
Pinout ESP01
La piedinatura del modulo ESP01 è raffigurata di seguito.
L'immagine è fatta dall'alto (dal lato in cui è visibile l' ESP8266).

Wiring ESP01
I collegamenti sono i soliti:
- VCC è il positivo di alimentazione a 3.3 volts
- CH_PD da collegare al positivo di alimentazione
- GND al negativo
- e GPIO0 tramite una resistenza al diodo led
Ecco come appare il prototipo:

Nella breadboard, sulla parte destra compare un piccolo integrato. E' un AMS1117 è ha il compito di stabilizzare i 5 volt in ingresso a 3.3volt.
Il codice da caricare sull'ESP01
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
const char* ssid = "NOME RETE";
const char* password = "PASSWORD RETE";
const char* device_identifier = "device1";
const char* mqtt_ip = "192.168.99.145";
const int mqtt_port = 1883;
const char* mqtt_user = "";
const char* mqtt_password = "";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
pinMode(0, OUTPUT);
digitalWrite(0, HIGH);
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
client.setClient(espClient);
client.setServer(mqtt_ip, mqtt_port);
client.setCallback(callback);
String in_topic ="esp8266/" + String(device_identifier) + "/fromserver/";
client.subscribe(in_topic.c_str());
digitalWrite(0, LOW);
}
int packetNumber=0;
unsigned long lastSent=0;
void loop() {
i f (!client.connected()) {
reconnect();
String in_topic ="esp8266/" + String(device_identifier) + "/fromserver/";
client.subscribe(in_topic.c_str());
}
client.loop();
i f(millis() - lastSent>5000){
packetNumber++;
String topic ="esp8266/" + String(device_identifier) + "/toserver/";
//String p = "OK ";
//String payload = p + packetNumber;
String payload = "OK";
client.publish(topic.c_str(), payload.c_str(), false);
lastSent=millis();
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
i f (client.connect(device_identifier, mqtt_user, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
String topicStr = topic;
Serial.println(topicStr);
payload[length] = 0;
String recv_payload = String(( char *) payload);
i f(recv_payload == "ON"){
digitalWrite(0, HIGH);
Serial.println("On");
}else i f(recv_payload == "OFF"){
digitalWrite(0, LOW);
Serial.println("OFF");
}else{
Serial.println("UNKNOWN:");
Serial.println(recv_payload);
for (int i = 0; i < 3; i++) {
digitalWrite(0, HIGH);
delay(200);
digitalWrite(0, LOW);
}
}
Serial.println();
}
Penso che il codice sia abbastanza semplice e si commenta da solo.
MQTT e Raspberry Pi Pico W (Python)
Il secondo tipo di device è basato su un Raspberry Pi Pico W. L'utilizzo del Raspberry è giustificato dall'esigenza di avere un maggior numero di porte di ingresso/uscita. Avrei potuto utilizzare un Arduino Nano che comunica con l'ESp01 tramite software serial, ma così avrei avuto una maggiore complessita sia hardware che software oltre a due "codici" da caricare su due diversi dispositivi.
Inoltre sul Raspberry Pi Pico W ho la possibilità di scrivere in Python che conosco meno del C/C++ e lo reputo quindi un ottimo esercizio.
Collegamenti breadboard Raspberry Pi Pico W
Pinout Raspberry Pi Pico
Fare riferimento a : Raspberry Pi Pico W - Tips and Trick (iemma.it)
Wiring Raspberry Pi Pico W
I collegamenti del Raspberry Pi Pico W sono ancora più semplice del modulo ESP01.
L'alimentazione negativa è sul pin 38 (GND) mentre l'alimentazione positiva è sul pin 39 (VSYS).
Sul piendino numero 21 (GPIO16) ho invece collegato un led cosi come ho fatto per l' ESP01:

Il codice da caricare sul Raspberry Pi Pico W
import utime
import machine
import network
from lib.umqtt.simple import MQTTClient
SSID = "NOMERETE"
SSID_PASSWORD = "PASSWORDRETE"
CLIENT_ID = "Raspberry Pi Pico W"
MQTT_BROKER = "192.168.99.145"
SUBSCRIBE_TOPIC="rasp/device1/fromserver/"
def blink():
for x in range(5):
led.on()
utime.sleep_ms(100)
led.off()
utime.sleep_ms(100)
def do_connect_to_network():
try:
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
print('connecting to network...')
sta_if.active(True)
sta_if.connect(SSID, SSID_PASSWORD)
while not sta_if.isconnected():
print("Attempting to connect....")
utime.sle ep(1)
print('Connected! Network config:', sta_if.ifconfig())
return True
except:
return False
def sub_cb(topic, msg):
#print((topic, msg))
serialized = msg.decode('utf8', 'strict')
if serialized == "ON":
led.on()
elif serialized == "OFF":
led.off()
else:
print("Unknown command")
print(serialized)
def do_connect_to_broker():
global mqttClient,SUBSCRIBE_TOPIC
try:
mqttClient = MQTTClient(CLIENT_ID, MQTT_BROKER, keepalive=60)
mqttClient.set_callback(sub_cb)
mqttClient.connect()
print(SUBSCRIBE_TOPIC)
mqttClient.subscribe(SUBSCRIBE_TOPIC)
print(f"Connected to MQTT Broker :: {MQTT_BROKER}, and waiting for callback function to be called!")
return True
except:
return False
def reset():
print("Resetting...")
utime.sleep_ms(1000)
machine.reset()
try:
led = machine.Pin(16,machine.Pin.OUT)
blink()
print("Connecting to your wifi...")
if do_connect_to_network():
if do_connect_to_broker():
led.off()
else:
reset()
else:
reset()
led.off()
while True:
mqttClient.check_msg()
except:
reset()
Anche in questo caso il codice si commenta da solo
WebApp Blazor e Mobile app MAUI
Nei prossimi giorni inseriò anche la casistica per la mobile app MAUI e la parte web in Blazor.
Qui le cose si complicano un po perchè entra in gioco la rete pubblica e, specialmente per l'applicazione MAUI, ci sono dettagli non trascurabili da considerare
Buon divertimento!
Se hai voglia di lasciarmi un commento mi trovi su Linkedin.