TL;DR: Direct TRIAC phase-cut from Raspberry Pi GPIO is not feasible — Linux OS jitter is 1–10 ms while TRIAC timing requires < 100 µs. Use DimmerLink as a hardware controller: RPi writes a brightness value (0–100) to I2C address 0x50, register 0x10. One line in Python:
bus.write_byte_data(0x50, 0x10, level).
Why Raspberry Pi Cannot Drive a TRIAC Directly
Phase-cut TRIAC dimming fires the gate at a precise delay after every AC zero crossing (every 10 ms at 50 Hz). The allowable timing error is under 100 µs to avoid visible flicker.
Raspberry Pi runs Linux — a general-purpose OS with a preemptive scheduler, swap, network drivers, and GPU activity all competing for CPU time. GPIO interrupt latency under this OS is:
| Condition | GPIO interrupt latency |
|---|---|
| Idle system | 0.5–2 ms |
| Active network / USB | 2–10 ms |
| Disk I/O | up to 50 ms |
A 2 ms jitter at 10% brightness (9 ms firing window) equals 22%
brightness variation — clearly visible flicker. Even with pigpio
daemon (DMA-based GPIO, best available option) jitter is typically
200–500 µs — still above the required threshold under any real load.
This is not a limitation specific to Raspberry Pi. Any Linux SBC (Orange Pi, Rock Pi, Jetson Nano, Banana Pi) has the same constraint. Real-time TRIAC control on Linux requires either an RT kernel patch (impractical for hobbyist use) or offloading timing to a dedicated microcontroller.
DimmerLink is that dedicated controller. It handles zero-cross detection and TRIAC firing on a Cortex-M0+ (STM32-family), achieving < 50 µs jitter regardless of host CPU load. Raspberry Pi only tells it what brightness to use — no timing requirements whatsoever.
Hardware Required
- Raspberry Pi 3, 4, 5, or Zero 2W (any model with I2C)
- DimmerLink module
- rbdimmer AC dimmer module (any current rating)
- 2 × 4.7 kΩ resistors (I2C pull-ups — often not needed on RPi 3/4)
Step 1 — Switch DimmerLink to I2C Mode
DimmerLink ships in UART mode by default. Switch it to I2C mode before connecting to Raspberry Pi.
Connect DimmerLink to a USB-UART adapter (TX → RX, RX → TX, GND, VCC 3.3V). On any computer open the port at 115200 baud, 8N1, and send:
Raw bytes: 02 5BExpected response: 00 (OK). The device now starts in I2C mode after
each power-up. (Mode is saved to EEPROM.)
Step 2 — Wiring
Raspberry Pi → DimmerLink (I2C)
| Raspberry Pi GPIO header | DimmerLink | Function |
|---|---|---|
| Pin 1 (3.3V) | VCC | Power |
| Pin 6 (GND) | GND | Ground |
| Pin 3 (GPIO2 / SDA) | SDA | I2C data |
| Pin 5 (GPIO3 / SCL) | SCL | I2C clock |
Pull-up resistors: Raspberry Pi 3/4/5 has built-in 1.8 kΩ pull-ups on GPIO2 and GPIO3. These are usually sufficient for cable lengths under 30 cm. For longer cables or unreliable communication, add external 4.7 kΩ resistors from SDA and SCL to 3.3V.
DimmerLink → AC Dimmer Module
| DimmerLink | Dimmer module |
|---|---|
| VCC | VCC |
| GND | GND |
| Z-C | Z-C |
| Dim | DIM |
Connect the dimmer module to mains AC and the load per the Hardware Connection Guide.
Step 3 — Enable I2C on Raspberry Pi
sudo raspi-config
# Interface Options → I2C → Enable → OK → Finish
sudo rebootAfter reboot, install i2c-tools and verify the connection:
sudo apt install i2c-tools
i2cdetect -y 1Expected output — address 50 visible in the table:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
...
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --If the device does not appear, see DimmerLink Not Detected.
Step 4 — Python Control (smbus2)
Install the library:
pip install smbus2Minimal Example
from smbus2 import SMBus
DIMMER_ADDR = 0x50
REG_LEVEL = 0x10 # DIM0_LEVEL — brightness 0–100%
bus = SMBus(1) # Raspberry Pi I2C bus 1
bus.write_byte_data(DIMMER_ADDR, REG_LEVEL, 50) # set 50%
bus.close()Full Class with Error Handling
from smbus2 import SMBus
import time
class DimmerLink:
ADDR = 0x50
REG_STATUS = 0x00
REG_LEVEL = 0x10 # brightness 0-100%
REG_CURVE = 0x11 # 0=LINEAR, 1=RMS, 2=LOG
REG_FREQ = 0x20 # mains frequency (50 or 60 Hz)
def __init__(self, bus_number=1):
self.bus = SMBus(bus_number)
def is_ready(self):
status = self.bus.read_byte_data(self.ADDR, self.REG_STATUS)
return bool(status & 0x01)
def set_level(self, level: int):
"""Set brightness 0–100%."""
level = max(0, min(100, level))
self.bus.write_byte_data(self.ADDR, self.REG_LEVEL, level)
def get_level(self) -> int:
"""Read current brightness."""
return self.bus.read_byte_data(self.ADDR, self.REG_LEVEL)
def set_curve(self, curve: int):
"""Set dimming curve: 0=LINEAR, 1=RMS, 2=LOG."""
if curve not in (0, 1, 2):
raise ValueError("curve must be 0, 1, or 2")
self.bus.write_byte_data(self.ADDR, self.REG_CURVE, curve)
def get_frequency(self) -> int:
"""Read mains frequency (50 or 60 Hz)."""
return self.bus.read_byte_data(self.ADDR, self.REG_FREQ)
def close(self):
self.bus.close()
if __name__ == "__main__":
dimmer = DimmerLink()
if dimmer.is_ready():
print(f"Mains frequency: {dimmer.get_frequency()} Hz")
# Smooth fade up
for level in range(0, 101, 5):
dimmer.set_level(level)
print(f"Brightness: {level}%")
time.sleep(0.1)
# Set RMS curve for incandescent lamps
dimmer.set_curve(1) # 1 = RMS
print("Curve: RMS (incandescent)")
else:
print("DimmerLink not ready — check wiring and I2C mode")
dimmer.close()Quick CLI Control (no Python needed)
# Set brightness to 50% (0x32 hex = 50 decimal)
i2cset -y 1 0x50 0x10 50
# Read current brightness
i2cget -y 1 0x50 0x10
# Read mains frequency
i2cget -y 1 0x50 0x20Step 5 — Node-RED Integration
Using i2cset Command Node
In a simple flow, use exec to run i2cset:
[inject: level=50] → [function: build command] → [exec] → [debug]Function node:
var level = msg.payload; // 0-100
msg.payload = "i2cset -y 1 0x50 0x10 " + level;
return msg;Using node-red-contrib-i2c
Install the node:
cd ~/.node-red
npm install node-red-contrib-i2cConfigure an i2c out node:
- Address:
0x50 - Command:
0x10(register) - Payload: the brightness value (0–100) from
msg.payload
A slider UI node mapped to 0–100 then directly controls the dimmer.
Step 6 — Home Assistant OS on Raspberry Pi
If Raspberry Pi runs Home Assistant OS (not Home Assistant on Raspberry Pi OS), I2C hardware access from add-ons is restricted by the HA OS container model.
Recommended path:
- Keep the Raspberry Pi as the HA server.
- Add a separate ESP32 with DimmerLink as the hardware controller.
- Use the ESPHome add-on in Home Assistant to flash and manage the ESP32.
- HA controls brightness via the ESPHome integration — no direct I2C from the RPi host.
See: AC Dimmer with Home Assistant and ESPHome
If you are running Home Assistant supervised on Raspberry Pi OS
(not HA OS), I2C access from Python scripts is unrestricted and the
smbus2 approach above works directly.
Step 7 — Run as a systemd Service
To start the dimmer control script automatically:
Create /etc/systemd/system/dimmer.service:
[Unit]
Description=DimmerLink Controller
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/dimmer_control.py
Restart=on-failure
User=pi
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable dimmer.service
sudo systemctl start dimmer.serviceOther Linux SBCs
The same approach works on any Linux SBC with I2C:
| Board | I2C bus | Detect command | Notes |
|---|---|---|---|
| Raspberry Pi 3/4/5 | /dev/i2c-1 | i2cdetect -y 1 |
Standard |
| Raspberry Pi Zero 2W | /dev/i2c-1 | i2cdetect -y 1 |
Standard |
| Orange Pi Zero 2 | /dev/i2c-3 | i2cdetect -y 3 |
Model-dependent |
| Rock Pi 4 | /dev/i2c-7 | i2cdetect -y 7 |
Model-dependent |
| Banana Pi M2 | /dev/i2c-1 | i2cdetect -y 1 |
Standard |
| Jetson Nano | /dev/i2c-1 | i2cdetect -y 1 |
— |
| HA OS on RPi | Not accessible | — | Use ESP32 + DimmerLink |
To find available I2C buses on any Linux board:
ls /dev/i2c-*For Orange Pi and Banana Pi, enable I2C via armbian-config:
sudo armbian-config
# System → Hardware → enable i2cIn Python, pass the correct bus number to SMBus():
bus = SMBus(3) # for /dev/i2c-3 on Orange Pi Zero 2Common Pitfalls
| Problem | Cause | Fix |
|---|---|---|
i2cdetect shows nothing at 0x50 |
DimmerLink still in UART mode | Switch with 02 5B via UART |
OSError: [Errno 121] |
No pull-up / wrong wiring | Check SDA/SCL connections |
PermissionError: /dev/i2c-1 |
User not in i2c group | sudo usermod -aG i2c $USER |
i2cdetect hangs (no output) |
UART mode pulls SDA low | Switch DimmerLink to I2C mode first |
| Flicker from RPi OS jitter | Direct TRIAC attempt | Must use DimmerLink — RPi GPIO cannot do phase-cut |
| RPi Zero 2W slow I2C | Low-speed mode | Set smbus2.SMBus(1, force_open=True) or reduce cable length |
Quick Checklist
Related Articles
- DimmerLink I2C not detected → DimmerLink Not Detected: I2C vs UART Mode
- Tasmota integration → Tasmota AC Dimmer via DimmerLink
- Home Assistant guide → AC Dimmer with Home Assistant and ESPHome
- ESPHome YAML → ESPHome YAML for AC Dimmer and DimmerLink
- Safety and wiring → AC Dimmer Safety: Mains Voltage, Isolation, and Wiring
Still have questions?
Ask on forum.rbdimmer.com or open a GitHub Issue.