MicroPython MQTT Tutorial

MicroPython MQTT Tutorial
MicroPython Programming

Article

MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for constrained devices and low-bandwidth, high-latency networks. It's perfect for Internet of Things (IoT) applications where devices need to communicate efficiently with minimal resources.

MicroPython, a lean implementation of Python 3 for microcontrollers and embedded systems, makes it easy to program these devices with familiar Python syntax. When combined with MQTT, MicroPython allows you to create sophisticated IoT solutions with minimal code and effort.

Why Use MQTT with MicroPython?

  • Efficiency: MQTT's publish-subscribe model minimizes network bandwidth and device resource requirements

  • Reliability: Built-in QoS levels ensure message delivery even in unstable networks

  • Scalability: The broker architecture allows for easy scaling from one to thousands of devices

  • Simplicity: MicroPython's clean syntax makes complex IoT logic readable and maintainable

Prerequisites

Before we dive into coding, make sure you have the following:

Hardware

  • ESP32 or ESP8266 development board (other MicroPython-compatible boards work too)

  • Micro USB cable for programming

  • Optional: Sensors for the project section (e.g., DHT22 temperature/humidity sensor)

Software

  • MicroPython firmware flashed on your development board

  • Python 3.x installed on your computer

  • esptool for flashing firmware

  • ampy, rshell, or Thonny IDE for file transfer

  • MQTT broker (local Mosquitto installation or cloud-based like HiveMQ or CloudMQTT)

Note: If you haven't yet installed MicroPython on your board, check out the official documentation for detailed instructions.

Setting Up Your Environment

Installing the MQTT Library

MicroPython has a simple MQTT client library we'll use called umqtt.simple (and later we'll explore umqtt.robust for more reliability).

Connect to your board using your preferred method (e.g., WebREPL, serial connection, or Thonny IDE).

Install the umqtt.simple library:

import upip
upip.install('micropython-umqtt.simple')

If upip isn't available on your firmware or you have limited memory, you can download the library files manually and upload them to your board.

Network Configuration

Before using MQTT, we need to connect to a WiFi network. Create a file named boot.py with the following code:

import network
import time

def connect_wifi():
    ssid = "YOUR_WIFI_NAME"
    password = "YOUR_WIFI_PASSWORD"
    
    # Connect to WiFi
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    
    if not wlan.isconnected():
        print("Connecting to WiFi...")
        wlan.connect(ssid, password)
        
        # Wait for connection with timeout
        max_wait = 20
        while max_wait > 0:
            if wlan.isconnected():
                break
            max_wait -= 1
            print("Waiting for connection...")
            time.sleep(1)
    
    if wlan.isconnected():
        print("Connected to WiFi")
        print("Network config:", wlan.ifconfig())
        return True
    else:
        print("Failed to connect to WiFi")
        return False

# Connect to WiFi on boot
connect_wifi()

Warning: Never hardcode your WiFi credentials in production code. Consider using a config file that isn't shared in version control or implementing a WiFi manager.

Basic MQTT Communication

Understanding MQTT Concepts

Before we start coding, let's clarify some key MQTT concepts:

  • Broker: The central server that routes messages between clients

  • Publisher: Clients that send messages to topics

  • Subscriber: Clients that receive messages from topics they're subscribed to

  • Topics: Hierarchical strings (like home/livingroom/temperature) used to filter messages

  • QoS (Quality of Service): Delivery guarantee levels (0, 1, or 2)

Creating a Simple Publisher

Let's create a basic publisher that sends temperature readings to an MQTT broker. Create a file named simple_publisher.py:

from umqtt.simple import MQTTClient
import ubinascii
import machine
import time

# MQTT Broker Configuration
MQTT_BROKER = "broker.hivemq.com"  # Public test broker
MQTT_PORT = 1883
MQTT_CLIENT_ID = ubinascii.hexlify(machine.unique_id())
MQTT_TOPIC = b"micropython/temperature"

def connect_mqtt():
    client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT)
    client.connect()
    print(f"Connected to MQTT Broker: {MQTT_BROKER}")
    return client

