Zum Inhalt springen
Alle Beiträge

29. Oktober 202610 Min. Lesezeit

Raspberry Pi Pico 2 W und MicroPython: WLAN und eine LED, die per Browser schaltet

Das erste Projekt auf dem Pico 2 W: MicroPython aufspielen, Werkzeuge unter Fedora, WLAN-Verbindung und eine kleine Seite im Heimnetz, die die LED ein- und ausschaltet. Zuerst mit nackten Sockets, dann mit Microdot.

Raspberry Pi Pico 2 W und MicroPython: WLAN und eine LED, die per Browser schaltet

Der Raspberry Pi Pico 2 W ist eine kleine Platine mit dem Mikrocontroller RP2350 und dem Funkmodul Infineon CYW43439 (WLAN 2,4 GHz und Bluetooth). Ich zeige darauf ein einfaches erstes „Internet der Dinge“-Projekt: eine Webseite im lokalen Netz mit den Schaltflächen ON und OFF, um eine LED zu steuern. Schritt für Schritt: MicroPython aufspielen, Werkzeuge unter Fedora (Thonny oder mpremote), WLAN und HTTP-Server, zuerst auf nackten Sockets, dann mit der Bibliothek Microdot.

Befehle und API stütze ich auf die offizielle Dokumentation von Raspberry Pi und MicroPython. Der Code ist für die aktuelle MicroPython-Version geschrieben; in älteren fehlen unter anderem die Konstante WLAN.IF_STA und die Methode ipconfig().

Was Sie brauchen

  • Raspberry Pi Pico 2 W (unbedingt die Version W, denn nur sie hat das Funkmodul),

  • ein Micro-USB-Kabel mit Datenleitungen (ein reines Ladekabel reicht nicht),

  • einen Computer mit Linux (die Beispiele gelten für Fedora),

  • ein WLAN im 2,4-GHz-Band. Der CYW43439 des Pico 2 W arbeitet nur in einem Band und sieht daher keine „reinen 5-GHz“-Netze.

Schritt 1: MicroPython aufspielen (UF2 und BOOTSEL)

Der RP2350 hat im ROM einen Bootloader, der sich nach dem Anschluss an USB als gewöhnliches Laufwerk meldet. Die Firmware spielt man auf, indem man eine .uf2-Datei auf dieses Laufwerk kopiert.

  1. Laden Sie die UF2-Datei für den Pico 2 W von der MicroPython-Seite (micropython.org/download/RPI_PICO2_W/) oder aus der Raspberry-Pi-Dokumentation herunter. Vorsicht: Es gibt getrennte Dateien für Pico, Pico W, Pico 2 und Pico 2 W. Die Datei einer anderen Platine unterstützt kein WLAN oder startet gar nicht.

  2. Halten Sie die Taste BOOTSEL gedrückt und stecken Sie erst dann das USB-Kabel am Computer an.

  3. Sobald das Laufwerk mit dem Namen RP2350 erscheint, lassen Sie die Taste los.

  4. Ziehen Sie die .uf2-Datei auf dieses Laufwerk. Die Platine startet von selbst neu, das Laufwerk verschwindet, und im System erscheint ein serieller Port mit der MicroPython-REPL-Konsole.

Code
   [BOOTSEL gedrückt] ──► USB anstecken ──► Laufwerk "RP2350"
                                              │
                           *.uf2 kopieren ◄───┘
                                │
                       Neustart ─┴─► /dev/ttyACM0 (MicroPython-REPL)

Auf der Download-Seite gibt es auch eine Variante für die RISC‑V-Kerne (der RP2350 hat zwei Arm-Cortex‑M33-Kerne und zwei Hazard3-RISC‑V-Kerne). Wählen Sie zunächst die normale Standarddatei.

Später, wenn MicroPython bereits läuft, gelangen Sie ohne Taste in den Bootloader: In der REPL genügt machine.bootloader(), im Terminal mpremote bootloader.

Schritt 2: Werkzeuge unter Fedora

Berechtigungen für den seriellen Port

Unter Fedora gehören die Geräte /dev/ttyACM* zur Gruppe dialout. Fügen Sie sich dieser Gruppe hinzu und melden Sie sich ab und wieder an, denn ein neues Terminalfenster allein genügt nicht:

bash
sudo usermod -aG dialout "$USER"

Option A: Thonny (grafisch)

Thonny ist in den offiziellen Fedora-Repositories enthalten:

bash
sudo dnf install thonny

