Files
portal/firmware/panel/README.md
Jimmy d5cab2efdf Add multi-panel Pico UDP firmware and Python LED control.
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>
2026-06-28 22:46:08 +12:00

3.1 KiB
Raw Blame History

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.1014
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: ~3050 fps per panel (vs ~1020 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 04 (make deploy PANEL_ID=N). Each gets 10.1.1.1N (1014), a unique MAC, and UDP filtering. Send frames from the Pi to each panels IP, or use a leading panel_id byte with 255 broadcast filtering.