Applicazione relativa al Web-IO
Web-client MQTT con JavaScript
come interfaccia personalizzata per Web-IO
In questo tutorial viene utilizzato il client JavaScript dal progetto Eclipse Paho, un’interfaccia basata sul web per il Web-IO 4.0 digitale.
Qui vengono memorizzati temporaneamente gli stati del Web-IO come retained message dal broker e raffigurati dal web client su una semplice tabella HTML.
Se si clicca una delle uscite, il web client invia un publish MQTT che ordina al Web-IO di attivare la relativa uscita.
Struttura del client
Il Web client fornisce un’interfaccia utente per un Web-IO distante. La comunicazione tra Web-IO e Web-client avviene tramite il protocollo IoT MQTT.
Il client si compone dei tre file:
- webio.html
- ui.js
- mqtt.js
Comunicazione tra web-client e web-IO
Nella rappresentazione a fianco della comunicazione tra Web-IO e Web-client, per semplicità si evita di usare il broker .
La comunicazione avviene attraverso i topics mywebio/<io>/status, attraverso il quale il Web-IO pubblica lo stato di attivazione attuale e mywebio/<io>/set, attraverso il quale la Web-Interface invia una richiesta di attivazione al Web-IO.
1 Dopo che il web client si è collegato, riceve gli stati delle singole entrate e uscite come retained message e li riproduce in una tabella HTML.
2 Non appena l’utente clicca su un’uscita (qui output 0), il web-client scarica un publish con il payload "ON" e il topic "mywebio/output0/set". La richiesta non confermata viene visualizzata nel web-client per mezzo di un colore più tenue.
3 Il Web-IO reagisce alla ricezione della subscription, attivando la relativa uscita. Poi invia un publish con il nuovo stato come payload e il topic "mywebio/output0/status". Non appena il Web-client ha ricevuto la notizia, raffigura l’uscita di nuovo a colori.
Nota: In caso di varie richieste di modifica consecutive senza una conferma del Web-IO la relativa uscita viene colorata in grigio e nuovamente attivata, quando il Web-IO ha pubblicato il suo stato attuale.
Preparazione: Configurazione del broker e del Web-IO
Definire utenti e diritti di accesso
Per questo tutorial viene impiegato, come già per il tutorial box-2-box via MQTT, il broker gratuito per piccole applicazioni cloudmqtt.com.
Per semplicità viene creato un unico utente con ampi diritti:
nome utente | password | diritti di scrittura | diritti di lettura |
---|---|---|---|
webio | Super$icher123 | # | # |
Impostazioni di base per MQTT
Il Web-IO viene configurato sulla pagina "canali di comunicazione >> MQTT" per l’interazione con il broker.
1 Attivare innanzitutto MQTT.
2 Inserire il nome utente e la password.
3 Indicare le informazioni di collegamento per il broker.
Creare azioni per l’attivazione delle uscite
Aprite ora la pagina "Azioni" e cliccate su "Aggiungi", per creare le azioni per la ricezione di una subscription.
1 Attivare l’azione e darle un nome sensato.
2 Il trigger è la ricezione di una subscription MQTT con il percorso topic mywebio/output0/set e il testo topic ON.
3 Se si verifica il trigger, il Web-IO deve commutare un output, in questo caso output 0 su "ON".
Ripetete l’ultimo passo, fino a quando avrete definito complessivamente quattro azioni: output 0 su ON, output 0 su OFF, output 1 su ON e output 1 su OFF.
Creare azioni per il publish di stati IO
Nell’ultimo passo della configurazione vengono realizzate le azioni, che scaricano un publish MQTT in caso di cambio di stato in un’entrata o uscita qualsiasi.
1 Mettete l’azione su attivo e assegnatele un nome sensato.
2 Selezionare come trigger input e selezionate un cambio in OFF per Input0.
3 Come azione si deve scegliere un publish MQTT.
4 Il topic per la pubblicazione di un nuovo stato è "mywebio/input0/status".
5 Il payload (testo topic) per un cambio ad OFF è "OFF".
6 Il testo topic clear è la notizia che viene inviata quando non è più presente il trigger ovvero "ON".
Ripetete l’impostazione dell’azione per l’altra entrata e per le due uscite.
Implementazione del web-client
Qui di seguito verranno illustrati nel dettaglio i tre file webio.html, mqtt.js e ui.js. Il file HTML contiene solo elementi di realizzazione di base, per questo se ne parla solo brevemente. Segue una discussione approfondita sui due file JavaScript.
webio.html: La rappresentazione grafica
<!doctype html>
<html lang="de">
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js"></script>
<script src="webio_mqtt.js" type="text/javascript"></script>
<script src="ui.js" type="text/javascript" defer></script>
<meta charset="utf-8">
<title>My Web-IO</title>
<style type="text/css">
body {font-family: sans-serif;}
table {empty-cells: show;}
.on {background-color: #00FF00;}
.off {background-color: #FF0000;}
.set_on {background-color: #CCFFCC;}
.set_off {background-color: #FFCCCC;}
.unknown {background-color: #DDDDDD;}
.on:before, .set_on:before {content: "ON";}
.off:before, .set_off:before {content: "OFF";}
.unknown:before {content: "?";}
#output0,#output1{cursor: pointer;}
</style>
</head>
<body>
<h1>My Web-IO</h1>
<table>
<tr>
<th></th>
<th>Input</th>
<th>Output</th>
</tr>
<tr>
<td >[0]</td>
<td class="unknown" id="input0"></td>
<td class="unknown" id="output0"></td>
</tr>
<tr>
<td >[1]</td>
<td class="unknown" id="input1"></td>
<td class="unknown" id="output1"></td>
</tr>
</table>
</body>
</html>
- Righe 4-6:
Integrazione del client JavaScript PAHO. URL alternativo: https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js - Righe 7 e 8:
Integrazione dei file JS locali. L’attributo defer garantisce che le funzioni che prendono elementi HTML, vengano eseguite solo quando è stata caricata l’intera pagina. - Righe 12-24:
Informazioni style per i diversi stati. Oltre all’assegnazione di un colore, l’elemento viene fatto precedere dal testo ON, OFF oppure ?. - Righe 29-45:
La tabella che indica entrate e uscite. Le singole entrate e uscite vengono qui rappresentate da celle di tabelle, alle quali viene assegnata una classe CSS per la rappresentazione. Gli elementi vengono identificati tramite ID che sono denominati esattamente come le entrate e le uscite del Web-IO.
mqtt.js: Il vero client MQTT
Il file mqtt.js contiene le funzioni per stabilire una connessione con il broker MQTT e per ricevere e inviare messaggi.
/*
* MQTT-WebClient example for Web-IO 4.0
*/
var hostname = "m21.cloudmqtt.com";
var port = 37719;
var clientId = "webio4mqttexample";
clientId += new Date().getUTCMilliseconds();;
var username = "webclient";
var password = "Super$icher123";
var subscription = "mywebio/+/status";
mqttClient = new Paho.MQTT.Client(hostname, port, clientId);
mqttClient.onMessageArrived = MessageArrived;
mqttClient.onConnectionLost = ConnectionLost;
Connect();
/*Initiates a connection to the MQTT broker*/
function Connect(){
mqttClient.connect({
onSuccess: Connected,
onFailure: ConnectionFailed,
keepAliveInterval: 10,
userName: username,
useSSL: true,
password: password});
}
/*Callback for successful MQTT connection */
function Connected() {
console.log("Connected");
mqttClient.subscribe(subscription);
}
/*Callback for failed connection*/
function ConnectionFailed(res) {
console.log("Connect failed:" + res.errorMessage);
}
/*Callback for lost connection*/
function ConnectionLost(res) {
if (res.errorCode !== 0) {
console.log("Connection lost:" + res.errorMessage);
Connect();
}
}
/*Callback for incoming message processing */
function MessageArrived(message) {
console.log(message.destinationName +" : " + message.payloadString);
switch(message.payloadString){
case "ON":
displayClass = "on";
break;
case "OFF":
displayClass = "off";
break;
default:
displayClass = "unknown";
}
var topic = message.destinationName.split("/");
if (topic.length == 3){
var ioname = topic[1];
UpdateElement(ioname, displayClass);
}
}
Valori di configurazione e realizzazione di una connessione
Innanzitutto vengono posti un paio di valori di configurazione:
var hostname = "m21.cloudmqtt.com";
var port = 37719;
var clientId = "webio4mqttexample";
clientID += new Date().getUTCMilliseconds();
var username = "webclient";
var password = "Super$icher123";
var subscription = "mywebio/+/status";
- Righe 4 e 5:
Le informazioni di connessione per il broker MQTT - Riga 6:
Un’unica stringa che identifica il client che interviene. - Riga 7:
L’avvisonew Date().getUTCMilliseconds()
fa in modo che più istanze del client possano essere aperte contemporaneamente, aggiungendo un marcatempo attuale alla ID client. In base allo standard il broker interromperebbe diverse connessioni con la stessa ID client. - Righe 8 e 9:
Dati login dell’utente - Riga 10:
Il topic su cui il Web-IO pubblica i suoi stati.
Segue l’inizializzazione del client:
mqttClient = new Paho.MQTT.Client(hostname, port, clientId);
mqttClient.onMessageArrived = MessageArrived;
mqttClient.onConnectionLost = ConnectionLost;
Connect();
- Riga 12:
Inizializzazione del client MQTT con il nome dell’host del broker, la porta e l’ID client unica. - Riga 13:
MessageArrived()
è la funzione di callback che viene attivata alla ricezione di una subscription. - Riga 14:
ConnectionLost()
viene attivata nel caso di un’interruzione della connessione. - Riga 15:
Con Connect viene stabilita la connessione.
Questa funzione Connect()
è responsabile di stabilire una connessione con il broker MQTT. L’oggetto della configurazione contiene oltre ai parametri già indicati, anche le funzioni di callback per una realizzazione della connessione riuscita o fallita.
/*Initiates a connection to the MQTT broker*/
function Connect(){
mqttclient.connect({
onSuccess: Connected,
onFailure: ConnectionFailed,
keepAliveInterval: 10,
userName: username,
useSSL: true,
password: password
});
}
- Riga 19:
La connessione attiva il metodoconnect()
del client MQTT. - Riga 20:
Dopo che la connessione è riuscita, viene eseguita la funzioneConnected()
MQTT. - Riga 21:
Dopo che la connessione è fallita, viene attivataConnectionFailed()
. - Riga 24:
La comunicazione viene criptata tramite TLS.
Le funzioni callback
Nella prima sezione sono state citate quattro funzioni di callback: MessageArrived()
e ConnectionLost()
sono state poste come attributo del client MQTT Connected()
e ConnectionFailed()
sono state assegnate alla funzione Connect()
nell’oggetto di configurazione.
Connected()
viene eseguito se la connessione è riuscita.
/*Callback for successful MQTT connection */
function Connected() {
console.log("Connected");
mqttclient.subscribe(subscription);
}
- Riga 31:
Lo stato della connessione viene indicato tramite la consolle JavaScript. - Riga 32:
Dato che si tratta di una connessione persistente, il topic deve abbonarsi nuovamente alle notizie del Web-IO ogni volta che stabilisce una connessione.
Ogni volta che fallisce una connessione viene emessa ConnectionFailed()
un’informazione sulla consolle JavaScript.
/*Callback for failed connection*/
function ConnectionFailed(res) {
console.log("Connect failed:" + res.errorMessage);
}
In caso di interruzione della connessione ConnectionLost()
viene attivato.
/*Callback for lost connection*/
function ConnectionLost(res) {
if (res.errorCode !== 0)
{
console.log("Connection lost:" + res.errorMessage);
Connect();
}
}
- Riga 43:
Tramite la consolle JavaScript vengono fornite informazioni sulla creazione di una connessione. - Riga 44:
Dopodiché viene stabilita una nuova connessione.
La funzione più completa è MessageArrived()
. La parte superiore della funzione valuta la stringa PayLoad. Con questa viene determinata la classe CSS che viene assegnata al relativo elemento.
/*Callback for incoming message processing */
function MessageArrived(message) {
switch(message.payloadString){
case "ON":
displayClass = "on";
break;
case "OFF":
displayClass = "off";
break;
default:
displayClass = "unknown";
}
- Riga 50:
L’oggetto del messaggio presenta un attributopayloadString
contenente il contenuto del messaggio trasmesso. - Righe 51-56::
Se il contenuto delle notizie è "ON", la Variable displayClass viene messa su "on". Per il PayLoad "OFF" su "off". - Righe 57-58:
In caso di payload difettoso la classe display viene messa su "unknown".
La parte inferiore della funzione determina quale input e output debbano essere aggiornati:
var topic = message.destinationName.split("/");
if (topic.length == 3){
var ioname = topic[1];
UpdateElement(ioname, displayClass);
}
}
- Riga 61:
Innanzitutto ilmessage.destinationName
, ovvero il topic della notizia viene diviso nelle sue singole parti con il metodo stringasplit()
sul segno "/" e queste vengono memorizzate nell’arraytopic
. - Riga 63:
Nello stato mywebio/ioname/ l’elemento centrale è il nome della rispettiva entrata o uscita. - Riga 64:
All’elemento DOMioname
viene assegnata in questa funzioneUpdateElement()
la classe CSSdisplayClass
.
ui.js: La connessione tra pagina HTML e client MQTT
Il ui.js contiene le funzioni che aggiornano la rappresentazione della pagina HTML e che dopo un’interazione con l’utente attivano le funzioni client MQTT.
/*
* Web-IO 4.0: MQTT WebSocket example
*/
/* Updates the CSS class of an DOM element */
function UpdateElement(ioname, displayClass){
var cell = document.getElementById(ioname);
if (cell){
cell.className = displayClass;
}
}
/* Toggles an input in the web interfaces and
* initiates an MQTT publish */
function ToggleOutput(ioname){
var cell = document.getElementById(ioname);
switch (cell.className){
case "on":
var message = new Paho.MQTT.Message("OFF");
message.destinationName = "webio/" + ioname + "/set";
mqttClient.send(message);
cell.className = "set_off";
break;
case "off":
var message = new Paho.MQTT.Message("ON");
message.destinationName = "webio/" + ioname + "/set";
mqttClient.send(message);
cell.className = "set_on";
break;
default:
cell.className = "unknown";
break;
}
};
/* Adds an Click-Event-Listener to a table cell, so that after
* a click the element can is toggeled */
function EnableToggle(ioname){
var cell = document.getElementById(ioname)
if (cell){
cell.addEventListener("click",
function(){
ToggleOutput(ioname)
},
true);
}
}
/*Initialize elements that can be toggled my by click*/
EnableToggle("output0");
EnableToggle("output1");
Aggiornamento della visualizzazione tramite il client MQTT
Con questa funzione UpdateElement()
il client MQTT al momento della ricezione di una subscription mette la classe CSS della rispettiva cella della tabella nello stato attuale.
/* Updates the CSS class of an DOM element */
function UpdateElement(ioname, displayClass){
var cell = document.getElementById(ioname);
if (cell){
cell.className = displayClass;
}
}
- Riga 6:
Alla funzione viene assegnata l’ID dell’elemento DOM e la classe CSS. - Riga 9:
Questa classe viene assegnata all’elemento.
Interazioni utente
Facendo clic su un output la funzione ToggleOutput()
adegua la rappresentazione e invia un relativo publish MQTT. Se l’attuale classe on
, le viene assegnata la classe, set_off
se la classe off
, le viene assegnata la nuova classe set_on
. In tutti gli altri casi lo stato attuale dell’uscita rimane non confermato e la visualizzazione viene assegnata alla classe unknown
.
/* Toggles an input in the web interfaces and
* initiates an MQTT publish
*/
function ToggleOutput(ioname){
var cell = document.getElementById(ioname);
switch (cell.className){
case "on":
var message = new Paho.MQTT.Message("OFF");
message.destinationName = "webio/" + ioname + "/set";
mqttClient.send(message);
cell.className = "set_off";
break;
case "off":
var message = new Paho.MQTT.Message("ON");
message.destinationName = "webio/" + ioname + "/set";
mqttClient.send(message);
cell.className = "set_on";
break;
default:
cell.className = "unknown";
break;
}
};
- Riga 16:
Alla funzione viene assegnata l’ID dell’elemento DOM che rappresenta l’output. - Riga 18:
A seconda della classe attuale dell’elemento viene messa - Righe 19-24, 25-30 e 21-33:
la nuova classe suset_on
,set_off
oppureunknown
.
Affinché possa essere attivata la funzione ToggleOutput()
con un clic sulla cella della tabella, aggiungete EnableToggle()
agli elementi inoltrati un gestore di evento per un clic del mouse.
/* Adds an Click-Event-Listener to a table cell, so that after
* a click the element can is toggeled
*/
function EnableToggle(ioname){
var cell = document.getElementById(ioname)
if (cell){
cell.addEventListener("click",
function(){
ToggleOutput(ioname)
},
true);
}
}
- Riga 40:
Il metodo riceve il nome di IO. - Riga 41:
Questo nome viene utilizzato per referenziare la rispettiva cella della tabella. - Righe 43-47:
Con il metodoaddEventListener()
viene assegnato all’elementocell
un gestore di evento per unclick
. Se si clicca sul rispettivo elemento, si deve attivare la funzioneToggleOutput()
con il nome dell’IO come parametro.
In seguito viene attivato EnableToggle()
per entrambi gli elementi "output1"
e "output2"
:
/*Initialize elements that can be toggled my by click*/
EnableToggle("output0");
EnableToggle("output1");
Elaborare questo tutorial con l’hardware
Desiderate elaborare questo tutorial, ma vi manca l’hardware necessario? Mettiamo a vostra disposizione il web-IO digitale 4.0 come campione.
Avete domande sul Web-IO digitale e su MQTT?
Il signor Thiel sarà lieto di assistervi.
Telefono: +49 202/2680-110 (Lun-Ven. 8-17)
E-mail: f.thiel@wut.de