Raspberry Pi Pico 2 W to mała płytka z mikrokontrolerem RP2350 i modułem radiowym Infineon CYW43439 (Wi‑Fi 2,4 GHz i Bluetooth). Pokazuję na niej prosty pierwszy projekt „internetu rzeczy”: stronę WWW w sieci lokalnej z przyciskami ON i OFF do sterowania diodą. Po kolei: wgranie MicroPythona, narzędzia na Fedorze (Thonny albo mpremote), Wi‑Fi i serwer HTTP, najpierw na gołych socketach, potem z biblioteką Microdot.
Polecenia i API opieram na oficjalnej dokumentacji Raspberry Pi i MicroPythona. Kod jest pisany pod aktualne wydanie MicroPythona; w starszych brakuje m.in. stałej WLAN.IF_STA i metody ipconfig().
Co będzie potrzebne
Raspberry Pi Pico 2 W (koniecznie wersja W, bo tylko ona ma radio),
kabel micro‑USB z liniami danych (sam kabel do ładowania nie wystarczy),
komputer z Linuksem (przykłady są dla Fedory),
sieć Wi‑Fi w paśmie 2,4 GHz. CYW43439 na Pico 2 W jest jednopasmowy, więc sieci „tylko 5 GHz” nie zobaczy.
Krok 1: wgranie MicroPythona (UF2 i BOOTSEL)
RP2350 ma w pamięci ROM bootloader, który po podłączeniu do USB udaje zwykły dysk. Firmware wgrywa się, kopiując na ten dysk plik .uf2.
Pobierz plik UF2 dla Pico 2 W ze strony MicroPythona (
micropython.org/download/RPI_PICO2_W/) albo z dokumentacji Raspberry Pi. Uważaj, bo osobne pliki są dla Pico, Pico W, Pico 2 i Pico 2 W. Plik od innej płytki nie obsłuży Wi‑Fi albo w ogóle nie ruszy.Przytrzymaj przycisk BOOTSEL i dopiero wtedy podłącz kabel USB do komputera.
Gdy pojawi się dysk o nazwie RP2350, puść przycisk.
Przeciągnij plik
.uf2na ten dysk. Płytka sama się zrestartuje, dysk zniknie, a w systemie pojawi się port szeregowy z konsolą REPL MicroPythona.
[BOOTSEL wciśnięty] ──► podłącz USB ──► dysk "RP2350"
│
skopiuj *.uf2 ◄───┘
│
restart ─┴─► /dev/ttyACM0 (REPL MicroPythona)
Na stronie pobierania jest też wariant dla rdzeni RISC‑V (RP2350 ma parę rdzeni Arm Cortex‑M33 i parę Hazard3 RISC‑V). Na początek wybierz zwykły, domyślny plik.
Później, gdy MicroPython już działa, do bootloadera wejdziesz bez przycisku: wystarczy w REPL wywołać machine.bootloader() albo w terminalu mpremote bootloader.
Krok 2: narzędzia na Fedorze
Uprawnienia do portu szeregowego
Na Fedorze urządzenia /dev/ttyACM* należą do grupy dialout. Dodaj się do niej i wyloguj się, a potem zaloguj ponownie, bo samo otwarcie nowego terminala nie wystarczy:
sudo usermod -aG dialout "$USER"
Opcja A: Thonny (graficznie)
Thonny jest w oficjalnych repozytoriach Fedory:
sudo dnf install thonny
W prawym dolnym rogu okna wybierz interpreter MicroPython dla Raspberry Pi Pico i odpowiedni port (nazwy pozycji w menu różnią się nieco między wersjami Thonny). Od tej chwili dolny panel to REPL na płytce, a „Zapisz jako… → Raspberry Pi Pico” zapisuje plik bezpośrednio w pamięci flash.
Opcja B: mpremote (terminal)
mpremote to oficjalne narzędzie projektu MicroPython. Nie ma go w repozytoriach Fedory, ale jest na PyPI, więc najwygodniej zainstalować je przez pipx:
sudo dnf install pipx
pipx install mpremote
mpremote connect list # lista wykrytych płytek
mpremote # połączenie z pierwszą znalezioną i REPL
mpremote a0 # skrót dla /dev/ttyACM0
Przydatne jest też mpremote run plik.py, które uruchamia plik z dysku komputera bez kopiowania go na płytkę.
Szybki test w REPL:
from machine import Pin
led = Pin("LED", Pin.OUT)
led.on()
led.off()
led.toggle()
Na Pico 2 W (tak jak na Pico W) dioda nie jest podłączona do RP2350, tylko do pinu WL_GPIO0 układu radiowego CYW43439. Dlatego nie odwołuję się do niej numerem GPIO, tylko nazwą "LED". MicroPython sam przekazuje polecenie przez sterownik radia. Potwierdza to zarówno datasheet Pico 2 W, jak i dokument Raspberry Pi o łączeniu płytek z serii Pico W z internetem.
Krok 3: połączenie z Wi‑Fi
Dane logowania trzymaj w osobnym pliku secrets.py. Dzięki temu nie trafią przypadkiem do repozytorium: dopisz go do .gitignore.
# secrets.py
SSID = "NazwaTwojejSieci"
PASSWORD = "TwojeHasloWiFi"
Funkcja łącząca się z siecią (używam jej w main.py):
import time
import network
from machine import Pin
led = Pin("LED", Pin.OUT)
def polacz_wifi(ssid, haslo, limit_s=20):
network.country("PL") # kod kraju dla przepisów radiowych
wlan = network.WLAN(network.WLAN.IF_STA)
wlan.active(True)
wlan.config(pm=0xA11140) # wyłącza oszczędzanie energii, serwer szybciej odpowiada
wlan.connect(ssid, haslo)
for _ in range(limit_s):
status = wlan.status()
if status == 3: # CYW43_LINK_UP: połączono i jest adres IP
break
if status < 0: # -1 błąd, -2 brak sieci, -3 złe hasło
raise RuntimeError("Błąd Wi-Fi, status %d" % status)
led.toggle() # mruganie = trwa łączenie
time.sleep(1)
else:
raise RuntimeError("Nie udało się połączyć w %d s" % limit_s)
led.off()
ip = wlan.ipconfig("addr4")[0]
print("Połączono, adres IP:", ip)
return ip
Wyjaśnienie:
network.country("PL"): domyślnie kraj to „XX” (worldwide), a według Raspberry Pi bez ustawienia kraju część kanałów może być niedostępna.Kody statusu pochodzą ze sterownika CYW43 i są opisane w dokumencie Raspberry Pi:
0rozłączony,1łączenie,2połączono bez IP,3połączono z IP,-1błąd,-2nie znaleziono sieci,-3błąd uwierzytelnienia. Pętla z limitem czasu jest potrzebna, bo bez niej literówka w haśle wygląda jak zawieszona płytka.pm=0xA11140to wartość z dokumentacji Raspberry Pi wyłączająca oszczędzanie energii Wi‑Fi (dokumentacja MicroPythona opisuje też stałeWLAN.PM_NONE,PM_PERFORMANCE,PM_POWERSAVE).wlan.ipconfig("addr4")zwraca parę (adres, maska). Starsze poradniki używająwlan.ifconfig(), które dokumentacja MicroPythona oznacza jako przestarzałe.
Krok 4: serwer HTTP na socketach
Najpierw wersja bez żadnych bibliotek, bo tak najlepiej widać, co się dzieje „pod maską”. Przeglądarka wysyła żądanie w stylu GET /on HTTP/1.1. Program czyta pierwszą linię, wyciąga z niej ścieżkę i reaguje:
przeglądarka Pico 2 W
─────────── ────────
GET / ───────────────► strona z przyciskami
GET /on ───────────────► led.on(), odpowiedź 303 → /
GET /off ───────────────► led.off(), odpowiedź 303 → /
GET /cokolwiek ─────────────► 404
Przekierowanie 303 See Other po kliknięciu sprawia, że w pasku adresu zostaje /, więc odświeżenie strony nie przełącza diody ponownie.
Cały plik main.py (funkcję polacz_wifi wklej z poprzedniego kroku, w miejscu komentarza):
# main.py
import socket
import time
import network
from machine import Pin
import secrets
led = Pin("LED", Pin.OUT)
stan_led = False
# ... tutaj funkcja polacz_wifi() z poprzedniego kroku ...
STRONA = """<!DOCTYPE html>
<html lang="pl"><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>Dioda na Pico 2 W</h1>
<p>Stan diody: <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() # np. b"GET /on HTTP/1.1\r\n"
while True: # pomijamy pozostałe nagłówki
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__", "włączona" if stan_led else "wyłączona")
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"Nie ma takiej strony\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("Otwórz w przeglądarce: http://%s/" % ip)
while True:
klient, zdalny = s.accept()
try:
klient.settimeout(5)
obsluz(klient)
except OSError as e:
print("Błąd połączenia:", e)
finally:
klient.close()
ip = polacz_wifi(secrets.SSID, secrets.PASSWORD)
serwer(ip)
Wgrywanie i uruchomienie:
mpremote fs cp secrets.py :secrets.py # dwukropek oznacza płytkę
mpremote fs cp main.py :main.py
mpremote reset
mpremote repl # tu zobaczysz adres IP, wyjście z REPL: Ctrl-]
MicroPython uruchamia main.py automatycznie po starcie, więc płytka podłączona do zasilacza USB sama połączy się z siecią i wystawi stronę. Otwórz w tej samej sieci adres z konsoli, np. http://192.168.1.50/.
Program przerwiesz, naciskając Ctrl‑C w REPL.
Ograniczenia tej wersji
Ten serwer jest synchroniczny: obsługuje jedno połączenie naraz i w czasie oczekiwania na klienta nie robi nic innego. Dokument Raspberry Pi sam zaznacza, że w solidniejszych zastosowaniach lepiej obsługiwać żądania asynchronicznie. Do nauki w zupełności wystarczy.
Krok 5: to samo z Microdot
Microdot to mały framework webowy w stylu Flaska dla CPythona i MicroPythona. Jest aktywnie rozwijany i obsługuje żądania asynchronicznie (asyncio).
Dokumentacja Microdot zaleca na MicroPythonie ręczne skopiowanie pliku microdot.py z repozytorium (katalog src/microdot/). Do podstawowego serwera wystarczy ten jeden plik:
mpremote fs cp microdot.py :microdot.py
# main.py - wersja z Microdot
from machine import Pin
from microdot import Microdot, Response, redirect
import secrets
led = Pin("LED", Pin.OUT)
stan_led = False
# ... tutaj importy time i network, funkcja polacz_wifi() i stała STRONA
# z poprzednich kroków ...
app = Microdot()
Response.default_content_type = "text/html; charset=utf-8"
@app.route("/")
async def index(request):
return STRONA.replace("__STAN__", "włączona" if stan_led else "wyłączona")
@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) # domyślnie Microdot słucha na porcie 5000
Microdot nie konfiguruje sieci, więc z Wi‑Fi łączę się sam. Domyślny typ odpowiedzi to text/plain, stąd ustawienie default_content_type.
Bezpieczeństwo: tylko sieć domowa
Ten serwer nie ma uwierzytelniania ani szyfrowania. Każdy w tej samej sieci, kto zna adres IP, może przełączać diodę. Przy diodzie to nic groźnego, ale zanim podłączysz przekaźnik, pamiętaj o kilku zasadach:
Używaj tylko w sieci lokalnej. Nie przekierowuj portu 80 na routerze i nie wystawiaj płytki do internetu.
HTTP to czysty tekst. Wszystko, co przesyłasz, da się podsłuchać w sieci. Nie przesyłaj w ten sposób haseł.
Żądania GET zmieniają stan. Wygodne w przykładzie, ale w poważniejszym projekcie przełączanie rób metodą POST.
Chroń
secrets.py. Hasło leży na płytce jawnym tekstem i odczyta je każdy, kto podłączy Pico do USB.
Gdy coś nie działa
Brak dysku RP2350: sprawdź kabel (czy przesyła dane) i czy przycisk BOOTSEL był wciśnięty przed podłączeniem.
Permission deniedna/dev/ttyACM0: brak grupydialoutalbo nie było ponownego logowania. Sprawdź poleceniemgroups.Status
-2: płytka nie widzi sieci. Sprawdź, czy sieć nadaje w 2,4 GHz, i czy SSID jest wpisany dokładnie (wielkość liter ma znaczenie).Status
-3: złe hasło.Błąd „adres zajęty” (
EADDRINUSE) po ponownym uruchomieniu skryptu z REPL: najprościej zróbmpremote reset.Brak Wi‑Fi albo dioda nie reaguje: sprawdź, czy wgrałeś firmware RPI_PICO2_W, a nie RPI_PICO2. Plik dla Pico 2 bez W nie jest przeznaczony do obsługi układu radiowego, a na Pico 2 W dioda wisi właśnie na radiu.
Co dalej
Ten szkielet (Wi‑Fi, serwer i sterowanie wyjściem) łatwo rozbudować o odczyt czujnika albo endpoint z JSON-em. Jak zaprojektować panel, który uczciwie pokazuje stan urządzenia, opisuję we wpisie o interfejsie do sterowania urządzeniem, a Pico 2 W z ESP32 porównuję w osobnym wpisie. Jeśli potrzebujesz urządzenia zrobionego na zamówienie, zajrzyj na stronę elektronika. Pytania i własne wersje projektu można wrzucać na forum wirelab.
Źródła
Raspberry Pi – MicroPython na Pico: https://www.raspberrypi.com/documentation/microcontrollers/micropython.html
Raspberry Pi – Connecting to the internet with Raspberry Pi Pico W-series: https://datasheets.raspberrypi.com/picow/connecting-to-the-internet-with-pico-w.pdf
Raspberry Pi – datasheet Pico 2 W: https://datasheets.raspberrypi.com/picow/pico-2-w-datasheet.pdf
MicroPython – firmware dla RPI_PICO2_W: https://micropython.org/download/RPI_PICO2_W/
MicroPython – klasa
network.WLAN: https://docs.micropython.org/en/latest/library/network.WLAN.htmlMicroPython – moduł
network: https://docs.micropython.org/en/latest/library/network.htmlMicroPython – quick reference dla rp2: https://docs.micropython.org/en/latest/rp2/quickref.html
MicroPython – mpremote: https://docs.micropython.org/en/latest/reference/mpremote.html
MicroPython – pakiety i
mip: https://docs.micropython.org/en/latest/reference/packages.htmlFedora – pakiet thonny: https://packages.fedoraproject.org/pkgs/thonny/thonny/
Fedora – grupy systemowe (
dialout): https://src.fedoraproject.org/rpms/setup/raw/rawhide/f/uidgidMicrodot – instalacja: https://microdot.readthedocs.io/en/latest/intro.html
Microdot – odpowiedzi: https://microdot.readthedocs.io/en/latest/users-guide/responses.html
Microdot – PyPI: https://pypi.org/project/microdot/
Microdot – repozytorium (lustro GitHub): https://github.com/miguelgrinberg/microdot
