ESP32 und Arduino

MicroPython umqtt auf dem ESP32: Publish und Subscribe mit umqtt.simple und umqtt.robust

Verbinden Sie einen ESP32 mit MicroPython über umqtt.simple mit einem MQTT-Broker, ergänzen Sie automatische Reconnects mit umqtt.robust, empfangen Sie Befehle ohne Blockieren und lernen Sie, was diese minimalen Clients nicht können.

ESP32-Board mit einem MicroPython-umqtt-Skript, das Sensorwerte per MQTT veröffentlicht
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Mit MicroPython umqtt auf dem ESP32 bekommen Sie MQTT in wenigen Zeilen Python, ohne Kompilierschritt und mit einer interaktiven Konsole zum Debuggen. Dieses Tutorial verbindet einen ESP32 mit umqtt.simple mit broker.connectmq.com, ergänzt automatische Wiederverbindung mit umqtt.robust, verarbeitet eingehende Befehle ohne Blockieren und erklärt die Grenzen, die Sie kennen sollten, bevor Sie sich in der Produktion auf diese Clients verlassen.

Warum MicroPython für MQTT-Geräte

C++ mit dem Arduino-Core ist schneller und bietet mehr Bibliotheken, doch MicroPython punktet bei der Iterationsgeschwindigkeit. Sie bearbeiten eine Datei, kopieren sie aufs Board und führen sie in Sekunden aus. Über die REPL können Sie WLAN, Sensoren und MQTT interaktiv ausprobieren – unbezahlbar, wenn Sie herausfinden müssen, warum etwas nicht funktioniert.

Das Paket umqtt aus micropython-lib stellt zwei Module bereit:

  • umqtt.simple: ein minimaler MQTT-3.1.1-Client. Verbinden, Publish, Subscribe, Ping, Last Will. Nichts passiert automatisch.
  • umqtt.robust: eine schlanke Unterklasse von simple, die Socket-Fehler in publish(), wait_msg() und einigen weiteren Aufrufen abfängt und automatisch neu verbindet.

Beide sind klein genug, um problemlos auf jeden ESP32 zu passen.

Das Board einrichten

  1. Flashen Sie mit esptool.py eine aktuelle MicroPython-Firmware für Ihren ESP32 von der offiziellen Download-Seite.
  2. Installieren Sie mpremote auf Ihrem Rechner: pip install mpremote.
  3. Prüfen Sie, ob umqtt bereits vorhanden ist. Manche Firmware-Builds enthalten es, andere nicht. Schlägt import umqtt.simple in der REPL fehl, installieren Sie es mit mip:
mpremote connect auto mip install umqtt.simple
mpremote connect auto mip install umqtt.robust

mip setzt voraus, dass das Board online ist. Ist es noch nicht mit dem WLAN verbunden, starten Sie die Installation aus einem Skript, das sich zuerst verbindet, oder kopieren Sie die Moduldateien manuell mit mpremote cp. Die MicroPython-Dokumentation zur Paketverwaltung beschreibt alle Optionen.

Mit dem WLAN verbinden

Legen Sie diesen Code in boot.py ab, damit er bei jedem Start ausgeführt wird:

import network
import time

WIFI_SSID = "YOUR_WIFI"
WIFI_PASS = "YOUR_WIFI_PASSWORD"

def connect_wifi(timeout=15):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        wlan.connect(WIFI_SSID, WIFI_PASS)
        start = time.time()
        while not wlan.isconnected():
            if time.time() - start > timeout:
                raise RuntimeError("Wi-Fi connection timed out")
            time.sleep(0.25)
    print("Wi-Fi OK", wlan.ifconfig()[0])
    return wlan

connect_wifi()

Veröffentlichen mit umqtt.simple

Das erste Skript verbindet sich mit dem öffentlichen MQTT-Broker broker.connectmq.com und veröffentlicht alle fünf Sekunden einen Messwert. Die Client ID wird aus der eindeutigen Chip-ID gebildet, sodass sie nie leer ist und nie mit einem anderen Board kollidiert.

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

BROKER = "broker.connectmq.com"
PREFIX = b"connectmq/demo/2b7e95"
CLIENT_ID = b"mpy-" + ubinascii.hexlify(machine.unique_id())

