MQTT#
MQTT is an efficient protocol for communication between IoT devices.
Broker#
ide49 comes with a built-in mosquitto MQTT broker. By default it is configured for both encrypted and authenticated as well as unencrypted traffic. Without encryption, only messages to topic public/#
are permitted.
Clients#
Below we show several examples of MQTT clients. The Web Client and Command Line tools are mostly useful for debugging. The CPython and MicroPython are examples of what you might incorporate in an application.
Web Client#
Web clients are great to observe diagnose MQTT communication. Below is an example using the HiveMQ client. Change the Host
field below to the URL of your instance of ide49 (e.g. https://iot49.local/
) and make sure Port
is set to 9001. The ClientID
is a random string generated automatically and must be different for evey client connecting to the broker.
Click connect and add subscriptions and publish messages. The example below publishes and subscribes to the same topic. Normally these would differ to communicate with other MQTT clients.
Command Line#
Alternatively use mosquitto_sub and mosquitto_pub in a terminal window or from a notebook like in the examples below:
!mosquitto_sub -h $HOST_IP -t "public/#"
hello, how are you today?
Interrupted
!mosquitto_pub -h $HOST_IP -t "public/hello" -m "Grüeziwohl Frau Stirnima"
CPython#
A common use case of MQTT is communication between microcontrollers and host computers. Below is a simple example receiving and sending MQTT messages. It first publishes a few messages to testtopic
and then waits for 20 seconds for messages published to testpub
. Use the web client to command line discussed above to listen and send to these topics.
Before running the code below, update the broker address.
%%host
from paho.mqtt.client import Client
from threading import Event
import time, os
USE_WS = False
BROKER = os.getenv('HOST_IP')
if USE_WS:
PORT = 9001
TRANSPORT = 'websockets'
else:
PORT = 1883
TRANSPORT = 'tcp'
# on_message runs on a different thread ...
connected = Event()
def on_message(client, userdata, message):
print(f"message received: {message.topic} = {message.payload}")
def on_connect(client, userdata, flags, rc):
global connected
client.subscribe('public/msg')
connected.set()
def on_disconnect(client, userdata, rc):
global connected
connected.clear()
print(f"disconnected, rc={rc}")
client = Client("client-socks", transport=TRANSPORT)
client.on_message = on_message
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.connect(BROKER, PORT)
# start a separate thread listening to messages
client.loop_start()
# wait for connection before publishing
connected.wait()
for i in range(5):
topic = f"public/topic_{i}"
msg = f"msg {i}"
print(f"publish {topic} = {msg}")
client.publish(topic, msg)
time.sleep(0.1)
# keep listening to messages
time.sleep(10)
print("disconnect")
client.disconnect()
publish public/topic_0 = msg 0
publish public/topic_1 = msg 1
publish public/topic_2 = msg 2
publish public/topic_3 = msg 3
publish public/topic_4 = msg 4
message received: public/msg = b'hello, how are you today?\n'
disconnect
disconnected, rc=0
0
MicroPython#
Several MQTT clients are available for MicroPython. The sample below uses a simple client from the “official” library, micropython-lib.
Let’s first download the code:
%%bash
src=https://raw.githubusercontent.com/micropython/micropython-lib/master/micropython/umqtt.simple/umqtt/simple.py
dest_dir=$IOT_PROJECTS/internet/code/lib/
mkdir -p $dest_dir
curl -o $dest_dir/mqtt_client.py $src
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6479 100 6479 0 0 10517 0 --:--:-- --:--:-- --:--:-- 10500
%connect esp32 -q
%rsync
UPDATE /lib/mqtt_client.py
The examples below are taken from the microPython-lib github archive. Update the broker IP address in the code below and set up a client (e.g. HiveMQ) to check them out.
from mqtt_client import MQTTClient
# Test e.g. with:
# mosquitto_sub -h $HOST_IP -t public
client = MQTTClient('umqtt_client', '10.39.40.200', port=1883)
client.connect()
client.publish(b"public", b"Hello from micropython!")
client.disconnect()
This example subscribes to topic led
(note that topics and messages are bytes
, not strings) to control an LED. Change the LED pin number to match your board.
from mqtt_client import MQTTClient
from machine import Pin
import ubinascii
import machine
import micropython
led = Pin(13, Pin.OUT, value=1)
def sub_cb(topic, msg):
global quit
print("topic {} = {}".format(topic.decode(), msg.decode()))
if msg == b"on":
led.value(1)
elif msg == b"off":
led.value(0)
elif msg == b"toggle":
led.value(1-led.value())
elif msg == b"quit":
quit = True
client = MQTTClient('mqtt_led_client', '10.39.40.200')
# Subscribed messages will be delivered to this callback
client.set_callback(sub_cb)
client.connect()
client.subscribe(b'public/led')
quit = False
try:
while not quit:
# call this to receive messages
client.wait_msg()
except Exception as e:
print("*****", e)
finally:
client.disconnect()
topic public/led = on
topic public/led = off
topic public/led = toggle
topic public/led = quit
Note that it is necessary to call client.wait_msg()
to receive messages, i.e. place this instruction somewhere in your program where it will be executed regularly.
In simple programs like the example above that’s easy enough to do, but in more complex applications this may not be trivial.
An alternative is to use an mqtt client that uses the asyncio
, a cooperative multitasking framework. An asyncronous MQTT client for MicroPython is available here.
Secure Communication#
Securing MQTT communication involves three measures:
Encrypt all communication (TLS) to prevent eavesdropping
Verify the broker (using a certificate) to prevent “man in the middle” attacks
User/password authentication
The default ide49 configuration of the broker does not restrict topics. A practical application would add topic access control lists and disable unencrypted communication.
# upload the certificate
!cp /service-config/mosquitto/certs/ca.crt $IOT_PROJECTS/internet/code/ssl
%rsync
UPDATE /ssl/ca.crt
from mqtt_client import MQTTClient
# Test e.g. with:
# mosquitto_sub -h $HOST_IP -t public
import ubinascii as binascii
with open('/ssl/ca.crt') as f:
cert = binascii.a2b_base64(f.read())
client = MQTTClient('umqtt_client', '10.39.40.200', port=8883,
user=b'iot49', password=b'iot49',
ssl=True, ssl_params={"cert":cert})
client.connect()
client.publish(b"public", b"Hello from micropython!")
client.publish(b"public/abc", b"Hello again!")
client.disconnect()