Wählen Sie unten rechts im Fenster den MicroPython-Interpreter für den Raspberry Pi Pico und den passenden Port (die Namen der Menüeinträge unterscheiden sich leicht zwischen den Thonny-Versionen). Ab jetzt ist das untere Fenster die REPL auf der Platine, und „Speichern unter… → Raspberry Pi Pico“ schreibt die Datei direkt in den Flash-Speicher.

Option B: mpremote (Terminal)

mpremote ist das offizielle Werkzeug des MicroPython-Projekts. Es ist nicht in den Fedora-Repositories, aber auf PyPI, daher installiert man es am bequemsten über pipx:

bash
sudo dnf install pipx
pipx install mpremote

mpremote connect list        # Liste der erkannten Platinen
mpremote                     # Verbindung zur ersten gefundenen Platine und REPL
mpremote a0                  # Kurzform für /dev/ttyACM0

Nützlich ist auch mpremote run datei.py, das eine Datei vom Computer ausführt, ohne sie auf die Platine zu kopieren.

Ein schneller Test in der REPL:

Python
from machine import Pin
led = Pin("LED", Pin.OUT)
led.on()
led.off()
led.toggle()

Beim Pico 2 W (wie beim Pico W) ist die LED nicht mit dem RP2350 verbunden, sondern mit dem Pin WL_GPIO0 des Funkchips CYW43439. Deshalb spreche ich sie nicht über eine GPIO-Nummer an, sondern über den Namen "LED". MicroPython gibt den Befehl selbst über den Funktreiber weiter. Das bestätigen sowohl das Datenblatt des Pico 2 W als auch das Raspberry-Pi-Dokument zur Internetanbindung der Platinen der Pico-W-Serie.

Schritt 3: Verbindung mit dem WLAN

Bewahren Sie die Zugangsdaten in einer separaten Datei secrets.py auf. So landen sie nicht versehentlich im Repository: Tragen Sie sie in die .gitignore ein.

Python
# secrets.py
SSID = "NameIhresNetzwerks"
PASSWORD = "IhrWLANPasswort"

Eine Funktion, die sich mit dem Netzwerk verbindet (ich verwende sie in main.py):

Python
import time
import network
from machine import Pin

led = Pin("LED", Pin.OUT)


def polacz_wifi(ssid, haslo, limit_s=20):
    network.country("DE")  # Ländercode für die Funkvorschriften
    wlan = network.WLAN(network.WLAN.IF_STA)
    wlan.active(True)
    wlan.config(pm=0xA11140)  # schaltet den Energiesparmodus aus, der Server antwortet schneller
    wlan.connect(ssid, haslo)

    for _ in range(limit_s):
        status = wlan.status()
        if status == 3:  # CYW43_LINK_UP: verbunden und IP-Adresse vorhanden
            break
        if status < 0:  # -1 Fehler, -2 kein Netz, -3 falsches Passwort
            raise RuntimeError("WLAN-Fehler, Status %d" % status)
        led.toggle()  # Blinken = Verbindungsaufbau läuft
        time.sleep(1)
    else:
        raise RuntimeError("Verbindung in %d s nicht möglich" % limit_s)

    led.off()
    ip = wlan.ipconfig("addr4")[0]
    print("Verbunden, IP-Adresse:", ip)
    return ip

Erläuterung:

  • network.country("DE"): Standardmäßig ist das Land „XX“ (worldwide), und laut Raspberry Pi können ohne gesetztes Land einige Kanäle nicht verfügbar sein. Setzen Sie hier den Code Ihres Landes.

  • Die Statuscodes stammen aus dem CYW43-Treiber und sind im Raspberry-Pi-Dokument beschrieben: 0 getrennt, 1 Verbindung wird aufgebaut, 2 verbunden ohne IP, 3 verbunden mit IP, -1 Fehler, -2 Netz nicht gefunden, -3 Authentifizierungsfehler. Die Schleife mit Zeitlimit ist nötig, denn ohne sie sieht ein Tippfehler im Passwort wie eine eingefrorene Platine aus.

  • pm=0xA11140 ist ein Wert aus der Raspberry-Pi-Dokumentation, der den WLAN-Energiesparmodus ausschaltet (die MicroPython-Dokumentation beschreibt auch die Konstanten WLAN.PM_NONE, PM_PERFORMANCE, PM_POWERSAVE).

  • wlan.ipconfig("addr4") liefert ein Paar (Adresse, Maske). Ältere Anleitungen verwenden wlan.ifconfig(), das die MicroPython-Dokumentation als veraltet markiert.

Schritt 4: HTTP-Server auf Sockets

