-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Add dali_gear usermod: DALI-2 DT8 (colour) gear emulation #5846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
057d7df
e07b39a
37fdce4
1da5eb3
80c4bf8
91a6815
cfd6cec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "name": "dali_gear", | ||
| "build": { "libArchive": false }, | ||
| "dependencies": { | ||
| "qqqDALI": "https://github.com/netmindz/DALI-Lighting-Interface.git#b260a6e6a3509ce9fcc1e8b384573628ae720c93" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| # DALI Gear Usermod | ||
|
|
||
| Makes WLED act as a **DALI control gear** (IEC 62386) — i.e. a light that responds to commands from an external DALI master (wall dimmer, BMS, building automation system, etc.). | ||
|
|
||
| DALI (Digital Addressable Lighting Interface) is a standardised two-wire bus protocol for lighting control. This usermod puts WLED on the bus as a gear device: the DALI master sends brightness/on/off/colour commands, and WLED adjusts its LEDs accordingly. | ||
|
|
||
| > **ESP32 only.** The hardware timer API used for Manchester decoding is not available on ESP8266. | ||
| ## Hardware | ||
|
|
||
| You need a DALI bus interface circuit to convert between the DALI bus voltage (9.5–22.5 V) and the ESP32's 3.3 V GPIO levels. | ||
|
|
||
| ### Minimal DIY circuit (from [qqqlab/DALI-Lighting-Interface](https://github.com/qqqlab/DALI-Lighting-Interface)) | ||
|
|
||
| ```text | ||
| 3.3V ESP32 5.6V ___ | ||
| Zener +----|___|---- 12V Power Supply | ||
| ___ Diode | 220 Ω | ||
| RX ---+-----|___|---|>|----------+------------- DALI+ | ||
| | 10K | | ||
| +-+ | | ||
| | | 100K ___ |/ PNP DALI BUS | ||
| +-+ TX ---|___|----| Transistor | ||
| | 1K |\ | ||
| | V | ||
| GND --+---------------------------+------------- DALI- | ||
| ``` | ||
|
|
||
| > **⚠️ Warning:** This is a conceptual schematic for experimentation — it is **not isolated** and exposes the ESP32 GPIO to DALI bus voltages through a resistor divider only. Do not use it in a production installation. The PNP transistor produces a **single inversion**: GPIO HIGH drives the bus LOW (asserted). Enable **TX Inverted** in the usermod settings when using this circuit. | ||
| Commercial DALI interface modules with proper isolation (e.g. Waveshare Pico-DALI2, Mikroe DALI Click) are strongly recommended for any real installation. | ||
|
|
||
| ### Pin assignment | ||
|
|
||
| | Signal | Direction | Description | | ||
| |---|---|---| | ||
| | RX | Input | Reads DALI bus state (high = bus idle, low = bus asserted) | | ||
| | TX | Output | Drives DALI bus — needed for backward frame responses to QUERY commands | | ||
|
|
||
| Default pins are **RX=14, TX=17** (Waveshare Pico-DALI2). | ||
|
|
||
| Configure both pins in the WLED usermod settings page. | ||
|
|
||
| ## Configuration | ||
|
|
||
| | Setting | Default | Description | | ||
| |---|---|---| | ||
| | Enabled | false | Enable/disable the usermod | | ||
| | pin_rx | 14 | GPIO for DALI bus RX | | ||
| | pin_tx | 17 | GPIO for DALI bus TX | | ||
| | tx_inverted | false | Invert TX polarity. Enable for single-stage inverting circuits (e.g. DIY PNP). Leave off for Waveshare Pico-DALI2 and other NPN+opto-isolated boards. | | ||
| | daliAddr | -1 | Short address (0–63) to respond to, or -1 to respond to broadcast only | | ||
|
|
||
| ## DALI commands handled | ||
|
|
||
| ### Direct Arc Power Control (DAPC) | ||
|
|
||
| When the master sends a DAPC frame, the arc level (0–254) is mapped linearly to WLED brightness (0–255). WLED's existing gamma correction handles perceptual uniformity at the LED output. | ||
|
|
||
| | DALI arc level | WLED behaviour | | ||
| |---|---| | ||
| | 0 | Turn off | | ||
| | 1–254 | Set brightness proportionally, turn on | | ||
| | 255 (mask) | Ignored (no change) | | ||
|
|
||
| ### Indirect commands | ||
|
|
||
| | Command | Number | WLED action | | ||
| |---|---|---| | ||
| | OFF | 0 | Turn off | | ||
| | UP | 1 | Increase brightness by 10 | | ||
| | DOWN | 2 | Decrease brightness by 10 | | ||
| | STEP UP | 3 | Increase brightness by 1 | | ||
| | STEP DOWN | 4 | Decrease brightness by 1 | | ||
| | RECALL MAX LEVEL | 5 | Set brightness to 255, turn on | | ||
| | RECALL MIN LEVEL | 6 | Set brightness to 1, turn on | | ||
| | STEP DOWN AND OFF | 7 | Decrease by 1; turn off if at minimum | | ||
| | ON AND STEP UP | 8 | Turn on if off, then increase by 10 | | ||
| | GO TO LAST ACTIVE LEVEL | 10 | Restore last brightness before turn-off | | ||
|
|
||
| ### Query commands (backward frame responses) | ||
|
|
||
| These allow a DALI master to detect gear presence and read basic status. Responses are sent as DALI backward frames 4 ms after the query, within the IEC 62386-102 required window of 7Te–22Te (≈2.9–9.2 ms). | ||
|
|
||
| | Command | Byte | Response | | ||
| |---|---|---| | ||
| | QUERY STATUS | 0x90 | Status byte: bit 2 = lamp on, bit 6 = no short address | | ||
| | QUERY CONTROL GEAR PRESENT | 0x91 | `0xFF` (Yes, I am here) | | ||
| | QUERY DEVICE TYPE | 0x18 | `0x08` (device type 8 = colour control) | | ||
| | QUERY ACTUAL LEVEL | 0xA0 | Current arc level (0–254) derived from WLED brightness | | ||
|
|
||
| ### DT8 colour temperature (IEC 62386-209) | ||
|
|
||
| Colour temperature commands from a DALI master are mapped to WLED's CCT value via `strip.setCCT()`. The mired value is converted to Kelvin (`K = 1,000,000 / mireds`). WLED's accepted range is 1900–10091 K; values outside this range are clamped. | ||
|
|
||
| Two CCT application flows are supported: | ||
|
|
||
| **Standard flow (IEC 62386-209 §11.3.4.1):** | ||
|
|
||
| 1. `SET DTR0` — lower byte of colour temperature in mireds | ||
| 2. `SET DTR1` — upper byte of colour temperature in mireds | ||
| 3. `ENABLE DEVICE TYPE 8` — activates DT8 interpretation | ||
| 4. `SET TEMPORARY COLOUR TEMPERATURE` (0xE1) — loads DTR0+DTR1 into temporary register | ||
| 5. `ACTIVATE` (0xE2) — applies the temporary colour temperature | ||
|
|
||
| **Non-standard combined flow (observed in some masters):** | ||
|
|
||
| Some DALI masters skip the `0xE1` + `0xE2` sequence and instead apply the colour temperature implicitly alongside the subsequent DAPC command. The usermod detects this: if DTR0/DTR1 are set and DT8 is active when a DAPC frame arrives, the CCT is applied at the same time as the brightness change. | ||
|
|
||
| **QUERY COLOUR TYPE (0xF7):** | ||
|
|
||
| Some masters query the gear's colour capabilities before sending CCT commands. Per IEC 62386-209 §11.3.4.2, this command is `0xF7`. The usermod responds with `0x02` (bit 1 = Tc colour temperature supported), sent 4 ms after the query frame to meet the DALI spec requirement (7Te–22Te ≈ 2.9–9.2 ms settling window). Command `0xE7` does not generate a backward frame response (it is not a query command per the spec). | ||
|
|
||
| `SET DTR0`, `SET DTR1`, and `ENABLE DEVICE TYPE 8` are sniffed as broadcast-level frames regardless of the configured `daliAddr`. | ||
|
|
||
| ### CCT on RGB-only strips | ||
|
|
||
| `strip.setCCT()` adjusts the colour temperature via WLED's internal CCT pipeline. For an RGB-only strip (no dedicated white or CCT channel), **White Balance Correction** must be enabled in WLED LED settings (Config → LED Preferences → White Balance Correction) for the CCT value to affect the LED output. This makes WLED apply a colour temperature correction on the RGB channels. | ||
|
|
||
| ## Addressing | ||
|
|
||
| DALI addressing works as follows: | ||
|
|
||
| - **Broadcast** (`0xFE`/`0xFF`): always handled regardless of `daliAddr` setting | ||
| - **Short address** (0–63): set `daliAddr` to the address the DALI master has assigned to this device | ||
| - **Group address**: not handled | ||
|
|
||
| Set `daliAddr` to `-1` (default) to respond only to broadcast commands. This is useful for a single-gear installation. | ||
|
|
||
| ## Enabling the usermod | ||
|
|
||
| Add to your `platformio_override.ini`: | ||
|
|
||
| ```ini | ||
| [env:esp32dev] | ||
| custom_usermods = dali_gear | ||
| lib_deps = | ||
| ${env.lib_deps} | ||
| https://github.com/netmindz/DALI-Lighting-Interface.git#fix/esp32-volatile-cast | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: set -eu
printf '%s\n' '--- README references ---'
sed -n '128,158p' usermods/dali_gear/readme.md
printf '%s\n' '--- library manifest ---'
cat -n usermods/dali_gear/library.json
printf '%s\n' '--- all relevant dependency references ---'
rg -n -F 'DALI-Lighting-Interface.git' usermods/dali_gearRepository: wled/WLED Length of output: 1787 Use the immutable dependency commit in both README references.
Proposed fix- https://github.com/netmindz/DALI-Lighting-Interface.git#fix/esp32-volatile-cast
+ https://github.com/netmindz/DALI-Lighting-Interface.git#e39a7da06242010bbb6771532c4ac17b3ec73834🤖 Prompt for AI Agents |
||
| ``` | ||
|
|
||
| ## Limitations | ||
|
|
||
| - No short address commissioning via DALI bus (set the address manually in WLED config) | ||
| - No group address support | ||
| - No DALI scene mapping | ||
|
|
||
| ## Dependencies | ||
|
|
||
| - [qqqlab/DALI-Lighting-Interface](https://github.com/qqqlab/DALI-Lighting-Interface) (GPL-3.0) | ||
| Low-level Manchester-encoded DALI bus driver by qqqlab. | ||
| This usermod uses the fork at `https://github.com/netmindz/DALI-Lighting-Interface.git#fix/esp32-volatile-cast` which includes an ESP32 volatile-cast fix. | ||
Uh oh!
There was an error while loading. Please reload this page.