Passa al contenuto

Raspberry Pi AC Dimmer Control via DimmerLink

Linux does not provide realtime GPIO control — direct TRIAC phase-cut is impossible without a dedicated controller. DimmerLink solves this: Raspberry Pi sends only a brightness level, DimmerLink handles precision timing.

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:

text
Raw bytes: 02 5B

Expected 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

bash
sudo raspi-config
# Interface Options → I2C → Enable → OK → Finish
sudo reboot

After reboot, install i2c-tools and verify the connection:

bash
sudo apt install i2c-tools
i2cdetect -y 1

Expected output — address 50 visible in the table:

text
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:

bash
pip install smbus2


Minimal Example

python
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

python
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)

bash
# 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 0x20



Step 5 — Node-RED Integration


Using i2cset Command Node

In a simple flow, use exec to run i2cset:

text
[inject: level=50] → [function: build command] → [exec] → [debug]

Function node:

javascript
var level = msg.payload;  // 0-100
msg.payload = "i2cset -y 1 0x50 0x10 " + level;
return msg;


Using node-red-contrib-i2c

Install the node:

bash
cd ~/.node-red
npm install node-red-contrib-i2c

Configure 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:

  1. Keep the Raspberry Pi as the HA server.
  2. Add a separate ESP32 with DimmerLink as the hardware controller.
  3. Use the ESPHome add-on in Home Assistant to flash and manage the ESP32.
  4. 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:

ini
[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.target

Enable and start:

bash
sudo systemctl daemon-reload
sudo systemctl enable dimmer.service
sudo systemctl start dimmer.service



Other 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:

bash
ls /dev/i2c-*

For Orange Pi and Banana Pi, enable I2C via armbian-config:

bash
sudo armbian-config
# System → Hardware → enable i2c

In Python, pass the correct bus number to SMBus():

python
bus = SMBus(3)  # for /dev/i2c-3 on Orange Pi Zero 2



Common 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

  • ☐ DimmerLink switched to I2C mode (`02 5B` via UART)
  • ☐ I2C enabled in `raspi-config` (or `armbian-config`)
  • ☐ `i2cdetect -y 1` shows address `50`
  • ☐ User added to `i2c` group (`sudo usermod -aG i2c $USER`)
  • ☐ `pip install smbus2` completed
  • ☐ `bus.write_byte_data(0x50, 0x10, 50)` changes lamp brightness



  • Related Articles



    Still have questions?

    Ask on forum.rbdimmer.com or open a GitHub Issue.

    Condividi articolo
    Accedi per lasciare un commento