MQTT è un protocollo applicativo di cui si è ampiamente discusso su antima.it in passato. Si è visto come sfruttarlo tramite Python3, usando paho-mqtt, e su piattaforme quali ESP8266 tramite la libreria PubSubClient.h.
L’implementazione di MQTT che verrà trattata qui di seguito è quella fornita da Eclipse Paho per il linguaggio di programmazione Go. La libreria può essere reperita all’interno dell’account Github dell’organizzazione, al seguente indirizzo.
Qui di seguito verrà realizzata una semplice applicazione che consiste in un Client MQTT che sottoscrive ad un topic e stampa i messaggi ricevuti da ogni dispositivo che pubblica sul topic, che può servire da spunto per iniziare ad approcciarsi alla libreria per costruire applicativi più complessi.
Setup dell’ambiente di lavoro
Si parte creando un progetto nella directory a cui punta la variabile GOPATH, che, di norma, è identificata dalla cartella dal nome go posizionata all’interno della directory utente (es. C:\Users\Utente\go in Windows, /home/utente/go in unix-like) ma che è possibile in ogni caso riconoscere eseguendo:
go env | grep GOPATH # Unix-like
go env | findstr /i GOPATH # Windows
A questo punto, si può procedere con il creare una cartella all’interno di GOPATH/src col nome del nostro progetto, per poi scaricare le dipendenze esterne, che per questo esempio sono composte dalla sola libreria paho.mqtt; ponendosi in GOPATH si può eseguire:
cd src
mkdir mqtt_demo
cd mqtt_demo
go mod init
go get github.com/eclipse/paho.mqtt.golang
Esempio di esecuzione del processo di inizializzazione del modulo
La libreria paho.mqtt.golang: utilizzare mqtt.Client
La libreria paho.mqtt.golang offre un meccanismo semplice per la gestione del traffico mqtt. Un’istanza di tipo mqtt.Client può essere costruita, associata ad un broker e fatta sottoscrivere ad una serie di topic, definendo delle funzioni che verranno invocate ogni volta che un messaggio viene ricevuto da uno specifico topic.
Il broker verrà eseguito sulla stessa macchina su cui questo programma girerà, sulla porta standard MQTT che è la 1883; se, ad esempio, si volesse chiamare identificare il client con l’id Main, si potrebbe scrivere:
const ( brokAddr = "localhost" port = 1883 hostId = "Main" topic = "test" ) func main() { ...
opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%v:%v", brokAddr, port)) opts.SetClientID(hostId) client := mqtt.NewClient(opts) connectionToken := client.Connect() if connectionToken.Wait() && connectionToken.Error() != nil { log.Fatal(connectionToken.Error()) } ...
}
Dove la funzione mqtt.NewClient viene utilizzata per inizializzare un nuovo Client con le opzioni specificate in ingresso. Queste opzioni permettono di definire delle caratteristiche interne del client, in questo caso ne sono state specificate due, partendo da un’istanza di mqtt.ClientOptions ottenuta invocando mqtt.NewClientOptions():
- Il broker a cui il client dovrà connettersi, tramite la funzione AddBroker, a cui dovrà essere fornita una stringa con l’indirizzo a cui reperire il broker; la formattazione passata in ingresso corrisponde alla stringa tcp://localhost:1883.
- L’identificatore del client, tramite la funzione SetClientId, per far sì che, ispezionando l’output del broker, sarà possibile individuare i messaggi in ingresso all’applicazione.
Una volta inizializzato, il client può essere connesso a mosquitto invocando Connect e monitorando il token di connessione rilasciato come ritorno. L’ultimo if ha una doppia funzione: quella di attendere che la connessione abbia successo e quella di, eventualmente, gestire un errore durante il processo, fermando l’applicazione e stampando che qualcosa è andato storto.
Subscribe e Callback
A questo punto, per effettuare la subscribe sul topic prescelto, basterà utilizzare Subscribe:
subscriptionToken := client.Subscribe(topic, 0, receiveCallback) if subscriptionToken.Wait() && subscriptionToken.Error() != nil { log.Fatal(subscriptionToken.Error()) }
I parametri di ingresso sono, rispettivamente, il topic, la qualità del servizio desiderata e la funzione da invocare qualora si ricevano messaggi MQTT sul topic specificato. Come per la connessione al broker, si usa lo stesso pattern per controllare che l’esito del processo di sottoscrizione.
Le funzioni chiamate all’arrivo di messaggi devono avere una firma particolare; qui di seguito viene riportato un esempio che stampa il topic di arrivo ed il payload del pacchetto:
func receiveCallback(client mqtt.Client, message mqtt.Message) {
fmt.Printf("Topic: %v\tPayload: %s\n", message.Topic(), message.Payload())
}
Come anticipato, la firma della funzione è tale da prendere in ingresso un’istanza di tipo mqtt.Client ed una di tipo mqtt.Message. La prima contiene informazioni sul Client in ricezione, la seconda sul messaggio in arrivo. Le informazioni sul topic e sul payload possono essere recuperate invocando Topic() e Payload() sulla variabile message e facendo attenzione ai tipi di ritorno (Payload() ritorna un dato di tipo []byte).
Compilazione ed esecuzione
Il codice completo è disponibile al seguente link; all’interno sono presenti tutti gli import utilizzati oltre ad una piccola aggiunta al main inclusa per far sì che il programma termini solo alla ricezione di un segnale di terminazione (CTRL+C).
Una volta salvato all’interno di un file, nella cartella del progetto, si può effettuare la compilazione de programma spostandosi con una finestra di terminale nella cartella di progetto ed esgeuendo:
go build
A questo punto il tutto è pronto. Si può testare il corretto funzionamento del programma utilizzando mosquitto e l’utility mosquitto_pub per simulare un publisher nella rete che pubblicherà messaggi sul topic test, eseguendo il nostro programma in una finestra:
./mqtt_test # Unix-like
mqtt_test.exe # Windows
il broker in una seconda istanza di terminale (con l’opzione -v verbose):
mosquitto -v
ed il publisher in una terza:
mosquitto_pub -t test -m funziona!
Output finale con tutte e tre le applicazioni in esecuzione
È tutto per questo articolo! Nel caso in cui aveste curiosità o dubbi sui contenuti non esitate a lasciare un commento nella sezione qui sotto. Potete anche contattarci mandando un messaggio tramite la pagina dei Contatti!