Skip to content
Tutorial
15 min read
October 17, 2026

MicroPython MQTT on ESP32: Publish and Subscribe with umqtt

Install umqtt, connect to Mosquitto, publish readings and react to command messages. Includes a topic layout, client ID rules and how to test everything from the command line before blaming the code.

MicroPython MQTT on ESP32: Publish and Subscribe with umqtt

MQTT is a lightweight publish/subscribe messaging protocol often used by sensors, home-automation systems, dashboards, and IoT gateways. Instead of sending data directly to every consumer, an ESP32 publishes a message to a broker, which distributes it to clients subscribed to the matching topic. MicroPython can run an MQTT client on supported ESP32 firmware, making it practical to send sensor readings and receive simple commands.

This guide builds the connection step by step: prepare Wi-Fi, choose a broker, install or verify a compatible MQTT client, publish a test message, subscribe to a topic, and add reconnect and security practices. The examples use fictional placeholders. Never paste real passwords or tokens into public code or screenshots.

1. Understand MQTT roles

An MQTT system has clients and a broker. A client can publish messages, subscribe to topics, or do both. The broker receives publications and routes them to interested subscribers. An ESP32 may publish temperature to lab/esp32/temperature, while a dashboard subscribes to that topic. Another client might publish a command to a separate topic that the ESP32 subscribes to.

Topics are case-sensitive strings separated by /. Choose clear, stable names that describe device, location, and measurement. Avoid publishing unrelated values to the same topic unless the payload format is deliberately structured. Wildcards are available for subscriptions: + matches one topic level and # matches multiple trailing levels, but publishing topic names should not contain wildcards.

MQTT is a transport protocol, not a complete security system. Broker authentication, access-control rules, TLS support, client identity, and network placement determine who can read or send messages. A broker reachable on a LAN is not automatically private if the network contains untrusted devices.

2. Prepare the ESP32 and Wi-Fi

Install a MicroPython firmware build appropriate for the exact ESP32 variant. Confirm that the REPL works and that the board can connect to your Wi-Fi before debugging MQTT. Use a 2.4 GHz network when required by your board's radio capabilities and router configuration. Keep the ESP32 and broker reachable on the same LAN for the first test.

import network, time
SSID = "YOUR_WIFI_NAME"
PASSWORD = "YOUR_WIFI_PASSWORD"
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PASSWORD)
deadline = time.ticks_add(time.ticks_ms(), 15000)
while not wlan.isconnected() and time.ticks_diff(deadline, time.ticks_ms()) > 0:
    time.sleep_ms(250)
if not wlan.isconnected():
    raise RuntimeError("Wi-Fi connection timed out")
print("ESP32 network:", wlan.ifconfig())

Replace the placeholders only in your private local file. Do not commit credentials. The timeout prevents an incorrect password or missing access point from causing an infinite wait. For a deployed device, use a controlled reconnect policy and report when it is offline.

3. Choose and prepare a broker

A broker is the service that receives and distributes MQTT messages. For learning, use a broker on your local network or a reputable hosted service whose terms and security model you understand. If running a local broker on a computer, make sure the service is actually installed and running; a desktop MQTT client is not itself a broker. Record the broker hostname or LAN IP, port, and whether authentication or TLS is required.

The default MQTT port is commonly 1883 for unencrypted MQTT and 8883 for MQTT over TLS, but the broker administrator may configure other ports. Do not assume a port is open just because a broker application is installed. Check its service status and listener configuration, firewall rules, and network reachability.

Use a private LAN address or hostname for a local lab. Do not expose an unauthenticated broker directly to the public internet. If a hosted broker provides credentials or certificates, treat them as secrets and follow that provider's instructions for client authentication and TLS. Public test brokers can be useful for a quick experiment but should not carry private data or control important devices.

4. Install or verify the MQTT client library