def publish_message(client):
    count = 0
    while True:
        # In a real application, you would get data from a sensor here
        temperature = 20 + (count % 10)  # Dummy temperature data
        
        message = f"Temperature: {temperature}°C"
        client.publish(MQTT_TOPIC, message)
        print(f"Published: {message}")
        
        count += 1
        time.sleep(5)

try:
    client = connect_mqtt()
    publish_message(client)
except Exception as e:
    print(f"Error: {e}")
    machine.reset()

Creating a Simple Subscriber

Now, let's create a subscriber that listens for messages. Create a file named simple_subscriber.py:

from umqtt.simple import MQTTClient
import ubinascii
import machine
import time

# MQTT Broker Configuration
MQTT_BROKER = "broker.hivemq.com"  # Public test broker
MQTT_PORT = 1883
MQTT_CLIENT_ID = ubinascii.hexlify(machine.unique_id()) + b"_sub"
MQTT_TOPIC = b"micropython/temperature"

def connect_mqtt():
    client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT)
    
    # Set up callback for incoming messages
    client.set_callback(on_message)
    client.connect()
    client.subscribe(MQTT_TOPIC)
    print(f"Connected to MQTT Broker: {MQTT_BROKER}")
    print(f"Subscribed to: {MQTT_TOPIC}")
    return client

def on_message(topic, message):
    print(f"Received message on topic {topic.decode()}: {message.decode()}")
    # In a real application, you could process the data here
    # For example, display it on an LCD screen or trigger an action

def listen_for_messages(client):
    try:
        while True:
            # Check for new messages
            client.check_msg()
            # Alternative: Use wait_msg() to block until a message arrives
            # client.wait_msg()
            time.sleep(1)
    finally:
        client.disconnect()

try:
    client = connect_mqtt()
    listen_for_messages(client)
except Exception as e:
    print(f"Error: {e}")
    machine.reset()

Note: For testing, you can use a public broker like HiveMQ. For production applications, consider using a private broker or a cloud MQTT service with authentication.

Advanced Techniques

Using the Robust MQTT Client

The umqtt.simple library works fine for basic applications, but for more reliability, especially in unstable network environments, we can use umqtt.robust:

from umqtt.robust import MQTTClient
import ubinascii
import machine
import time

# MQTT Broker Configuration
MQTT_BROKER = "broker.hivemq.com"
MQTT_PORT = 1883
MQTT_CLIENT_ID = ubinascii.hexlify(machine.unique_id())
MQTT_TOPIC = b"micropython/sensors"
MQTT_USERNAME = None  # Set if your broker requires authentication
MQTT_PASSWORD = None  # Set if your broker requires authentication

def connect_mqtt():
    client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT, 
                       MQTT_USERNAME, MQTT_PASSWORD, 60)
    client.connect()
    print(f"Connected to MQTT Broker: {MQTT_BROKER}")
    return client

def publish_reliable(client, topic, message):
    # The robust client will automatically handle reconnection
    try:
        client.publish(topic, message)
        return True
    except Exception as e:
        print(f"Failed to publish message: {e}")
        return False

# Example usage
try:
    client = connect_mqtt()
    for i in range(10):
        message = f"Message #{i}"
        if publish_reliable(client, MQTT_TOPIC, message):
            print(f"Published: {message}")
        time.sleep(5)
    client.disconnect()
except Exception as e:
    print(f"Error: {e}")
    machine.reset()

Implementing QoS Levels

MQTT supports three QoS (Quality of Service) levels:

QoS Level

Description

Use Case

0 (At most once)

Fire and forget - no guarantee of delivery

High-frequency sensor readings where occasional loss is acceptable

1 (At least once)

Guaranteed delivery, but possible duplicates

Important messages where duplicates can be handled

2 (Exactly once)

Guaranteed delivery exactly once

Critical messages where duplicates are problematic

Here's how to publish with different QoS levels:

def publish_with_qos(client, topic, message, qos=0):
    client.publish(topic, message, qos=qos, retain=False)