Zuerst die Version ganz ohne Bibliotheken, denn so sieht man am besten, was „unter der Haube“ passiert. Der Browser sendet eine Anfrage im Stil GET /on HTTP/1.1. Das Programm liest die erste Zeile, entnimmt ihr den Pfad und reagiert:

Code
 Browser                          Pico 2 W
 ───────                          ────────
 GET /        ───────────────►    Seite mit Schaltflächen
 GET /on      ───────────────►    led.on(),  Antwort 303 → /
 GET /off     ───────────────►    led.off(), Antwort 303 → /
 GET /irgendwas ─────────────►    404

Die Weiterleitung 303 See Other nach dem Klick sorgt dafür, dass in der Adressleiste / stehen bleibt, sodass ein Neuladen der Seite die LED nicht erneut umschaltet.

Die komplette Datei main.py (die Funktion polacz_wifi fügen Sie aus dem vorherigen Schritt an der Stelle des Kommentars ein):

Python
# main.py
import socket
import time
import network
from machine import Pin
import secrets

led = Pin("LED", Pin.OUT)
stan_led = False

# ... hier die Funktion polacz_wifi() aus dem vorherigen Schritt ...

STRONA = """<!DOCTYPE html>
<html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pico 2 W - LED</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 2em; }
a { display: inline-block; font-size: 1.6em; margin: .4em; padding: .4em 1.2em;
    border: 2px solid #333; border-radius: .4em; text-decoration: none; color: #333; }
</style></head><body>
<h1>LED am Pico 2 W</h1>
<p>Zustand der LED: <b>__STAN__</b></p>
<a href="/on">ON</a> <a href="/off">OFF</a>
</body></html>
"""


def odpowiedz(klient, status, naglowki=b"", tresc=b""):
    klient.sendall(b"HTTP/1.0 " + status + b"\r\n" + naglowki + b"Connection: close\r\n\r\n")
    if tresc:
        klient.sendall(tresc)


def obsluz(klient):
    global stan_led
    plik = klient.makefile("rwb", 0)
    pierwsza = plik.readline()  # z. B. b"GET /on HTTP/1.1\r\n"
    while True:  # restliche Header überspringen
        linia = plik.readline()
        if not linia or linia == b"\r\n":
            break
    czesci = pierwsza.split()
    sciezka = czesci[1] if len(czesci) > 1 else b"/"

    if sciezka == b"/on":
        led.on()
        stan_led = True
        odpowiedz(klient, b"303 See Other", b"Location: /\r\n")
    elif sciezka == b"/off":
        led.off()
        stan_led = False
        odpowiedz(klient, b"303 See Other", b"Location: /\r\n")
    elif sciezka == b"/":
        html = STRONA.replace("__STAN__", "an" if stan_led else "aus")
        odpowiedz(klient, b"200 OK", b"Content-Type: text/html; charset=utf-8\r\n", html.encode())
    else:
        odpowiedz(klient, b"404 Not Found", b"Content-Type: text/plain\r\n", b"Diese Seite gibt es nicht\n")


def serwer(ip):
    adres = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
    s = socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(adres)
    s.listen(1)
    print("Im Browser öffnen: http://%s/" % ip)
    while True:
        klient, zdalny = s.accept()
        try:
            klient.settimeout(5)
            obsluz(klient)
        except OSError as e:
            print("Verbindungsfehler:", e)
        finally:
            klient.close()


ip = polacz_wifi(secrets.SSID, secrets.PASSWORD)
serwer(ip)

Aufspielen und starten:

bash
mpremote fs cp secrets.py :secrets.py   # der Doppelpunkt steht für die Platine
mpremote fs cp main.py :main.py
mpremote reset
mpremote repl        # hier sehen Sie die IP-Adresse, REPL verlassen mit Strg-]

MicroPython führt main.py nach dem Start automatisch aus, sodass sich eine an ein USB-Netzteil angeschlossene Platine von selbst mit dem Netzwerk verbindet und die Seite bereitstellt. Öffnen Sie im selben Netzwerk die Adresse aus der Konsole, z. B. http://192.168.1.50/.

Das Programm beenden Sie mit Strg‑C in der REPL.

Einschränkungen dieser Version

Dieser Server ist synchron: Er bedient jeweils nur eine Verbindung und tut beim Warten auf den Client nichts anderes. Das Raspberry-Pi-Dokument weist selbst darauf hin, dass man bei anspruchsvolleren Anwendungen Anfragen besser asynchron bearbeitet. Zum Lernen reicht er völlig aus.