MicroPython firmware does not always include an MQTT client by default. A commonly used lightweight client is umqtt.simple, but availability and maintained forks vary. Use a library compatible with your MicroPython version and the broker's required features. Some examples online use APIs that differ between library versions, so check the source and documentation for the exact client you install.

In Thonny, open the device filesystem and check whether the expected module exists. If it is absent, obtain the client from a trusted project source and copy the needed .py file to the board. Save a backup on your computer. Avoid installing arbitrary packages from unknown archives, and check whether the chosen client supports authentication, keep-alive, and TLS if your broker requires them.

Test the import before writing the full application:

try:
    from umqtt.simple import MQTTClient
    print("MQTT client import succeeded")
except ImportError as exc:
    print("MQTT client unavailable:", exc)

An import error usually means the module is missing, named differently, stored in the wrong location, or incompatible with the firmware. Resolve that first rather than debugging broker settings and application code simultaneously.

5. Publish and subscribe with a minimal script

The following example uses the commonly available umqtt.simple API. Replace the broker hostname and credentials with your own settings. For a local broker, use its LAN address rather than localhost: from the ESP32, localhost refers to the ESP32 itself, not your computer.

from umqtt.simple import MQTTClient
import time

CLIENT_ID = b"solderhub-esp32-demo-01"
BROKER = "192.168.1.50"  # example LAN address only
PORT = 1883
TOPIC = b"solderhub/demo/status"
client = MQTTClient(CLIENT_ID, BROKER, port=PORT, keepalive=30)
client.connect()
client.publish(TOPIC, b"ESP32 connected")
print("Published test message")
client.disconnect()

Run this after Wi-Fi is connected. Replace the example address with the real broker address on your own network. The message payload is bytes, so the topic is also represented as bytes in this client API. Open a separate MQTT client and subscribe to solderhub/demo/status before running the script, then confirm that the message arrives.

To subscribe, create a callback that receives the topic and message, connect, subscribe, and enter the client's message loop. A blocking loop is suitable for a small test, but it will stop other application work unless you design around it. For an application that also reads sensors or drives outputs, use a bounded polling loop or a supported asynchronous design and make sure sensor sampling and network processing both get time to run.

6. Receive messages with a callback

A subscription lets the ESP32 receive publications on selected topics. The callback should be short: decode and validate the message, update a requested state, and return. Do not perform long sensor measurements or delays inside the callback because they can block message handling.

from umqtt.simple import MQTTClient

BROKER = "192.168.1.50"  # example only
CLIENT_ID = b"solderhub-subscriber-01"
SUB_TOPIC = b"solderhub/demo/command"

def on_message(topic, message):
    print("Topic:", topic, "Message:", message)

client = MQTTClient(CLIENT_ID, BROKER, port=1883, keepalive=30)
client.set_callback(on_message)
client.connect()
client.subscribe(SUB_TOPIC)
print("Subscribed; waiting for messages")
try:
    while True:
        client.wait_msg()
finally:
    client.disconnect()

Run a separate publisher and send a test message to solderhub/demo/command. The callback prints the topic and payload. This example waits for a message and therefore does not continue other tasks while idle. If the device must also sample a sensor, use the polling method supported by the installed library or restructure the application around cooperative scheduling. Verify the library's exact behaviour and method names before combining it with other code.

Never let an incoming message directly execute arbitrary Python or set arbitrary pins. Parse a small, explicit set of allowed commands, validate the payload, and reject unknown values. A topic name is not an authorization mechanism; anyone permitted to publish to that topic could otherwise control the device.

7. Recover from connection failures

Wi-Fi and broker connections can fail independently. The access point may restart, the broker service may stop, or the router may assign a new address. A robust device should detect errors, close a stale connection where possible, wait briefly, reconnect Wi-Fi if needed, and then reconnect to MQTT. Avoid retrying in a tight loop because rapid retries waste power and flood logs.

For a small project, put connection setup in a function, catch expected I/O errors around network operations, and retry after a delay. Exact exceptions and cleanup behaviour depend on the chosen client library. Do not assume that a failed publish automatically reconnects the client. Check the library documentation and re-establish the session as needed.

