MicroPython MQTT Tutorial

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 messagesQoS (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!
Article Info
Engage
Table of Contents
- Why Use MQTT with MicroPython?
- Prerequisites
- Hardware
- Software
- Setting Up Your Environment
- Installing the MQTT Library
- Network Configuration
- Basic MQTT Communication
- Understanding MQTT Concepts
- Creating a Simple Publisher
- Creating a Simple Subscriber
- Advanced Techniques
- Using the Robust MQTT Client
- Implementing QoS Levels
- Using Retained Messages
- Working with Topics and Wildcards
- Complete Project: Weather Station
- Hardware Setup
- Required Libraries
- Weather Station Code
- Receiving and Visualizing Data
- Troubleshooting
- Common Issues and Solutions
- Debugging Techniques
- Best Practices
- Conclusion
- Next Steps