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.

../../_images/hivemq_connect.png

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.

../../_images/hivemq_publish.png

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:

  1. Encrypt all communication (TLS) to prevent eavesdropping

  2. Verify the broker (using a certificate) to prevent “man in the middle” attacks

  3. 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()