client = MQTTClient(CLIENT_ID, BROKER, port=1883, keepalive=30)
client.set_last_will(PREFIX + b"/status", b"offline", retain=True, qos=1)
client.connect()
client.publish(PREFIX + b"/status", b"online", retain=True, qos=1)
print("Connected as", CLIENT_ID)

while True:
    temp_c = (esp32.raw_temperature() - 32) / 1.8   # grober Wert des internen Sensors
    payload = '{"temp":%.1f,"uptime":%d}' % (temp_c, time.ticks_ms() // 1000)
    client.publish(PREFIX + b"/telemetry", payload.encode())
    time.sleep(5)

Auf ESP32-Varianten ohne raw_temperature() ersetzen Sie diese Zeile durch das Auslesen eines echten Sensors. Verfolgen Sie die Nachrichten von Ihrem Rechner aus:

mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-2b7e95 -t 'connectmq/demo/2b7e95/#' -v

Beachten Sie, dass Topics und Payloads Bytes sind. umqtt akzeptiert an vielen Stellen auch str, doch die konsequente Verwendung von Bytes vermeidet Überraschungen – vor allem in Callbacks, die immer Bytes erhalten.

Befehle empfangen, ohne zu blockieren

Um Nachrichten zu empfangen, registrieren Sie einen Callback und abonnieren das Topic. Anschließend müssen Sie regelmäßig entweder wait_msg() aufrufen, das blockiert, bis eine Nachricht eintrifft, oder check_msg(), das sofort zurückkehrt, wenn nichts ansteht. Verwenden Sie check_msg() in einer Schleife, die auch andere Aufgaben erledigt:

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

BROKER = "broker.connectmq.com"
PREFIX = b"connectmq/demo/2b7e95"
CLIENT_ID = b"mpy-" + ubinascii.hexlify(machine.unique_id())
led = machine.Pin(2, machine.Pin.OUT)

def on_message(topic, msg):
    print("Received", topic, msg)
    if topic == PREFIX + b"/led/set":
        led.value(1 if msg == b"ON" else 0)
        client.publish(PREFIX + b"/led/state", msg, retain=True)

client = MQTTClient(CLIENT_ID, BROKER, port=1883, keepalive=30)
client.set_callback(on_message)
client.connect()
client.subscribe(PREFIX + b"/led/set", qos=1)

last_ping = time.ticks_ms()
last_publish = time.ticks_ms()

while True:
    client.check_msg()                       # wartende Nachricht verarbeiten

    now = time.ticks_ms()
    if time.ticks_diff(now, last_publish) > 10000:
        client.publish(PREFIX + b"/telemetry", b'{"alive":true}')
        last_publish = now
        last_ping = now
    elif time.ticks_diff(now, last_ping) > 20000:
        client.ping()                        # Verbindung im Leerlauf aufrechterhalten
        last_ping = now

    time.sleep_ms(50)

Testen Sie es:

mosquitto_pub -h broker.connectmq.com -p 1883 -i pc-2b7e95-pub -t connectmq/demo/2b7e95/led/set -m ON

Zwei Dinge übersieht man leicht. Erstens sendet umqtt.simple keine Keepalive-Pings von selbst. Wenn Sie ein Keepalive festlegen und seltener veröffentlichen, müssen Sie wie in der Schleife oben ping() aufrufen, sonst trennt der Broker die Verbindung. Zweitens muss set_callback() vor subscribe() aufgerufen werden.

Automatische Wiederverbindung mit umqtt.robust

umqtt.simple wirft einen OSError, wenn die Verbindung abbricht, und Ihr Skript beendet sich, sofern Sie den Fehler nicht behandeln. umqtt.robust umhüllt die wichtigsten Aufrufe und verbindet sich mit einer kurzen Pause zwischen den Versuchen neu. Der Import ist die einzige Änderung:

from umqtt.robust import MQTTClient

Es gibt allerdings einen Haken. Der robuste Client stellt die Socket-Verbindung wieder her, abonniert die Topics aber nicht erneut, und bei einer Clean Session vergisst der Broker Ihre Abonnements. Ein zuverlässiges Muster besteht darin, den Verbindungsschritt in eine Funktion zu packen und diese sowohl beim Start als auch nach jedem selbst abgefangenen Fehler aufzurufen:

def connect_and_subscribe():
    client.connect(clean_session=True)
    client.subscribe(PREFIX + b"/led/set", qos=1)
    client.publish(PREFIX + b"/status", b"online", retain=True, qos=1)

connect_and_subscribe()

while True:
    try:
        client.check_msg()
        # ... periodisches Veröffentlichen ...
    except OSError as e:
        print("MQTT error", e, "- reconnecting")
        time.sleep(5)
        try:
            connect_and_subscribe()
        except OSError:
            pass
    time.sleep_ms(50)

Für wirklich unbeaufsichtigte Geräte ergänzen Sie machine.WDT(timeout=30000) und füttern den Watchdog in der Schleife, damit ein vollständiges Hängen das Board neu startet.

Ein MicroPython-umqtt-Projekt auf dem ESP32 strukturieren

Sobald ein Skript über ein paar Dutzend Zeilen hinauswächst, teilen Sie es auf wie jedes andere Python-Projekt. Ein Aufbau, der sich auf dem Board bewährt:

  • boot.py verbindet das WLAN und sonst nichts, sodass ein Fehler in der Anwendung Sie nie aus der REPL aussperrt.
  • config.py enthält Broker-Host, Port, Topic-Präfix und Zugangsdaten. Halten Sie die Datei aus der Versionsverwaltung heraus und kopieren Sie sie separat auf jedes Board.
  • main.py importiert config, erstellt den Client und führt die Hauptschleife in einem try/except aus, das die Exception protokolliert und nach einer kurzen Pause machine.reset() aufruft.

Zwei Gewohnheiten sparen viel Debugging-Zeit. Gestalten Sie die Hauptschleife so, dass Strg+C in der REPL sie sauber mit KeyboardInterrupt beendet, statt von Ihrer allgemeinen Fehlerbehandlung abgefangen zu werden. Und rufen Sie regelmäßig gc.collect() auf, wenn Sie Payload-Strings in der Schleife zusammensetzen: Der Heap von MicroPython ist klein, und Fragmentierung zeigt sich als MemoryError nach Stunden Laufzeit, nicht nach Minuten.

JSON veröffentlichen Sie am einfachsten mit dem eingebauten Modul json: client.publish(topic, json.dumps({"temp": 22.4})). Befehle parsen Sie genauso mit json.loads(msg) im Callback – eingebettet in ein try, damit eine fehlerhafte Nachricht das Gerät nicht zum Absturz bringt.

Grenzen von umqtt, die Sie kennen sollten

Einschränkung Auswirkung Abhilfe
Nur MQTT 3.1.1 Keine MQTT-5-Properties, Reason Codes oder Shared-Subscription-Funktionen Einen anderen Client verwenden, wenn Sie MQTT 5 benötigen
Nur QoS 0 und 1 QoS 2 wird nicht unterstützt QoS 1 reicht für die meisten Telemetriedaten und Befehle
Keine automatischen Keepalive-Pings Leerlaufende Verbindungen werden getrennt Regelmäßig ping() aufrufen
Blockierende Socket-Aufrufe connect() und wait_msg() blockieren das Skript check_msg() verwenden; für komplexe Geräte einen asyncio-basierten Client erwägen
Kein erneutes Abonnieren beim Reconnect Nach einer Wiederverbindung kommen keine Befehle mehr an In der eigenen Reconnect-Funktion neu abonnieren
Keine Offline-Warteschlange Während der Trennung veröffentlichte Nachrichten gehen verloren Messwerte bei Bedarf selbst puffern

Für Geräte, die mehrere Dinge gleichzeitig erledigen müssen, gibt es asyncio-basierte MQTT-Clients aus der Community, die Keepalives und Wiederverbindung im Hintergrund übernehmen. Für einfache Sensoren und Aktoren bleibt umqtt die schlankeste Wahl. Auch im industriellen Umfeld hat das seinen Platz, etwa bei der Nachrüstung von Bestandsmaschinen mit Sensorik für Energiemonitoring oder Zustandsüberwachung, deren Werte dann in die Topic-Struktur des Werks einfließen.

TLS auf Port 8883 verwenden

Bei einem privaten Broker sollten Sie die Verbindung verschlüsseln. Neuere Versionen von umqtt.simple akzeptieren ein Argument ssl, das einen SSLContext entgegennimmt; ältere Versionen verwenden stattdessen ssl=True mit einem Dictionary ssl_params. Prüfen Sie also, welche Version Sie installiert haben.

import ssl
from umqtt.simple import MQTTClient

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="isrg-root-x1.der")   # Root-CA von Let's Encrypt

