Advanced MiniMQTT Usage

MiniMQTT Loop

You should always use a loop when writing CircuitPython code which uses the MiniMQTT module. The loop methods check incoming and process outgoing mqtt messages along with keeping the network connection between your board and the MQTT broker alive . 

MiniMQTT supports two different types of loops: loop and loop_forever

loop

Calling loop creates a non-blocking network loop. You can create new code below the call to loop, and it'll be executed. This type of loop should be run frequently to avoid disconnecting from the MQTT server. The loop method also does not handle network hardware (WiFi) or MQTT broker disconnection. You'll need to handle that yourself.

Here's an example of using a non-blocking loop.

import time
import board
import busio
from digitalio import DigitalInOut
import neopixel
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_minimqtt import MQTT

### WiFi ###

# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

# If you have an externally connected ESP32:
# esp32_cs = DigitalInOut(board.D9)
# esp32_ready = DigitalInOut(board.D10)
# esp32_reset = DigitalInOut(board.D5)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

### Adafruit IO Setup ###

# Setup a feed named `testfeed` for publishing.
default_topic = secrets['user']+'/feeds/testfeed'

### Code ###
# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connected(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    print('Connected to MQTT broker! Listening for topic changes on %s'%default_topic)
    # Subscribe to all changes on the default_topic feed.
    client.subscribe(default_topic)

def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print('Disconnected from MQTT Broker!')

def message(client, topic, message):
    """Method callled when a client's subscribed feed has a new
    value.
    :param str topic: The topic of the feed with a new value.
    :param str message: The new value
    """
    print('New message on topic {0}: {1}'.format(topic, message))

# Connect to WiFi
wifi.connect()

# Set up a MiniMQTT Client
mqtt_client = MQTT(socket,
                   broker = secrets['broker'],
                   username = secrets['user'],
                   password = secrets['pass'],
                   network_manager = wifi)

# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message

# Connect the client to the MQTT broker.
mqtt_client.connect()

photocell_val = 0
while True:
    # Poll the message queue
    mqtt_client.loop()

    # Send a new message
    print('Sending photocell value: %d'%photocell_val)
    mqtt_client.publish(default_topic, photocell_val)
    photocell_val += 1
    time.sleep(0.5)

loop_forever

The other type of loop you can use is loop_forever. This starts a blocking loop. Network and broker error handling and reconnection is handled within the loop_forever method. 

Since this is a blocking loop, any code below a call to client.loop_forever() will not execute. With this method, you should handle all events within the callback methods.

Here's an example of using loop_forever:

# CircuitPython MiniMQTT Library
# Adafruit IO SSL/TLS Example for WiFi (ESP32SPI)
import board
import busio
from digitalio import DigitalInOut
import neopixel
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_minimqtt import MQTT

### WiFi ###

# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

# If you have an externally connected ESP32:
# esp32_cs = DigitalInOut(board.D9)
# esp32_ready = DigitalInOut(board.D10)
# esp32_reset = DigitalInOut(board.D5)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

### Adafruit IO Setup ###

# Setup a feed named `testfeed` for publishing.
default_topic = secrets['user']+'/feeds/testfeed'

### Code ###

# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connected(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    print('Connected to MQTT broker! Listening for topic changes on %s'%default_topic)
    # Subscribe to all changes on the default_topic feed.
    client.subscribe(default_topic)

def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print('Disconnected from MQTT Broker!')

def message(client, topic, message):
    """Method callled when a client's subscribed feed has a new
    value.
    :param str topic: The topic of the feed with a new value.
    :param str message: The new value
    """
    print('New message on topic {0}: {1}'.format(topic, message))

# Connect to WiFi
wifi.connect()

# Set up a MiniMQTT Client
mqtt_client = MQTT(socket,
                   broker = secrets['broker'],
                   username = secrets['user'],
                   password = secrets['pass'],
                   network_manager = wifi)

# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message

# Connect the client to the MQTT broker.
mqtt_client.connect()

# Start a blocking message loop
# If you only want to listen to incoming messages,
# you'll want to loop_forever as it handles network reconnections
# No code below this line will execute.
mqtt_client.loop_forever()

MiniMQTT Client Identifier

By default, MiniMQTT will generate a unique, randomly generated client identifier based off the CircuitPython device's microcontroller's UUID and a random number. The broker will see a client named something like cpy-3123

If you'd like to set a custom client_id (what the broker sees the CircuitPython device as), you can provide a string. Do make sure the client_id's you create are unique, or your broker will disconnect them.

client =  MQTT(socket,
               broker = secrets['broker'],
               username = secrets['user'],
               password = secrets['pass'],
               network_manager = wifi,
client_id = 'brentspyportal')

MiniMQTT Logging

MiniMQTT uses the CircuitPython logger module for printing different types of errors, depending on the priority the logger was set to.

To attach a logger to a MiniMQTT client, simply 

client =  MQTT(socket,
               broker = secrets['broker'],
               username = secrets['user'],
               password = secrets['pass'],
               network_manager = wifi,
log = True)

Then, you will need to add another line setting the logger's level. While the logger is initialized to the INFO level by default, you may want to see more information about your current MQTT session.

To set the logger to a higher priority logging level, like DEBUG, add the following line after the MQTT client has been initialized:

client.set_logger_level(DEBUG)

MiniMQTT Last Will and Testament

MiniMQTT supports setting publishing a message to a specific topic when your MQTT client disconnects.

To use the last will - specify the topic you'd like to publish to and provide it with a message to publish when the client disconnects.

client.last_will(topic='device/status/', message='Goodbye!')

This method must be called before the connect method. The last will and testament also must be allowed by your MQTT Broker - Adafruit IO does not support this feature.

This guide was first published on Jul 23, 2019. It was last updated on Jul 23, 2019. This page (Advanced MiniMQTT Usage) was last updated on Nov 12, 2019.