# Example usage
topic = b"micropython/important_data"
publish_with_qos(client, topic, "Regular data", qos=0)
publish_with_qos(client, topic, "Important data", qos=1)
publish_with_qos(client, topic, "Critical data", qos=2)  # Note: Not all implementations support QoS 2

Warning: Higher QoS levels require more resources and bandwidth. Only use QoS 1 or 2 when necessary.

Using Retained Messages

MQTT brokers can store the last message on a topic if the "retain" flag is set. This is useful for configuration or status information:

def publish_retained(client, topic, message):
    client.publish(topic, message, retain=True)

# Example: Publish device status that should be available immediately to new subscribers
publish_retained(client, b"device/status", "online")

Working with Topics and Wildcards

MQTT topics support wildcards for flexible subscription patterns:

# Subscribe to a specific topic
client.subscribe(b"home/livingroom/temperature")

# Single-level wildcard (+)
client.subscribe(b"home/+/temperature")  # Matches home/livingroom/temperature, home/kitchen/temperature, etc.

# Multi-level wildcard (#)
client.subscribe(b"home/#")  # Matches all topics starting with home/

Complete Project: Weather Station

Let's build a complete MicroPython-based weather station that publishes temperature, humidity, and pressure data to an MQTT broker.

Hardware Setup

  • ESP32 or ESP8266 board

  • DHT22 temperature and humidity sensor

  • BMP180 or BME280 pressure sensor (optional)

  • Breadboard and jumper wires

Required Libraries

Install the necessary sensor libraries:

import upip
upip.install('micropython-umqtt.robust')
upip.install('micropython-dht')  # For DHT22 sensor
# For BME280, you may need to upload the library manually

Weather Station Code

Create a file named weather_station.py:

import machine
import time
import ujson
import dht
from umqtt.robust import MQTTClient
import ubinascii

# Configuration
WIFI_SSID = "YOUR_WIFI_SSID"
WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"
MQTT_BROKER = "YOUR_MQTT_BROKER"
MQTT_PORT = 1883
MQTT_CLIENT_ID = ubinascii.hexlify(machine.unique_id())
MQTT_TOPIC_BASE = b"weather/station1"
PUBLISH_INTERVAL = 60  # seconds

# Pin definitions
DHT_PIN = 4  # GPIO pin where DHT22 is connected
LED_PIN = 2  # Built-in LED pin (for status indication)

# Initialize sensors
dht_sensor = dht.DHT22(machine.Pin(DHT_PIN))
led = machine.Pin(LED_PIN, machine.Pin.OUT)

# Initialize WiFi
def connect_wifi():
    import network
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    
    if not wlan.isconnected():
        print("Connecting to WiFi...")
        wlan.connect(WIFI_SSID, WIFI_PASSWORD)
        
        # Wait for connection with timeout
        max_wait = 20
        while max_wait > 0:
            if wlan.isconnected():
                break
            max_wait -= 1
            print("Waiting for connection...")
            time.sleep(1)
    
    if wlan.isconnected():
        print("Connected to WiFi")
        print("Network config:", wlan.ifconfig())
        return True
    else:
        print("Failed to connect to WiFi")
        return False

# Initialize MQTT
def connect_mqtt():
    client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT)
    client.connect()
    print(f"Connected to MQTT Broker: {MQTT_BROKER}")
    
    # Publish an online status message with retain flag
    client.publish(MQTT_TOPIC_BASE + b"/status", b"online", retain=True)
    return client

# Read sensor data
def read_sensors():
    try:
        dht_sensor.measure()
        temperature = dht_sensor.temperature()
        humidity = dht_sensor.humidity()
        
        # You could add more sensors here (e.g., BMP180, BME280)
        # pressure = bmp_sensor.pressure
        pressure = None
        
        return {
            "temperature": temperature,
            "humidity": humidity,
            "pressure": pressure
        }
    except Exception as e:
        print(f"Error reading sensors: {e}")
        return None

