Got DimmerLink working with Tasmota on an ESP32 over I2C — brightness control works fine when I send commands manually from the Tasmota console.
Problem is I wrote a Berry script to automate the dimmer and the lamp flickers randomly. Not a constant flicker — more like a brief dip every few seconds. Sometimes it’s fine for 30 seconds then does two flickers in a row.
Here’s my Berry script:
import wire
var DL_ADDR = 0x27 # DimmerLink I2C address
var DL_CH1 = 0x01 # Channel 1 register
def set_dimmer(channel, value) # value 0-100
wire.begin() # init I2C
var reg = channel
wire.write(DL_ADDR, reg, value, 1)
end
def fade_up()
for i: 0..100
set_dimmer(DL_CH1, i)
tasmota.delay(20)
end
end
The fade_up() function is where I see it — the lamp ramps up but with random flicker dips along the way. If I just call set_dimmer(DL_CH1, 50) once from the console it sets fine. It’s when I call it repeatedly that the flickering starts.
I2C scan shows DimmerLink at 0x27 no problem. Running Tasmota 14.4 on ESP32-WROOM.
Anyone done Berry + DimmerLink before? The Tasmota way for I2C devices is usually straightforward but something’s off here.
I think I see your problem Tom — you are calling wire.begin() inside your set_dimmer() function. This mean every time you call set_dimmer, the I2C bus is reinitialize completely.
I had the exact same issue in my Node-RED setup with a different I2C node. My flow was reinitializing the I2C connection on every MQTT message and the dimmer was doing strange flicker. The I2C bus need a small time to stabilize after begin() and if you call it 100 times during a fade, you get 100 reinitializations.
Try moving wire.begin() to an init function that run only once at startup:
import wire
var DL_ADDR = 0x27
var DL_CH1 = 0x01
def init()
wire.begin() # Initialize I2C ONCE here
end
def set_dimmer(channel, value)
var reg = channel
wire.write(DL_ADDR, reg, value, 1)
end
Call init() one time when the script load, then use set_dimmer() for all your commands. The I2C bus stay active between calls — no need to reinitialize.
In my Node-RED flow I have the same pattern: one I2C init node at deploy, then only write nodes in the flow. The MQTT message arrive and go directly to the write without any reconnection.
Seb you legend — that was it. Moved wire.begin() out of set_dimmer() and into an init() that runs once at script load. Zero flicker now.
Should have caught that myself. I was thinking of wire.begin() like a ‘select device’ call but it’s actually reinitializing the entire I2C peripheral on the ESP32 every time. No wonder it flickered — each reinitialization probably takes a few hundred microseconds and the DimmerLink loses communication briefly.
Here’s the corrected Berry script with fade support:
import wire
var DL_ADDR = 0x27 # DimmerLink I2C address
var DL_CH1 = 0x01 # Channel 1 register
var DL_CH2 = 0x02 # Channel 2 register
def init()
wire.begin() # Initialize I2C ONCE
end
def set_dimmer(channel, value) # value 0-100
var reg = channel
wire.write(DL_ADDR, reg, value, 1)
end
def fade(channel, target, step_ms)
var current = 0
for i: 0..target
set_dimmer(channel, i)
tasmota.delay(step_ms)
end
end
init() # run once at load
Smooth fade from 0 to 100 with 20ms steps — perfectly clean, no dips at all. The DimmerLink holds rock solid at any brightness during the ramp.
I can also call it from Tasmota console rules now:
Rule1 ON Power1#State=1 DO br set_dimmer(0x01, 80) ENDON
Glad it work for you! I can confirm the same fix solve my Node-RED problem too.
In my case, I had an I2C initialize node inside a function node that was called on every MQTT message. Moving the initialization to a separate inject node that fire only at deploy time fix all the flicker.
For anyone using Node-RED with DimmerLink over I2C — the pattern is the same:
One I2C init node at startup (triggered by inject on deploy)
All subsequent I2C write nodes just send the register + value without reinitializing
The DimmerLink registers are very simple — 0x01 for channel 1, 0x02 for channel 2, and so on up to 0x04 for channel 4. You just write one byte (0-100) to the register and the dimmer respond immediately. No need to close or reopen the bus between writes.
I think this is a common mistake for people coming from Arduino where Wire.begin() is often placed everywhere because it seem harmless. On ESP32 with Tasmota or Node-RED, the I2C driver reinitialize is more expensive.
Marking this solved. Final working Berry script above in my post.
Summary for anyone finding this later:
wire.begin() goes in init() — called ONCE at script load
set_dimmer() only does wire.write() — no I2C reinitialization
DimmerLink I2C address default: 0x27
Registers: 0x01=CH1, 0x02=CH2, 0x03=CH3, 0x04=CH4
Value range: 0-100 (percent power)
The Tasmota Berry I2C docs don’t explicitly warn about this but it makes sense — wire.begin() reinitializes the ESP32 I2C peripheral which takes time and interrupts any active communication on the bus.