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>
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.
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 and an ARM GCC toolchain.
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:
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)
- Join Wi‑Fi ArtNetDMX / password artnetdmx
- Open http://192.168.4.1/ (or http://artnetdmx.local/ once mDNS is up)
- WiFi page: choose mode, credentials, Art‑Net universe numbers, mDNS hostname
- 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
- Put the Pico in Station mode and note its IP (web UI, serial, or mDNS).
- Inputs/Outputs → Art-Net plugin.
- You will typically see two nodes: QLC+ itself and ArtNetDMX. Select ArtNetDMX (not the QLC+ loopback node).
- Set output to unicast → Pico STA IP (e.g.
10.1.1.190), not broadcast and not192.168.4.1unless you are on SoftAP. - Enable the Art-Net output line (icon lit).
- Map universes: 0 → DMX A, 1 → DMX B (or match the WiFi page).
- Patch fixtures to those universes.
Confirm on http://<pico-ip>/ (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 (BSD‑3‑Clause). DHCP server is from MicroPython / pico‑examples (MIT).