client = MQTTClient(
    b"YOUR_CLIENT_ID",
    "broker.connectmq.com",
    port=8883,
    user=b"YOUR_USERNAME",
    password=b"YOUR_PASSWORD",
    keepalive=30,
    ssl=ctx,
)
client.connect()

Kopieren Sie zuvor das Zertifikat ISRG Root X1 mit mpremote cp auf das Board. TLS benötigt einiges an RAM; Boards mit PSRAM kommen damit entspannter zurecht. Verwenden Sie genau die Client ID, den Benutzernamen und das Passwort, die für Ihren Broker generiert wurden, denn ConnectMQ bindet die Client ID an die ausgegebenen Zugangsdaten.

Weiterführende Anleitungen

Wenn Sie lieber in C++ arbeiten, zeigt die Anleitung zu ESP32-MQTT-Projekten mit PlatformIO ein gut strukturiertes Arduino-Setup, und die Anleitung zu ESP32 MQTT Last Will erklärt das Status-Muster aus dem ersten Skript. Das ESP32-IoT-Tutorial deckt das gesamte Bild vom Gerät bis zum Dashboard ab, und wenn Sie neu im Protokoll sind, beginnen Sie mit den Aufgaben eines MQTT-Brokers.

Verbinden Sie Ihre MicroPython-Geräte mit ConnectMQ