Use a unique client ID for every simultaneously connected device. If two clients connect using the same ID, many brokers disconnect the earlier connection. For multiple boards, use a stable device label plus a unique suffix. Log Wi-Fi connection, MQTT connection, successful publication, and reconnect attempts so you can distinguish broker problems from sensor faults.

8. Payload design, retained messages, and quality of service

Keep payloads small and predictable. Plain text is easy to inspect during learning; JSON is useful when a message contains several fields. For example, a sensor could publish {"temperature_c":24.6,"humidity_pct":51.2} to a telemetry topic. Make sure the payload is valid JSON, include units in field names or documentation, and use consistent field names so dashboards do not need to guess what a number means. If a reading is invalid, publish an explicit error or status rather than a fake zero.

MQTT quality of service (QoS) defines delivery guarantees. QoS 0 is best-effort delivery, QoS 1 uses acknowledgement and may deliver duplicates, and QoS 2 provides an exactly-once protocol flow at greater cost where supported. The lightweight client and broker must support the QoS you choose. Do not assume a successful function call means a message has been stored permanently or consumed by every subscriber.

Retained messages allow a broker to deliver the latest retained value to a new subscriber. They can be useful for current state, but retained commands can be dangerous if an old command is replayed when a device reconnects. Choose retain behaviour intentionally, especially for actuator topics. For important state, include a timestamp or sequence value and design the receiver to reject stale commands.

9. Security and troubleshooting

Use broker authentication whenever available and restrict permissions so each device can publish and subscribe only to the topics it needs. Do not use a shared administrator account on every sensor. Use TLS when the broker supports it and the installed client can validate the connection appropriately; certificate and memory requirements vary by firmware. If TLS is not available in the learning setup, keep the broker on a trusted private network and never send sensitive data across an untrusted network in plaintext.

Connection refused: check broker service status, hostname, port, and listener configuration. Timeout: verify routing, firewall rules, and that the ESP32 and broker can communicate. Authentication error: confirm username/password and broker access-control rules. Import error: check that the correct MQTT module is on the device. Subscriber sees nothing: verify exact topic spelling, case, broker address, and whether the publisher actually ran. Repeated disconnects: check duplicate client IDs, Wi-Fi signal, keep-alive settings, and broker logs.

Test in layers: first connect Wi-Fi, then verify broker reachability, then publish one known message, and finally add subscriptions. Use an MQTT desktop client to subscribe and publish while debugging, but remember it is a client tool rather than a broker service. Change one variable at a time and keep error messages for reference.

10. Bring the pieces together

A practical first milestone is a sensor that publishes one reading every few seconds. Keep the sensor sampling interval appropriate for the sensor, convert the reading to a clear payload, publish it to a documented topic, and report failures separately. Do not let a temporary broker outage prevent local safety-related logic from running. If the sensor is used for monitoring, show when the last valid reading arrived so an old value is not mistaken for live data.

Before adding remote control, define the allowed commands and the safe behaviour after reboot or loss of network. For example, a command handler might accept only ON and OFF, ignore unknown payloads, and apply a local timeout if the application requires one. MQTT delivery is not a substitute for a physical interlock or emergency stop. Test reconnects and stale messages with the actuator disconnected before using any real load.

SolderHub checklist: verify the board and firmware; prove Wi-Fi separately; confirm the broker is running; test publish and subscribe with a unique client ID; use consistent topics and payloads; handle disconnects without tight retry loops; restrict broker permissions; and keep credentials out of public examples. These habits make later integration with dashboards, Node-RED, or home-automation software far easier to diagnose.

Signal In

Built from what makers search for

The lookups piling up in SolderHub's search bar every day shape what we publish next — new component pages, board guides, and firmware notes, sent out as they go live.

By subscribing you agree to our Privacy Policy. Unsubscribe anytime.