W&T collega
Adattatori per TCP/IP, Ethernet, RS-232, RS-485, USB, 20 mA, Fibra ottica di vetro e plastica, http, SNMP, OPC, Modbus TCP, I/O digitale, I/O analogico, ISA, PCI

Applicazione relativa al Web-IO

Web-client MQTT con JavaScript

come interfaccia personalizzata per Web-IO


Web-client MQTT per Web-IO 4.0 digitale

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

Struttura del Web-client MQTT per Web-IO 4.0 digitale

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
Il file HTML fornisce il markup per la rappresentazione grafica. Il file Javascript mqtt.js contiene le funzioni specifiche MQTT come gestione dei collegamenti, publish e subscribe. Il file ui.js è l’interfaccia tra client MQTT e interfaccia web. Contiene le funzioni che al ricevimento di una subscription aggiornano l’interfaccia e che in caso di interazione dell’utente scatenano l’invio di un pacchetto PUBLISH.

Comunicazione tra web-client e web-IO

Svolgimento della comunicazione tra Web-IO e Web-client

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.

Impostazioni di base MQTT sul Web-IO
  • 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.

Azioni per la ricezione di subscription MQTT
  • 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.

Azioni per la ricezione di subscription MQTT
  • 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.

Scarica tutti i file

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’avviso new 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 metodo connect() del client MQTT.
  • Riga 20:
    Dopo che la connessione è riuscita, viene eseguita la funzione Connected() MQTT.
  • Riga 21:
    Dopo che la connessione è fallita, viene attivata ConnectionFailed().
  • 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 attributo payloadString 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 il message.destinationName, ovvero il topic della notizia viene diviso nelle sue singole parti con il metodo stringa split() sul segno "/" e queste vengono memorizzate nell’array topic.
  • Riga 63:
    Nello stato mywebio/ioname/ l’elemento centrale è il nome della rispettiva entrata o uscita.
  • Riga 64:
    All’elemento DOM ioname viene assegnata in questa funzione UpdateElement() la classe CSS displayClass.

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 su set_on, set_off oppure unknown.

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 metodo addEventListener() viene assegnato all’elemento cell un gestore di evento per un click. Se si clicca sul rispettivo elemento, si deve attivare la funzione ToggleOutput() 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