MQTT API

Qui viene descritto come connettere dispositivi di qualsiasi tipo, in grado di comunicare con il protocollo MQTT.

Requisiti

Affinché possiate integrare i vostri dispositivi è necessario aver configurato come scheda Master una delle nostre schede o un nostro dispositivo Gateway.

MQTT

MQTT è l’acronimo di Message Queuing Telemetry Transport e indica un protocollo di trasmissione dati TCP/IP basato su un modello di pubblicazione e sottoscrizione che opera attraverso un apposito Message Broker.

La scheda Master del Sistema (o il Gateway che forniamo in impianti privi di schede) fa le veci del Broker, quindi il dispositivo è ad essa che si deve connettere.

Connessione al Broker

Dall'applicazione tecnico potete ottenere l'indirizzo IP della scheda Master, quindi per la connessione al Broker è sufficiente definire:

  • Porta: 1883

  • Indirizzo Server: IP scheda Master o ohmb-master.local

Consigliamo di impostare un indirizzo IP statico per la scheda Master, per evitare che questo cambi in seguito a riavvii della scheda o del router.

Configurazione Elemento

Per poter integrare output e input via MQTT è necessario, in fase di configurazione di un elemento, definire MQTT come tipo di collegamento.

Di seguito gli elementi che supportano questo tipo di connessione:

  • Output

  • Input

  • Sensori

  • Led RGB

  • Variabili Virtuali

Questi i parametri che possono essere definiti:

  • Topic Lettura

  • Topic Scrittura (solo Output e Led RGB)

  • Path Messaggio (payload)

Path Messaggio

Tale parametro va definito solo nel caso in qui il messaggio (o payload) contenga un JSON. Il path permette infatti di definire il percorso per l'ottenimento del valore.

Se abbiamo ad esempio il seguente JSON:

{
    "temp": {
        "value": 22.5,
        "unity": "C"
    },
    "hum": {
        "value": 46,
        "unity": "%"
    }
}

i path per ottenere i valori di temperatura e umidità saranno:

  • Temperatura: temp/value

  • Umidità: hum/value

Se il JSON contenesse un array (lista) come in questo caso:

{
    "sensors": [
        {
            "id": "abc",
            "value": 22.5,
            "type": "temperature"
        },
        {
            "id": "xyz",
            "value": 46,
            "type": "humidity"
        }
    ]
}

i path da definire saranno:

  • Temperatura: sensors/0/value

  • Umidità: sensors/1/value

Publish

I topic per la pubblicazione degli stati non devono avere un formato particolare, ma è importante che siano univoci per ciascun input e output che desiderate integrare; è invece importante che il messaggio contenga soltanto il valore.

Quindi se vogliamo integrare un sensore di temperatura, il topic potrebbe essere test/temperatura e il messaggio il valore numerico che vogliamo comunicare.

Non ci sono vincoli sull'origine del valore numerico, potrebbe ad esempio trattarsi di un valore di temperatura ottenuto tramite internet sfruttando qualche API Meteo, o in seguito alla digitazione di un valore da tastiera.

Di seguito il formato del valore da inviare a seconda del tipo di elemento:

  • Sensore: invio del valore numerico con . come separatore decimale (es. 23.4).

  • Input: inviare 0 nel caso di input non verificato, 1 se verificato.

  • Output Digitale (relè): 0 se disattivo, 1 se attivo.

  • Output Analogico: valore tra 1 (o valore minimo di dimmerazione) e 255 (o valore massimo di dimmerazione) se attivo; se è disattivo va inviato 0:x, dove x è il valore prima di essere disattivato (es. 0:130).

  • Variabile Virtuale: numero intero o decimale (con il . come separatore decimale).

Subscribe

Nel caso si voglia integrare un output va definito anche il topic per la ricezione delle richieste. Anche in questo caso non ci sono vincoli, se non la definizione di topic univoci per ciascun output.

Il topic per la ricezione delle richieste potrebbe essere ad esempio out_1/comando, e a seconda del tipo di richiesta il messaggio sarà:

  • on: per l'attivazione dell'output

  • off: per la disattivazione

  • Valore tra 0 e 255: se l'uscita è analogica viene inviato un valore numerico (0 corrisponde alla minima intensità, 255 alla massima)

Anche in questo caso non è obbligatorio che il comando sia inviato ad un output reale, o comunque connesso fisicamente al dispositivo che volete integrare, ma tale valore potrebbe essere allegato ad una richiesta HTTP o ad un messaggio via bus seriale.

Esempi

Il codice sottostante (in Python) è stato testato su un Raspberry Pi, e permette di integrare un sensore DHT, per l'invio di temperatura e umidità:

import Adafruit_DHT
import time
import uuid
import paho.mqtt.client as mqtt


disconnected = False
last_message_sent = time.time()


class SuperClient(mqtt.Client):
	
	def loop(self, timeout=1.0, max_packets=1):
		time.sleep(0.001)
		return super(SuperClient, self).loop()


class DHTSensor(object):

	def __init__(self, pin):
		self.pin = pin
		self.temperature = 0.0
		self.humidity = 0.0
		self.old_temperature = 0.0
		self.old_humidity = 0.0
		self.last_update = 0

	def check(self) -> bool:
		return self.old_humidity != self.humidity or self.old_temperature != self.temperature

	def upate(self) -> bool:
		if time.time() - self.last_update > 5:
			self.old_humidity = self.humidity
			self.old_temperature = self.temperature
			humidity, temperature = Adafruit_DHT.read(sensor=Adafruit_DHT.DHT22, pin=self.pin)
			if None not in [humidity, temperature]:
				self.humidity = round(float(humidity), 1)
				self.temperature = round(float(temperature), 1)
			self.last_update = time.time()
			return self.check()
		return False

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
	global disconnected
	disconnected = False


# The callback for when the client receives a CONNACK response from the server.
def on_disconnect(client, userdata, rc):
	global disconnected
	disconnected = True


def set_client(host):
	try:
		global mqtt_client
		client_id = "rasp-" + str(uuid.uuid4())
		mqtt_client = SuperClient(client_id=client_id, clean_session=True)
		# Set callbacks methods
		mqtt_client.on_connect = on_connect
		mqtt_client.on_disconnect = on_disconnect
		mqtt_client._host = host
		mqtt_client._port = 1883
		mqtt_client._keepalive = 60

		#mqtt_client.max_queued_messages_set(queue_size=1)

		# Start connection
		mqtt_client.loop_start()
		mqtt_client.reconnect()
	except:
		pass


def update_via_mqtt(sensor: DHTSensor):
	global mqtt_client
	try:
		mqtt_client.publish(topic=f"rasp/dht/humidity/{sensor.pin}", payload=f"{sensor.humidity}", qos=0, retain=True)
		mqtt_client.publish(topic=f"rasp/dht/temperature/{sensor.pin}", payload=f"{sensor.temperature}", qos=0, retain=True)
	except:
		pass
		
		
set_client(host="x.x.x.x")

sensors = [DHTSensor(pin=19), DHTSensor(pin=26)]

# Set client loop
while True:
	for sensor in sensors:
		if sensor.upate():
			update_via_mqtt(sensor=sensor)

	if time.time() - last_message_sent > 10:
		for sensor in sensors:
			update_via_mqtt(sensor=sensor)
		last_message_sent = time.time()
	
	time.sleep(0.1)