Skip to content

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 brightness via 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_hz control 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_brightness from 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-right devices mirror brightness / palette / on-off automatically
  • kitchen-* devices form a separate group, don't mirror room1-*
  • 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.