# Publish data to MQTT
def publish_sensor_data(client, data):
    if data is None:
        return False
    
    try:
        # Blink LED to indicate data transmission
        led.value(1)
        
        # Convert data to JSON
        json_data = ujson.dumps(data)
        
        # Publish to a single topic with all data
        client.publish(MQTT_TOPIC_BASE + b"/data", json_data)
        
        # Also publish to individual topics for easier consumption
        for key, value in data.items():
            if value is not None:
                client.publish(MQTT_TOPIC_BASE + b"/" + key.encode(), str(value))
        
        led.value(0)
        return True
    
    except Exception as e:
        print(f"Error publishing data: {e}")
        led.value(0)
        return False

# Main execution loop
def main():
    # Connect to WiFi
    if not connect_wifi():
        machine.reset()
    
    try:
        # Connect to MQTT broker
        client = connect_mqtt()
        
        # Main loop
        last_publish = 0
        while True:
            current_time = time.time()
            
            # Check if it's time to publish
            if current_time - last_publish >= PUBLISH_INTERVAL:
                # Read sensor data
                sensor_data = read_sensors()
                
                # Publish data
                if sensor_data:
                    if publish_sensor_data(client, sensor_data):
                        print("Data published successfully")
                        print(sensor_data)
                        last_publish = current_time
            
            # Small delay to prevent busy waiting
            time.sleep(1)
    
    except Exception as e:
        print(f"Error in main loop: {e}")
        # Publish offline status before resetting
        try:
            client.publish(MQTT_TOPIC_BASE + b"/status", b"offline", retain=True)
            client.disconnect()
        except:
            pass
        machine.reset()

if __name__ == "__main__":
    main()

Receiving and Visualizing Data

You can use various tools to subscribe to your weather station's data:

  • MQTT Explorer - A cross-platform desktop application for monitoring MQTT messages

  • Node-RED - For creating visual flows and dashboards

  • Home Assistant - For home automation integration

  • Custom web application using MQTT.js

Tip: For long-term data storage, consider forwarding MQTT messages to a time-series database like InfluxDB or using Node-RED to store data in a database.

Troubleshooting

Common Issues and Solutions

Issue

Possible Cause

Solution

Connection failures

Network issues or incorrect broker details

Check WiFi connection, broker address, and port. Try using a ping test first.

Memory errors

MicroPython's limited RAM on small devices

Optimize code, use garbage collection, and consider a device with more memory.

Messages not received

Topic mismatch or QoS issues

Double-check topic strings and ensure both publisher and subscriber use matching topics.

Random disconnections

Poor network quality or power issues

Implement reconnection logic and use a stable power supply.

Debugging Techniques

# Enable debugging in your MQTT client
client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT, keepalive=60)
client.DEBUG = True  # Some implementations support this

# Memory management
import gc
gc.collect()  # Run garbage collection before heavy operations
print(f"Free memory: {gc.mem_free()} bytes")

Best Practices

  • Use Last Will and Testament (LWT) to notify when a device goes offline unexpectedly

  • Implement exponential backoff for reconnection attempts

  • Keep messages small to minimize bandwidth and memory usage

  • Use QoS appropriately based on message importance

  • Plan your topic hierarchy carefully for scalability

Conclusion

MQTT and MicroPython provide a powerful combination for building IoT devices that can communicate efficiently over networks. With the knowledge from this tutorial, you can:

  • Create devices that publish sensor data to MQTT brokers

  • Build subscribers that react to MQTT messages

  • Implement reliable communication with different QoS levels

  • Develop complete IoT projects with proper error handling

Next Steps

To further expand your MicroPython MQTT projects, consider exploring:

  • MQTT over TLS/SSL for secure communications

  • MQTT-SN for extremely constrained devices

  • Integration with cloud platforms like AWS IoT, Azure IoT, or Google Cloud IoT

  • More sophisticated sensor networks with multiple nodes

MicroPython's simplicity combined with MQTT's efficiency opens up a world of possibilities for IoT development. Happy hacking!

Edge Hackers

Join our community of makers, builders, and innovators exploring the cutting edge of technology.

Subscribe to our newsletter

The latest news, articles, and resources, sent to your inbox weekly.

© 2025 Edge Hackers. All rights reserved.