Schritt 5: Dasselbe mit Microdot

Microdot ist ein kleines Web-Framework im Stil von Flask für CPython und MicroPython. Es wird aktiv weiterentwickelt und bearbeitet Anfragen asynchron (asyncio).

Die Microdot-Dokumentation empfiehlt für MicroPython, die Datei microdot.py aus dem Repository manuell zu kopieren (Verzeichnis src/microdot/). Für einen einfachen Server genügt diese eine Datei:

bash
mpremote fs cp microdot.py :microdot.py
Python
# main.py - Version mit Microdot
from machine import Pin
from microdot import Microdot, Response, redirect
import secrets

led = Pin("LED", Pin.OUT)
stan_led = False

# ... hier die Importe time und network, die Funktion polacz_wifi() und die Konstante STRONA
# aus den vorherigen Schritten ...

app = Microdot()
Response.default_content_type = "text/html; charset=utf-8"


@app.route("/")
async def index(request):
    return STRONA.replace("__STAN__", "an" if stan_led else "aus")


@app.route("/on")
async def wlacz(request):
    global stan_led
    led.on()
    stan_led = True
    return redirect("/")


@app.route("/off")
async def wylacz(request):
    global stan_led
    led.off()
    stan_led = False
    return redirect("/")


polacz_wifi(secrets.SSID, secrets.PASSWORD)
app.run(port=80)  # standardmäßig lauscht Microdot auf Port 5000

Microdot konfiguriert das Netzwerk nicht, daher verbinde ich mich selbst mit dem WLAN. Der Standardtyp der Antwort ist text/plain, daher die Einstellung default_content_type.

Sicherheit: nur im Heimnetz

Dieser Server hat weder Authentifizierung noch Verschlüsselung. Jeder im selben Netzwerk, der die IP-Adresse kennt, kann die LED umschalten. Bei einer LED ist das harmlos, aber bevor Sie ein Relais anschließen, beachten Sie einige Regeln:

  • Nur im lokalen Netz verwenden. Leiten Sie Port 80 am Router nicht weiter und stellen Sie die Platine nicht ins Internet.

  • HTTP ist Klartext. Alles, was Sie übertragen, lässt sich im Netzwerk mitlesen. Übertragen Sie auf diese Weise keine Passwörter.

  • GET-Anfragen ändern den Zustand. Bequem im Beispiel, aber in einem ernsthafteren Projekt sollte das Umschalten per POST erfolgen.

  • Schützen Sie secrets.py. Das Passwort liegt im Klartext auf der Platine, und jeder, der den Pico per USB anschließt, kann es auslesen.

Wenn etwas nicht funktioniert

  • Kein Laufwerk RP2350: Prüfen Sie das Kabel (überträgt es Daten?) und ob die Taste BOOTSEL vor dem Anstecken gedrückt war.

  • Permission denied bei /dev/ttyACM0: Die Gruppe dialout fehlt, oder Sie haben sich nicht erneut angemeldet. Prüfen Sie es mit dem Befehl groups.

  • Status -2: Die Platine sieht das Netz nicht. Prüfen Sie, ob das Netz im 2,4-GHz-Band sendet und ob die SSID exakt eingetragen ist (Groß- und Kleinschreibung zählt).

  • Status -3: falsches Passwort.

  • Fehler „Adresse bereits in Benutzung“ (EADDRINUSE) nach erneutem Start des Skripts aus der REPL: Am einfachsten führen Sie mpremote reset aus.

  • Kein WLAN oder die LED reagiert nicht: Prüfen Sie, ob Sie die Firmware RPI_PICO2_W und nicht RPI_PICO2 aufgespielt haben. Die Datei für den Pico 2 ohne W ist nicht für den Betrieb des Funkchips gedacht, und beim Pico 2 W hängt die LED eben am Funkchip.

Wie geht es weiter

Dieses Grundgerüst (WLAN, Server und Ausgangssteuerung) lässt sich leicht um das Auslesen eines Sensors oder einen Endpunkt mit JSON erweitern. Wie man ein Bedienfeld entwirft, das den Zustand des Geräts ehrlich anzeigt, beschreibe ich im Beitrag über die Bedienoberfläche zur Steuerung eines Geräts, und den Pico 2 W vergleiche ich mit dem ESP32 in einem eigenen Beitrag. Wenn Sie ein maßgefertigtes Gerät brauchen, schauen Sie auf die Seite Elektronik. Fragen und eigene Varianten des Projekts können Sie im wirelab-Forum posten.

Quellen

Schreiben