Add ArtNetDMX Pico W firmware with dual DMX and docs.

Art-Net to two RS-485 outputs over Wi-Fi (AP or STA with SoftAP fallback), web UI, mDNS, and QLC+/ArtPoll notes for unicast on STA.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-11 00:00:23 +12:00
co-authored by Cursor
commit fea6dffa74
26 changed files with 3013 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# ArtNetDMX — Pico W Art-Net → Dual DMX
Firmware for **Raspberry Pi Pico W** that receives Art-Net over Wi‑Fi and outputs **two DMX512 universes** through RS‑485, using **PIO + DMA** at **40 Hz**. A web UI configures Wi‑Fi (STA / AP) and shows / sets all 512 channels per universe.
## Hardware
| Signal | Pico W | RS‑485 module |
|--------|--------|----------------|
| DMX A data | GP0 | DI (universe A) |
| DMX B data | GP1 | DI (universe B) |
| GND | GND | GND |
| Power | 3V3 or 5V | VCC (many MAX485 boards want 5V) |
TX‑only: tie **DE** and **/RE** high (to VCC) on each module. XLR pinout is typically 1=GND, 2=Data− (B), 3=Data+ (A) — match your module labels.
Pins are defined in [`include/board.h`](include/board.h).
```
Pico W GP0 ──► MAX485 A DI
Pico W GP1 ──► MAX485 B DI
Pico W GND ──► both module GND
VCC ──► both module VCC
DE,/RE ──► VCC (TX enable)
A/B ──► XLR / DMX cable
```
## Build
Requires [Pico SDK](https://github.com/raspberrypi/pico-sdk) and an ARM GCC toolchain.
```bash
export PICO_SDK_PATH=/path/to/pico-sdk
mkdir -p build && cd build
cmake -DPICO_BOARD=pico_w ..
make -j$(nproc)
```
Flash with BOOTSEL + drag-and-drop, or:
```bash
picotool load -f build/artnetdmx.uf2
```
USB serial (115200) prints boot status, Wi‑Fi mode, and IP addresses.
Do not `cp` the UF2 onto a path that is not the mounted `RPI-RP2` volume (a failed copy can leave a plain file instead of flashing).
## First boot (AP)
1. Join Wi‑Fi **ArtNetDMX** / password **artnetdmx**
2. Open **http://192.168.4.1/** (or **http://artnetdmx.local/** once mDNS is up)
3. **WiFi** page: choose mode, credentials, Art‑Net universe numbers, mDNS hostname
4. Save — device reboots
### Wi‑Fi modes
| Mode | Behaviour |
|------|-----------|
| **Access Point** | SoftAP only (default `ArtNetDMX` / `artnetdmx` @ `192.168.4.1`) |
| **Station** | Joins your LAN only. If join fails, SoftAP starts for recovery |
There is no concurrent AP+STA mode (legacy “AP+Station” configs are treated as Station). SoftAP is paused while STA obtains DHCP, then left off if join succeeds.
mDNS hostname defaults to **`artnetdmx`** → **http://artnetdmx.local/** (configurable on the WiFi page).
## Art-Net
- UDP port **6454**
- Replies to **ArtPoll** / sends **ArtPollReply** (QLC+ and similar discovery)
- Maps configured universes to physical ports A (GP0) and B (GP1)
- Defaults: universe **0** → A, universe **1** → B
### SoftAP vs Station (important)
| Mode | Art-Net transport |
|------|-------------------|
| **SoftAP** | Controller joins the Pico’s AP. **Broadcast** ArtDmx usually works. |
| **Station** | Pico and controller share a home/office AP. Many APs **drop Wi‑Fi broadcast**, so ArtDmx broadcast never arrives. Use **unicast** to the Pico’s STA IP. |
Unicast ArtDmx on STA has been verified end-to-end; subnet broadcast often is not delivered by the upstream AP.
### QLC+ setup
1. Put the Pico in **Station** mode and note its IP (web UI, serial, or mDNS).
2. **Inputs/Outputs** → Art-Net plugin.
3. You will typically see **two nodes**: **QLC+ itself** and **ArtNetDMX**. Select **ArtNetDMX** (not the QLC+ loopback node).
4. Set output to **unicast** → Pico STA IP (e.g. `10.1.1.190`), not broadcast and not `192.168.4.1` unless you are on SoftAP.
5. Enable the Art-Net **output** line (icon lit).
6. Map universes: **0** → DMX A, **1** → DMX B (or match the WiFi page).
7. Patch fixtures to those universes.
Confirm on **http://&lt;pico-ip&gt;/** (Channels): Art-Net packet count should rise and channel values should follow faders. If the web UI updates but fixtures do not, check RS‑485 wiring / DE·/RE / cabling.
## Web UI
- **Channels** — tabs for universe A/B; all 512 values; edit live; Clear / All 255
- **WiFi** — AP or Station, credentials, Art‑Net universes, mDNS hostname, live debug line
- Status strip: mode, IP, Art‑Net packet count, age since last packet per port
Manual channel edits drive the same buffers as Art‑Net; new Art‑Net packets overwrite those channels.
Embedded pages live in `src/http_server.c`. The `www/` tree is a reference copy of the HTML structure.
## Project layout
| Path | Role |
|------|------|
| `src/main.c` | Boot, DMX timer, poll loops |
| `src/wifi_mgr.c` | SoftAP / STA, DHCP, mDNS hooks |
| `src/artnet.c` | ArtDmx + ArtPoll/ArtPollReply |
| `src/dmx_out.c` + `.pio` | PIO + DMA DMX TX |
| `src/http_server.c` | Web UI + JSON API |
| `src/config.c` | Flash config (CRC, versioned) |
| `lwipopts.h` | lwIP tuning (UDP PCBs, SoftAP DHCP, broadcast) |
## License notes
DMX PIO timing is adapted from [jostlowe/Pico-DMX](https://github.com/jostlowe/Pico-DMX) (BSD‑3‑Clause). DHCP server is from MicroPython / pico‑examples (MIT).