Alle Beispiele hier funktionieren ohne Registrierung mit dem öffentlichen MQTT-Broker, der für Tests und Prototypen gedacht ist, nicht für Produktionsdaten. Wenn Sie über Experimente hinausgehen, bietet Ihnen ConnectMQ einen privaten Cloud-MQTT-Broker mit TLS auf Port 8883 und einem MQTT-Client im Browser, mit dem Sie prüfen, was Ihr Board sendet. Erstellen Sie einen im kostenlosen Serverless-Plan unter app.connectmq.com – keine Kreditkarte erforderlich.

Häufig gestellte Fragen

Unterstützt umqtt MQTT 5?

Nein. Sowohl umqtt.simple als auch umqtt.robust implementieren MQTT 3.1.1. Broker mit MQTT-5-Unterstützung wie ConnectMQ akzeptieren weiterhin 3.1.1-Verbindungen.

Warum trennt sich mein MicroPython-MQTT-Client nach einer Minute?

Sie haben ein Keepalive festgelegt, senden innerhalb dieses Intervalls aber nichts. umqtt pingt nicht automatisch, rufen Sie also regelmäßig client.ping() auf oder veröffentlichen Sie häufiger, als das Keepalive vorgibt.

Was ist der Unterschied zwischen wait_msg() und check_msg()?

wait_msg() blockiert, bis eine Nachricht eintrifft. check_msg() verarbeitet eine wartende Nachricht, falls vorhanden, und kehrt sofort zurück – ideal für Schleifen, die auch Sensoren auslesen.

Kann umqtt QoS 2 verwenden?

Nein. Es unterstützt QoS 0 und 1, was die meisten Anwendungsfälle auf Geräten abdeckt.

Verwandte Artikel

ESP32 NTP MQTT Zeitstempel: genaue Uhrzeit in jeder Nachricht

Synchronisieren Sie die ESP32-Uhr per NTP, formatieren Sie Zeitstempel in ISO 8601 und Epoch-Millisekunden und hängen Sie sie an MQTT-Payloads, damit Messwerte ihren echten Erfassungszeitpunkt behalten, auch wenn sie verspätet veröffentlicht werden.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.