====== 5. MQTT komunikace ======
===== Cíle cvičení =====
- Obousměrná UART komunikace s aplikací na PC
- Komunikace s MQTT brokerem
==== Komunikace po UART ====
Pro komunikaci po UART použijeme třídu ''UnbufferedSerial'', která umožňuje vyvolat při příchodu dat na sériový port Thunderboardu přerušení. V ISR pak provedeme potřebnou akci, např. změnu stavu LED diod.
#include
UnbufferedSerial serial_port(USBTX, USBRX, 115200);
DigitalOut R_LED(LED0); // LED0 = PD8 ~ RED, LED1 = PD9 ~ GREEN
DigitalOut G_LED(LED1);
void serial_isr()
{
char c;
if (serial_port.read(&c, 1))
{ // Read the data to clear the receive interrupt.
serial_port.write(&c, 1); // Echo the input back to the terminal.
if (c == 'r')
R_LED = !R_LED;
if (c == 'g')
G_LED = !G_LED;
}
}
int main()
{
serial_port.attach(serial_isr, SerialBase::RxIrq);
while(1) {}
}
POkud je po sériové lince poslán znak ''r'', změní se stav červené LED, znak ''g'' změní stav zelené LED. Abyste nemuseli případně kód kompilovat, můžete využít již hotový binární soubor {{ :courses:b0b37nsi:tutorials:unbuffered.zip |}}, který rozbalte a nahrajte do Thunderboardu připojeném jako disk.
===== MQTT komunikace =====
Aby bylo možné komunikovat, je třeba mít přístup na MQTT server, tzv. broker. Je buď možné využít veřejně dostupný (např. [[https://www.hivemq.com/public-mqtt-broker/|HiveMQ]], vytvořit si vlastní instanci v cloudu (např. [[https://devcenter.heroku.com/articles/cloudmqtt|Heroku]], [[https://cloud.google.com/iot/docs/how-tos/mqtt-bridge|Google]]) nebo si vytvořit vlastní, klidně na svém počítači. Patrně nejznámějším volně dostupným MQTT brokerem je [[https://mosquitto.org/|Mosquitto]]. Pro naše účely využijeme volně přístupný broker na adrese [[https://mqtt.eclipseprojects.io/]].
==== MQTT protokol ====
Na Internetu lze najít řadu článků o tom, jak MQTT funguje (v češtině např. [[https://www.root.cz/clanky/protokol-mqtt-komunikacni-standard-pro-iot/|tento]]). My se pro naše účely spokojíme s následujícím
* MQTT funguje na principu návrhového vzoru [[https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern|publisher-subscriber]]
* subscriber se přihlašuje k odběru zpráv, které přicházejí v rámci tématu (topic)
* publisher posílá zprávy do tématu, zpráva se dostane ke všem subscriberům, kteří se přihlásili k odběru
==== MQTT client - subscriber ====
Pro vytvoření odběratele zpráv použijeme knihovnu [[https://pypi.org/project/paho-mqtt/|paho-mqtt]]. Následující kód přečte dvě zprávy ze všech zpráv (topic ''#'' reprezentuje všechny zprávy), které v okamžiku připojení brokerem procházejí.
import paho.mqtt.subscribe as subscribe
topics = ['#']
m = subscribe.simple(topics, hostname="mqtt.eclipseprojects.io", retained=False, msg_count=2)
for a in m:
print(a.topic)
print(a.payload)
Pochopitelně nepotřebujeme přijímat všechny zprávy, ale pouze zprávy, které přijímat chceme. Následující kód se přihlásí k odběru topicu ''nsi/test'' a může přijímat zprávy, které do něj někdo pošle. Použijeme také asychronní zpracování dat pomocí callback funkce ''print_msg()'', která se zavolá v okamžiku, kdy se v topicu objeví zpráva:
import paho.mqtt.subscribe as subscribe
def print_msg(client, userdata, message):
print("%s : %s" % (message.topic, message.payload))
subscribe.callback(print_msg, "nsi/test", hostname="mqtt.eclipseprojects.io")
==== MQTT - publisher ====
Aby se v topicu objevila zpráva, je třeba ji tam publikovat.
import paho.mqtt.publish as publish
publish.single("nsi/test", "nazdar!", hostname="mqtt.eclipseprojects.io")
Data lze publikovat asychronně, jako v předchozím příkladu (nebo např. stisknutím tlačítka na embedded zařízení), nebo synchronně s využitím časovače. Můžeme použít např. časovač z knihovny Qt:
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
import paho.mqtt.publish as publish
def message():
publish.single("nsi/test", "nazdar!", hostname="mqtt.eclipseprojects.io")
def hello():
print("hello")
if __name__ == '__main__':
app = QApplication([])
timer = QTimer()
timer.timeout.connect(message)
timer.start(1000)
dlg = QDialog()
layout = QVBoxLayout()
button = QPushButton("hello")
button.clicked.connect(hello)
layout.addWidget(button)
dlg.setLayout(layout)
dlg.show()
app.exec()