Ein Pico-W-Sensor mit Batterie und MQTT funktioniert nur, wenn das Board fast sein ganzes Leben schlafend verbringt und nur wenige Sekunden wach ist. In diesem Tutorial bauen Sie einen MicroPython-Sensorknoten, der per Timer aufwacht, einen Sensor und die eigene Batteriespannung ausliest, eine QoS-1-Nachricht an einen MQTT-Broker publiziert, das Funkmodul abschaltet und zurück in den Lightsleep geht. Außerdem erfahren Sie, welche Designentscheidungen die Akkulaufzeit wirklich verlängern und welche vergebliche Mühe sind.
Warum Batteriebetrieb beim Pico W schwierig ist
Der Pico W wurde als günstiges, flexibles Mikrocontroller-Board entworfen, nicht als Ultra-Low-Power-Sensorplattform. Der RP2040 selbst ist effizient genug, aber drei Dinge arbeiten im Batteriebetrieb gegen Sie:
- Der WLAN-Chip CYW43439 zieht beim Verbindungsaufbau und Senden weit mehr Strom als der Mikrocontroller beim Rechnen. Jede Sekunde mit aktivem Funk kostet mehr als Minuten Schlaf.
- Der Buck-Boost-Regler auf dem Board hat einen Ruhestrom, der nie verschwindet, auch wenn der RP2040 schläft.
- Die Schlafmodi in MicroPython sind im Vergleich zu Chips wie dem ESP32 begrenzt.
machine.lightsleep()stoppt die Takte und behält den RAM, was nützlich ist, aber das Board zieht trotzdem spürbar mehr als ein eigens entwickelter Low-Power-Knoten.
Die praktische Folge ist einfach: Die Akkulaufzeit wird davon bestimmt, wie oft Sie aufwachen und wie lange die WLAN-Verbindung dauert. Den Python-Code zum Auslesen eines Sensors zu optimieren, bringt fast nichts. Ein Publish-Intervall von 30 Sekunden auf 5 Minuten zu verlängern oder die Verbindungszeit von acht auf drei Sekunden zu senken, bringt dagegen viel.
Wenn Sie einen Pico W noch gar nicht mit MQTT verbunden haben, beginnen Sie mit dem Leitfaden Raspberry Pi Pico W und MQTT und kehren Sie hierher zurück, sobald ein einfaches Publish funktioniert.
Das Duty-Cycle-Design
Jedes Aufwachen folgt demselben kurzen Ablauf. Ihn streng linear zu halten (keine Hintergrundaufgaben, keine dauerhafte Verbindung), macht ihn vorhersehbar.
- Aus dem Lightsleep aufwachen (oder nach einem Stromausfall kalt booten).
- Die Batteriespannung vor dem Einschalten des WLANs messen.
- Den Sensor auslesen.
- WLAN mit statischer Konfiguration und hartem Timeout einschalten.
- Mit dem Broker verbinden, eine Nachricht mit QoS 1 publizieren und auf die Bestätigung warten.
- Sauber trennen, das Funkmodul abschalten und deinitialisieren.
- Für das konfigurierte Intervall in den Lightsleep gehen, dann von vorn.
| Designentscheidung | Wirkung auf die Akkulaufzeit | Empfehlung |
|---|---|---|
| Publish-Intervall | Größter Einzelfaktor | 5 bis 15 Minuten für Raumklimadaten |
| WLAN-Verbindungszeit | Zweitgrößter Faktor | Starkes Signal, WPA2, kurzer Timeout |
| MQTT im Schlaf verbunden lassen | Funk bleibt teilweise aktiv | Im Batteriebetrieb niemals |
| Publish mit QoS 1 | Ein zusätzlicher Round Trip | Lohnt sich, Sie wissen, dass die Nachricht ankam |
Lightsleep vs. aktives time.sleep() |
Lightsleep stoppt die Takte | Zwischen den Zyklen immer Lightsleep |
| Blinkende LED als Statusanzeige | Kleiner, aber dauerhafter Verbrauch | In der Produktion deaktivieren |
| Externer Timer-Chip (TPL5110 o. ä.) | Trennt das Board komplett vom Strom | Beste Option für mehrmonatige Laufzeit |
Hardware und Verdrahtung
Sie benötigen einen Pico W (oder Pico 2 W), einen Sensor und eine Stromquelle an VSYS. Ein gängiger Aufbau sind drei AA- oder AAA-Zellen in Reihe direkt an VSYS oder eine einzelne LiPo-Zelle über eine Schutzplatine. Speisen Sie nicht mehr ein, als das offizielle Datenblatt für VSYS erlaubt, und schließen Sie niemals Batterien ohne Diode an VBUS an, während USB eingesteckt ist.
Als Sensor eignet sich jedes I2C-Bauteil mit geringem Standby-Strom. Dieses Beispiel nutzt einen BME280 an I2C0 (GP4 = SDA, GP5 = SCL), die Duty-Cycle-Logik ist aber vom Sensor unabhängig.
Der Pico W kann seine eigene Versorgungsspannung an ADC3 messen, der über einen 1:3-Teiler mit VSYS verbunden ist. Beim Pico W teilt sich dieser Pin (GP29) den SPI-Takt mit dem WLAN-Chip, daher müssen Sie ihn bei ausgeschaltetem Funk und mit GP25 auf High lesen. Deshalb erfolgt die Spannungsmessung als Erstes im Zyklus.
MicroPython und die MQTT-Bibliothek vorbereiten
Flashen Sie einen aktuellen MicroPython-Build für den Pico W aus der offiziellen Raspberry-Pi-Dokumentation und installieren Sie dann die Bibliothek umqtt.simple und einen BME280-Treiber mit mpremote:
pip install mpremote
mpremote mip install umqtt.simple
mpremote cp bme280.py :bme280.py
Legen Sie auf dem Board eine secrets.py an, damit Zugangsdaten nicht im Hauptskript stehen:
# secrets.py
WIFI_SSID = "your-ssid"
WIFI_PASSWORD = "your-wifi-password"
MQTT_HOST = "broker.connectmq.com"
MQTT_PORT = 1883
MQTT_CLIENT_ID = "pico-bat-7f3a91" # must be non-empty and unique
MQTT_USER = None # set YOUR_USERNAME for a private broker
MQTT_PASSWORD = None # set YOUR_PASSWORD for a private broker
TOPIC = "connectmq/demo/7f3a91/garden/climate"
INTERVAL_S = 600 # 10 minutes between readings
Der Öffentliche MQTT-Broker unter broker.connectmq.com akzeptiert anonyme Verbindungen mit jeder nicht leeren Client ID. Alles, was dort publiziert wird, ist für jeden lesbar; verwenden Sie also ein zufälliges Topic-Präfix und senden Sie niemals personenbezogene Daten. Er ist für Tests und Prototypen gedacht, nicht für Produktionsdaten. Der Leitfaden zum kostenlosen Öffentlichen MQTT-Broker listet seine Grenzen auf.
Das vollständige Batterie-MQTT-Skript
Speichern Sie dies als main.py. Es läuft bei jedem Boot automatisch, und weil die Schleife Lightsleep statt eines Resets verwendet, überlebt der RAM-Zustand wie der Zykluszähler zwischen den Aufwachvorgängen.
# main.py - battery-powered Pico W MQTT sensor
import time
import json
import network
import machine
from machine import Pin, ADC, I2C
from umqtt.simple import MQTTClient
import bme280
import secrets
led = Pin("LED", Pin.OUT)
led.off()
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100_000)
cycle = 0
def read_vsys():
"""Read VSYS on the Pico W. Must run while the Wi-Fi chip is off."""
Pin(25, Pin.OUT).value(1) # deselect the CYW43 SPI bus
Pin(29, Pin.IN, None) # release the shared pin for the ADC
adc = ADC(3)
total = 0
for _ in range(8):
total += adc.read_u16()
time.sleep_ms(2)
raw = total / 8
return round(raw * 3.3 * 3 / 65535, 3)
def read_sensor():
sensor = bme280.BME280(i2c=i2c)
t, p, h = sensor.read_compensated_data()
# The driver returns hundredths of a degree, Pa*256 and %RH*1024
return round(t / 100, 2), round(h / 1024, 1), round(p / 25600, 1)
def wifi_up(timeout_ms=10_000):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(secrets.WIFI_SSID, secrets.WIFI_PASSWORD)
start = time.ticks_ms()
while not wlan.isconnected():
if time.ticks_diff(time.ticks_ms(), start) > timeout_ms:
return wlan, False
time.sleep_ms(100)
return wlan, True
def wifi_down(wlan):
try:
wlan.disconnect()
except OSError:
pass
wlan.active(False)
wlan.deinit()
def publish(payload):
client = MQTTClient(
secrets.MQTT_CLIENT_ID,
secrets.MQTT_HOST,
port=secrets.MQTT_PORT,
user=secrets.MQTT_USER,
password=secrets.MQTT_PASSWORD,
keepalive=30,
)
client.connect()
# QoS 1: publish() blocks until the broker returns PUBACK
client.publish(secrets.TOPIC, payload, qos=1)
client.disconnect()
while True:
cycle += 1
vbat = read_vsys()
try:
temp_c, rh, hpa = read_sensor()
except OSError:
temp_c, rh, hpa = None, None, None
awake_start = time.ticks_ms()
wlan, ok = wifi_up()
if ok:
msg = json.dumps({
"t": temp_c, "rh": rh, "hpa": hpa,
"vbat": vbat, "cycle": cycle,
"rssi": wlan.status("rssi"),
"awake_ms": time.ticks_diff(time.ticks_ms(), awake_start),
})
try:
publish(msg)
except OSError as exc:
print("MQTT failed:", exc)
wifi_down(wlan)
# Back off harder when the battery is low to protect the cells
sleep_s = secrets.INTERVAL_S if vbat > 3.3 else secrets.INTERVAL_S * 3
machine.lightsleep(sleep_s * 1000)
Einige Details sind bewusst gewählt. Das Skript meldet in jeder Nachricht awake_ms und rssi, damit Sie von der Broker-Seite aus sehen, ob ein Knoten zu lange für den Verbindungsaufbau braucht. Der Zweig für niedrige Batteriespannung verdreifacht das Intervall, statt ganz aufzuhören, was Zeit für den Zellenwechsel verschafft, ohne den Knoten zu verlieren. Und deinit() nach active(False) stellt sicher, dass der CYW43-Treiber das Funkmodul freigibt, bevor das Board schläft.
Die Batterie-MQTT-Nachrichten des Pico W testen
Bevor Sie auf Zellen umstellen, testen Sie über USB mit einem kurzen Intervall wie 30 Sekunden. Abonnieren Sie vom Laptop aus:
mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-7f3a91 \
-t 'connectmq/demo/7f3a91/#' -v
Sie sollten pro Zyklus eine JSON-Nachricht sehen. Beobachten Sie das Feld awake_ms. In einem gesunden Netzwerk pendelt es sich in einem engen, reproduzierbaren Bereich ein. Schwankt es stark, liegt das Problem beim Access Point, nicht in Ihrem Code.
Wenn der Pico W am USB schläft, trennt sich die serielle REPL. Das ist normal. Um wieder die Kontrolle zu bekommen, ziehen Sie den Stecker, halten Sie bei Bedarf BOOTSEL oder verwenden Sie mpremote direkt nach dem Einschalten, bevor main.py in den ersten Schlaf geht. Ein nützlicher Trick ist ein Zwei-Sekunden-Fenster ganz oben in main.py, das zur REPL aussteigt, wenn ein Taster an einem GPIO-Pin gedrückt ist.
Mehr Akkulaufzeit aus dem Pico W herausholen
Sobald der Grundzyklus funktioniert, bringen diese Verfeinerungen den größten Nutzen:
- Zuerst das Intervall verlängern. Halbieren Sie die Zahl der Aufwachvorgänge, halbiert sich ungefähr die Funkenergie, und die ist der größte Verbraucher.
- Messwerte bündeln. Lesen Sie den Sensor während des Lightsleeps jede Minute aus, sammeln Sie die Werte in einer Liste und publizieren Sie alle zusammen alle zehn Minuten in einer Nachricht. Sie bezahlen das WLAN einmal statt zehnmal.
- Den WLAN-Pfad verbessern. Platzieren Sie den Knoten dort, wo das Signal stark ist. Ein schwaches Signal bedeutet Wiederholungen, langsamere Datenraten und längere Wachzeit.
- Einen externen Power-Timer verwenden. Ein Timer-Chip, der das ganze Board vom Strom trennt und es planmäßig wieder einschaltet, eliminiert Regler- und Schlafströme vollständig. In diesem Design läuft das Skript einmal ab Kaltstart, publiziert und signalisiert dem Timer dann das Abschalten. Sie verlieren den RAM zwischen den Zyklen; speichern Sie Zähler also sparsam im Flash oder lassen Sie den Broker den Zustand über retained messages halten.
- Alles abschalten, was blinkt. Status-LEDs auf dem Pico und auf Sensor-Breakout-Boards ziehen permanent Strom. Entfernen oder trennen Sie die Power-LED auf Breakouts, wo es geht.
Produktion: auf private Zugangsdaten und TLS umstellen
Der öffentliche MQTT-Broker ist ideal für die Entwicklung, ein eingesetzter Sensor sollte aber eigene Zugangsdaten verwenden. Bei einem verwalteten MQTT-Broker wie ConnectMQ legen Sie im Portal einen Broker an und erhalten einen generierten Benutzernamen, ein Passwort und eine Client ID. Tragen Sie diese als YOUR_USERNAME, YOUR_PASSWORD und YOUR_CLIENT_ID in secrets.py ein. Die Client ID ist an die ausgegebenen Zugangsdaten gebunden, daher braucht jeder Knoten die für ihn generierte.
TLS auf Port 8883 fügt jedem Aufwachen einen Handshake hinzu, was zusätzliche Wachzeit und damit Batterie kostet. Für Sensoren in einem vertrauenswürdigen Heimnetz akzeptieren manche unverschlüsseltes TCP; für alles, was ein Gebäude verlässt, ist die Verschlüsselung die Kosten wert. Das Tutorial Pico W MQTT über TLS zeigt die Einrichtung des SSL-Kontexts in MicroPython. Messen Sie awake_ms vorher und nachher, damit Sie die tatsächliche Auswirkung auf Ihren Knoten kennen.
Auch retained messages sind hier nützlich. Publizieren Sie den letzten Messwert mit Retain-Flag, zeigt ein Dashboard, das Stunden später geöffnet wird, sofort den aktuellsten Wert, statt bis zu zehn Minuten auf das nächste Aufwachen zu warten. ConnectMQ unterstützt retained messages in allen Plänen, und mit dem Portal-Dashboard und dem Live-Topic-Explorer sehen Sie die Nachrichten jedes Knotens eintreffen, ohne einen Subscriber zu schreiben.
Fehlerbehebung
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
vbat liegt nahe null oder ist zufällig |
ADC bei aktivem WLAN gelesen | VSYS vor wlan.active(True) lesen |
| Knoten publiziert einmal und hört dann auf | WLAN vor dem Lightsleep nicht deinitialisiert | active(False) und deinit() aufrufen |
OSError: -2 beim Verbinden |
DNS fehlgeschlagen oder WLAN nicht vollständig verbunden | isconnected() prüfen und einmal wiederholen |
awake_ms steigt immer weiter |
Schwaches Signal oder ausgelasteter Access Point | Knoten oder AP umsetzen |
| Batterie ist viel früher leer als erwartet | LED, Sensor-Standby oder Reglerverbrauch | LEDs entfernen, Schlafmodus des Sensors prüfen, Power-Timer ergänzen |
Mit ConnectMQ starten
Wenn Ihr Pico-W-Prototyp bereit ist, die Werkbank zu verlassen, legen Sie im ConnectMQ-Portal unter app.connectmq.com einen privaten Broker an. Der kostenlose Serverless-Plan braucht keine Kreditkarte, unterstützt QoS 1 und bis zu 25 Verbindungen, was eine kleine Flotte von Batteriesensoren bequem abdeckt. Weitere Pi-Projekte finden Sie im Raspberry-Pi-MQTT-Tutorial, das die ganze Reihe bündelt.
Häufig gestellte Fragen
Wie lange hält ein Pico W mit MQTT im Batteriebetrieb?
Das hängt vor allem vom Publish-Intervall, der WLAN-Verbindungszeit und den verwendeten Zellen ab; messen Sie also Ihren eigenen Knoten, statt einer einzelnen Zahl zu vertrauen. Wenn Sie awake_ms und vbat über einige Tage protokollieren, erhalten Sie einen verlässlichen Trend zum Hochrechnen.
Kann der Pico W im Schlaf mit MQTT verbunden bleiben?
Nicht auf batteriefreundliche Weise. Eine aktive MQTT-Session erfordert, dass das Funkmodul verbunden bleibt, was Zellen schnell leert; verbinden Sie sich stattdessen bei jedem Aufwachen neu.
Sollte ich für einen Batteriesensor QoS 0 oder QoS 1 verwenden?
QoS 1 ist meist die bessere Wahl, weil publish() auf die Bestätigung des Brokers wartet, bevor Sie das Funkmodul abschalten. Der zusätzliche Round Trip ist gering im Vergleich zu den Kosten für die WLAN-Anmeldung.
Ist machine.deepsleep auf dem Pico W besser als Lightsleep?
Beim RP2040-Port von MicroPython ist der Unterschied gering, und Deepsleep setzt das Board zurück, sodass Sie den RAM-Zustand verlieren. Für die längste Laufzeit schlägt ein externer Timer, der den Strom komplett trennt, beide.



