Tasmota Berry script for DimmerLink — set_dimmer function

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.

Excuse my english, I hope my question is clear.

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:

  1. One I2C init node at startup (triggered by inject on deploy)
  2. 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.

For the full Tasmota + DimmerLink integration guide including console commands and Berry examples, rbdimmer has a good writeup:
https://rbdimmer.com/faq/tasmota-ac-dimmer-control-via-dimmerlink-29

[SOLVED]