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>
This commit is contained in:
125
firmware/panel/README.md
Normal file
125
firmware/panel/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user