Release 7 — Network Protocols & Multi-Device¶
Theme: Releases 3–6 made one device drive a lot of LEDs well. Release 7 puts it on the network: integration with Home Assistant, industry-standard lighting protocols (Art-Net, DDP, sACN), and multi-device synchronization with hostname-based grouping. By the end, a projectMM device can sync with WLED peers, receive streams from xLights/Resolume, and participate in a group of siblings.
Release Overview¶
| Gap | Addressed by |
|---|---|
| No MQTT / Home Assistant integration | Sprint 1 |
| System time drifts — no NTP | Sprint 2 |
| Ethernet from R2 is LAN8720 only; no W5500 (SPI) path | Sprint 3 |
| No Art-Net / DDP / sACN output — can't drive remote fixtures | Sprints 4–6 |
| No network input — can't receive from xLights/Resolume/TouchDesigner | Sprint 7 |
| No peer discovery / group sync | Sprints 8–9 (SuperSync v1 + v2) |
| No remote / group control UI | Sprint 10 |
Sprint 1 — MQTT Client + Home Assistant Auto-Discovery¶
Scope¶
Goal: expose every control as an MQTT topic; auto-announce to Home Assistant.
Part A — MqttModule. Controls: broker_url (mqtt:// or mqtts://), username, password (sensitive), client_id, qos (0/1/2). Lifecycle: connect in setup(), reconnect with backoff on disconnect, publish dirty controls in loop().
Part B — Topic schema. One topic per module control:
projectmm/<hostname>/<module-id>/<control-key> for state, .../set for commands.
Part C — Home Assistant auto-discovery. On connect, publish discovery payloads to homeassistant/light/<hostname>/config etc. so HA auto-populates the entities.
Definition of Done:
- Setting
brightnessvia MQTT message changes the actual brightness - HA auto-discovers projectMM as a light entity; state roundtrip works
- TLS connection verified against a local Mosquitto with a self-signed cert
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 2 — NTP + Timezone¶
Scope¶
Goal: wall-clock time synced once Wi-Fi/Ethernet is up; timezone-aware display in the UI.
Part A — TimeModule. Controls: ntp_server (default pool.ntp.org), timezone (IANA string), current_time (read-only display).
Part B — Time-dependent effects. Expose unixTime() + localTime() helpers to the effect framework — used by R9's ScrollingTextEffect (clock preset) and future scheduled effects.
Part C — Drift handling. Re-sync every hour; log on drift > 1 s between syncs.
Definition of Done:
- NTP sync within 30 s of boot; UI shows current local time
- Timezone switch immediately updates displayed time
- No blocking NTP call in hot path — all via async SNTP
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 3 — Ethernet W5500 (SPI)¶
Scope¶
Goal: add the SPI-based Ethernet option for boards without RMII (notably several S3 and C3 variants).
Part A — EthernetW5500Module. Sibling to the existing EthernetModule (LAN8720). Claims SPI pins (SCK, MISO, MOSI, CS, optional IRQ) via IoModule. Lives alongside EthernetModule (can be either-or per board).
Part B — SPI bus arbitration. If APA102 also uses SPI (R6 S9), both modules share the bus with separate CS lines; bus-lock primitive ensures no corruption.
Part C — Board presets updated. SPI-Ethernet boards get their preset populated with the W5500 pins by default.
Definition of Done:
- W5500 on an ESP32-S3 board gets an IP via DHCP
- SPI-shared test with APA102 strip: both work without disrupting each other
- Pin-claim test: LAN8720 + W5500 can't both be active simultaneously (UI prevents it)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 4 — Art-Net Output¶
Scope¶
Goal: send pixel data as Art-Net universes over UDP port 6454.
Part A — ArtNetOutputModule. Controls: target_ip, start_universe, channels_per_universe (default 510 for RGB-multiple), refresh_rate_hz (default 44, cap 130).
Part B — Mapping. Each VirtualLayer pixel → 3 (or 4) bytes → packed into sequential universes. Broadcast option: target_ip = 255.255.255.255.
Part C — Frame pacing. Art-Net standard rate is 44 Hz; capped to prevent overload. If LED refresh > Art-Net rate, Art-Net sends every Nth frame.
Definition of Done:
- Driving 2 universes to another projectMM device (acting as Art-Net sink — Sprint 7) produces matching output
- 130 FPS achieved on LAN with
refresh_rate_hz=130 - Wireshark capture shows correct Art-Net packet format
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 5 — DDP Output¶
Scope¶
Goal: send pixels as DDP over UDP port 4048 — WLED's native protocol.
Part A — DdpOutputModule. Controls: target_ip (unicast only, no broadcast per protocol), id (destination identifier, default 1 = default output).
Part B — RGB vs RGBW awareness. DDP carries format info — ChannelsPerLight config propagates to the packet header.
Definition of Done:
- Driving a WLED device via DDP produces matching pixels in its preview
- Packet format verified with Wireshark against WLED's DDP spec
- Frame rate capped to 60 Hz default (configurable);
refresh_rate_hzcontrol matches Art-Net module
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 6 — E1.31 (sACN) Output¶
Scope¶
Goal: send pixels as E1.31 / sACN over UDP port 5568 — xLights/Resolume/QLC+ default.
Part A — E131OutputModule. Unicast only (per E1.31 recommendation); multicast deferred. Controls: target_ip, start_universe, priority (0–200).
Part B — CID + source name. Device generates a stable CID on first boot (stored in KvStore) and reports hostname as source name.
Definition of Done:
- Driving a QLC+ E1.31 receiver results in matching pixel output
- CID persists across reboots; source name appears correctly in receiver UI
- Priority channel works: two projectMM devices targeting same universe, higher priority wins
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 7 — Network Input: Art-Net / DDP / E1.31 Receive¶
Scope¶
Goal: projectMM as a receiver — xLights, Resolume, another projectMM can drive pixels to it.
Part A — NetworkInputModule. Listens on all three UDP ports simultaneously; auto-detects protocol per packet. Writes received pixels into a VirtualLayer as if they were effect output.
Part B — Universe binding. Map (protocol, universe/id) → which VirtualLayer the pixels go to. UI for the mapping table.
Part C — Pass-through vs take-over semantics. Two modes per VirtualLayer:
- takeover — network pixels replace effect output
- additive — network pixels add on top of effect output
Definition of Done:
- Receive Art-Net from xLights and display on a 16×16 panel
- Receive DDP from another projectMM (loop test with Sprint 5)
- 1000 network-input packets per second handled without drop (measured on ESP32-S3)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 8 — SuperSync v1: Discovery + Control¶
Scope¶
Goal: devices find each other and send basic control commands (on/off, brightness, palette) peer-to-peer.
Part A — mDNS advertisement. Device publishes _projectmm._udp.local. with port, hostname, and capability string.
Part B — Discovery broadcast. UDP broadcast on port 65506 every 10 s + on state change. Separate from WLED's port to prevent feedback loops.
Part C — Control UDP. Port 65507. Minimum command set: set_brightness, set_on, set_palette, set_preset.
Part D — Peer table. DevicesModule maintains list of visible peers (hostname, IP, last-seen, kind=projectMM/WLED). Auto-expires after 30 s silence.
Definition of Done:
- Two projectMM devices on the same subnet see each other within 10 s
- Sending
set_brightnessfrom device A updates device B - WLED devices also appear in the peer table (WLED discovery is compatible)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 9 — SuperSync v2: Hostname-Prefix Grouping¶
Scope¶
Goal: devices with a shared hostname prefix (e.g. room1-left, room1-right) auto-group and mirror state.
Part A — Group detection. Hostname split on hyphens; the first segment is the group name. Devices in same group auto-register with each other.
Part B — State mirroring. Any control change broadcasts to the group. A simple last-writer-wins resolution with a monotonic-clock timestamp.
Part C — Group UI indicator. DevicesModule shows group membership; badges visible in the UI header.
Definition of Done:
room1-left+room1-rightdevices mirror brightness / palette / on-off automaticallykitchen-*devices form a separate group, don't mirrorroom1-*- State convergence within 2 s under flaky wifi (verified by packet-drop injection)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 10 — Devices Module UI: Remote + Group Control¶
Scope¶
Goal: one UI view shows all discovered peers and lets the operator control them collectively or individually.
Part A — DevicesModule UI panel. Table: hostname, IP, kind (projectMM/WLED), group, last-seen, brightness slider, on-off, "open" link.
Part B — Group row. Collapsible group header with combined controls (set brightness for whole group).
Part C — "Follow this device" mode. Pick one peer; all controls on the current UI mirror into that peer instead of the local device. One-click reconfig across the fleet.
Definition of Done:
- Up to 10 peers display in the UI without layout breakage
- Group slider applies to all group members simultaneously
- Follow mode verified against 2 other projectMM devices + 1 WLED device
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Release 7 Backlog¶
- Art-Net / E1.31 multicast output. Slightly tricky on ESP-IDF; defer until demand is clear.
- Synchronized clocks across SuperSync group. PTP-lite or per-group leader election. Deferred to R11.
- Distributed effects across grouped devices. Effect runs on the leader, drives all peers. Deferred to R11.
- Credential encryption at rest. MQTT password + Wi-Fi password currently stored plain in LittleFS; encrypt once key-management story is decided.
- MQTT discovery for non-HA systems. Homie or custom. Pick up on demand.
- QoS 2 MQTT. Actually validate QoS 2 paths — QoS 0/1 are tested, QoS 2 hasn't been end-to-end.