Pico panels get static IPs on 10.1.1.10–14 with per-panel LED counts, Makefile deploy targets, and Python examples for animations, sync tests, and direct Pi SPI control. Co-authored-by: Cursor <cursoragent@cursor.com>
3.1 KiB
Portal panel firmware (Pico SDK)
C firmware for a Pico + W5500 + WS2812 panel adapter. Replaces the slow CircuitPython adapter/code.py path with native UDP receive and PIO WS2812 output.
Hardware
Matches adapter/code.py:
| Signal | GPIO |
|---|---|
| W5500 CS | GP9 |
| SPI1 SCK | GP10 |
| SPI1 MOSI | GP11 |
| SPI1 MISO | GP12 |
| W5500 RST | GP13 |
| Status LED | GP25 |
| WS2812 data | GP27 |
Default: 405 LEDs (45×9 matrix chain), one data pin per panel.
WS2812 output uses ForsakenNGS/Pico_WS2812 (FORMAT_GRB on GP27).
Build
Requires pico-sdk and the ARM GCC toolchain (arm-none-eabi-gcc).
# From repo root
make deploy # build + flash over USB
make build # build only
# Or from this directory
make deploy
Manual cmake (one-time pico-sdk setup):
git clone https://github.com/raspberrypi/pico-sdk.git ~/pico/pico-sdk
cd ~/pico/pico-sdk && git submodule update --init
export PICO_SDK_PATH=~/pico/pico-sdk
mkdir -p build && cd build
cmake ..
make -j$(nproc)
UF2 output: build/panel.uf2 — hold BOOTSEL and copy to the Pico.
Options
# Panel 0 of 5 → 10.1.1.10, unique MAC, UDP panel_id filter
make deploy PANEL_ID=0 # 10.1.1.10
make deploy PANEL_ID=1 # 10.1.1.11
# DHCP instead of static 10.1.1.10–14
make deploy PANEL_ID=0 NO_DHCP=0
# Fewer LEDs for bench test
make deploy NUM_LEDS=2
Static IP and MAC are derived from PANEL_ID:
| Panel | IP | MAC |
|---|---|---|
| 0 | 10.1.1.10 | 02:50:52:54:4C:00 |
| 1 | 10.1.1.11 | 02:50:52:54:4C:01 |
| … | … | … |
| 4 | 10.1.1.14 | 02:50:52:54:4C:04 |
| 255 (bench) | 10.1.1.19 | 02:50:52:54:4C:FF |
Edit board_config.h for gateway, subnet, and pin changes.
UDP protocol (port 50007)
| Payload | Action |
|---|---|
| 1215 bytes | Raw RGB (405 × 3), show immediately |
| 1216 bytes | panel_id (byte 0) + RGB; 255 = accept on any panel |
4 bytes SHOW |
Push buffered pixels to the strip (sync helper) |
Pi sends RGB order; firmware converts to WS2812 GRB.
Target throughput: ~30–50 fps per panel (vs ~10–20 fps on CircuitPython).
Pi test sender
pipenv run python examples/panel_udp_send.py --host 192.168.2.111 --animation rainbow
Flashing
Requires picotool with libusb support:
make deploy
Or ./scripts/panel_deploy.sh (same thing).
Or manually:
picotool load -x -f build/panel.uf2
-f forces a USB reboot into BOOTSEL when the panel is already running.
Legacy UF2 drag-and-drop also works: hold BOOTSEL, plug USB, copy panel.uf2 to RPI-RP2.
Serial debug:
make monitor
# or: picocom /dev/ttyACM0
Ctrl+A then Ctrl+X to exit picocom.
Multi-panel (5 Picos)
Flash each Pico with PANEL_ID 0–4 (make deploy PANEL_ID=N). Each gets 10.1.1.1N (10–14), a unique MAC, and UDP filtering. Send frames from the Pi to each panel’s IP, or use a leading panel_id byte with 255 broadcast filtering.