# 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](https://github.com/ForsakenNGS/Pico_WS2812) (FORMAT_GRB on GP27). ## Build Requires [pico-sdk](https://github.com/raspberrypi/pico-sdk) and the ARM GCC toolchain (`arm-none-eabi-gcc`). ```bash # 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): ```bash 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 ```bash # 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 ```bash pipenv run python examples/panel_udp_send.py --host 192.168.2.111 --animation rainbow ``` ## Flashing Requires [picotool](https://github.com/raspberrypi/picotool) with libusb support: ```bash make deploy ``` Or `./scripts/panel_deploy.sh` (same thing). Or manually: ```bash 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: ```bash 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.