WARNING: Some people try to build this with an optocoupler with zerocrossing coz 'that is better' right? Some are even told in electronics shops it is better to use such an optocoupler. WRONG. This will only work with a random fire optocoupler: NOT igniting at zerocrossing is the principle of this dimmer.

Switching an AC load with an Arduino is rather simpel: either a mechanical relay or a solid state relay with an optically isolated Triac. (if you use an 8051 or PIC16F877A microcontroller, there is stuff for you too here.)

It becomes a bit more tricky if one wants to dim a mains AC lamp with an arduino: just limiting the current through e.g. a transistor is not really possible due to the large power the transistor then will need to dissipate, resulting in much heat and it is also not efficient from an energy use point of view.

One way of doing it is through phase control with a Triac: the Triac then is fully opened, but only during a part of the sinus AC wave. This is called leading edge cutting.
One could let an Arduino just open the Triac for a number of microseconds, but that has the problem that it is unpredictable during what part of the sinus wave the triac opens and therefore the dimming level is unpredictable. One needs a reference point in the sinus wave.
For that a zero crossing detector is necessary. This is a circuit that tells the Arduino (or another micro controller) when the sinus-wave goes through zero and therefore gives a defined point on that sinus wave.
Opening the Triac after a number of microseconds delay starting from the zero crossing therefore gives a predictable level of dimming.

Another way of doing this is by Pulse Skip Modulation. With PSM, one or more full cycles (sinuswaves) are transferred to the load and then one or more cycles are not. Though effective, it is not a good way to dim lights as there is a chance for flickering. Though it might be tempting, in PSM one should always allow a full sinuswave to be passed to the load, not a half sinus as in that case the load will be fed factually from DC which is not a good thing for most AC loads. The difference between leading edge cutting and PSM is mainly in the software: in both cases one will need a circuit that detects the zero crossing and that can control a triac.

A circuit that can do this is easy to build: The zero crossing is directly derived from the rectified mains AC lines – via an optocoupler of course- and gives a signal every time the wave goes through zero. Because the sine wave first goes through double phased rectification, the zero-crossing signal is given regardless whether the sinus wave goes up through zero or down through zero. This signal then can be used to trigger an interrupt in the Arduino.

It goes without saying that there needs to be a galvanic separation between the Arduino side of things and anything connected to the mains. For those who do not understand 'galvanic separation' it means 'no metal connections' thus ---> opto-couplers. BUT, if you do not understand 'galvanic separation', maybe you should not build this.

The circuit pictured here does just that. The mains 220Volt voltage is led through two 30k resistors to a bridge rectifier that gives a double phased rectified signal to a 4N25 opto-coupler. The LED in this opto-coupler thus goes low with a frequency of 100Hz and the signal on the collector is going high with a frequency of 100Hz, in line with the sinusoid wave on the mains net. The signal of the 4N25 is fed to an interrupt pin in the Arduino (or other microprocessor). The interrupt routine feeds a signal of a specific length to one of the I/O pins. The I/O pin signal goes back to our circuit and opens the LED and a MOC3021, that triggers the Opto-Thyristor briefly. The LED in series with the MOC3021 indicates if there is any current going through the MOC3021. Mind you though that in dimming operation that light will not be very visible because it is very short lasting. Should you chose to use the triac switch for continuous use, the LED will light up clearly.

Mind you that only regular incandescent lamps are truly suitable for dimming. It will work with a halogen lamp as well, but it will shorten the life span of the halogen lamp. It will not work with any cfl lamps, unless they are specifically stated to be suited for a dimmer. The same goes for LED lamps

If you are interested in an AC dimmer such as this but you do not want to try building it yourself, there is a somewhat similar dimmer available at, however, that is a 110 Volt 60Hz version (but adaptable for 220 50Hz), that has been out of stock for a while. You will also find a schedule here.

NOTE! It is possible that depending on the LED that is used, the steering signal just does not cut it and you may end up with a lamp that just flickers rather than being smoothly regulated. Replacing the LED with a wire bridge will cure that. The LED is not really necessary. increase the 220 ohm resistor to 470 then

STOP: This circuit is attached to a 110-220 Voltage. Do not build this if you are not confident about what you are doing. Unplug it before coming even close to the PCB. The cooling plate of the Triac is attached to the mains. Do not touch it while in operation. Put it in a proper enclosure/container.

WAIT: Let me just add a stronger warning here: This circuit is safe if it is built and implemented only by people who know what they are doing. If you have no clue or if you are doubting about what you do, chances are you are going to be DEAD!

4N25 €0.25 or H11AA1 or IL250, IL251, IL252, LTV814 (see text in the next step)
Resistor 10k €0.10
bridge rectifier 400 Volt €0.30
2x 30 k resistor 1/2 Watt (resistors will probably dissipate 400mW max each €0.30
1 connector €0.20
5.1 Volt zenerdiode (optional)

Lamp driver
LED (Note: you can replace the LED with a wire bridge as the LED may sometimes cause the lamp to flicker rather than to regulate smoothly)
MOC3021 If you chose another type, make sure it has NO zero-crossing detection)
Resistor 220 Ohm €0.10 (I actually used a 330 Ohm and that worked fine)
Resistor 470 Ohm-1k (I ended up using a 560 Ohm and that worked well)
TRIAC TIC206 €1.20 or BR136 €0.50
1 connector €0.20

Piece of PCB 6x3cm
electric wiring

That is about €3 in parts

Remove these adsRemove these ads by Signing Up
1-40 of 387Next »
TomazS3 days ago


first let me thank you for sharing. Superb project! I managed to assemble the circuit, however I cannot find a way to make dimming time dependant. Let us say, I want to have 100% output for 20 minutes, then 80% for 30 minutes. Somehow it does not work with if statments:

if (millis()<60000){



With for loop it works fine, so it must be the code...


diy_bloke (author)  TomazS3 days ago

I am not sure what your code exactly looks like, but you cannot just check for a number of millis()asthey just keep on running. Try the following:
put the following lines in your initialisation (so at the beginning of yr code, not in the setup):

long previousMillis = 0;
long interval = 60000;

Then in your loop:

unsigned long currentMillis = millis();

if(currentMillis - previousMillis > interval) {



another possibility is to have an interrupt generated that sets a minute counter. Then just keep track of the counter.
How you can do that I describe in an article here:

check for the "Arduino timer CTC interrupt example"

Thank you for the answer. I don not really understand. The millis() in if statment works fine with my PID algorithm, when I change Setpoint accordingly. I will try your code and see.

diy_bloke (author)  TomazSyesterday

ThomasZ as i do not know your code it is hard to say.
I recently had some unexpected effects in using Millis() after i changed the code in an aspect that shouldnt really affect the millis() but it did.
Anyway, I hope it works :-)

diy_bloke (author)  TomazS3 days ago

just let me add (just to make sure) that in millis, 60000= 60 sec = 1 min so in yr code you have to keep track of that

if i want to use this for control 3 lighting load which can dim 3 level and 3 load can dim different level in the same time and 3 load can work in the same time.

example have level 1 2 3 and load A is level 1 load B is 3 and load C is 2 .

please recommend me, what i should to do????

it is possible to use this for 3 loads that you regulate independently.
Say you have an RGB setup, what you need to do is maintain 3 level variables (that are basically time delays). You then need to count from the zero crossing till the shortest delay, fire the necessary TRIAC, count further till the next delay, fire that Triac etc.
Mind you though that the processor will spend a lot of time just waiting.
When you dim one lamp, it is possible to use a delay or use a timer, as the examples show, but if you have 3 lamps, you just do not have timers enough, so you have to use delays.
Just to make sure: you would ofcourse only need ONE zerocrossing circuit, but three Triacs that you can fire

thank you so much

if you have some example code please can you give it for me

I know I started some but I am not sure if i still can find it. Will look, but dont wait for that

PrinsNathan2 months ago
Hi, diybloke! When I tried your code with this circuit today it didn't work, when I ran a simple analog write to the input pin it did, but it wasn't in sync of course so it faded. I think there might me a problem with my zero cross signal. I don't have a scope so I can't check that, should I replace my 4N27 or try and borrow a scope?
diy_bloke (author)  PrinsNathan2 months ago

sorry to hear you had a problem. as it seems your output works well, and provided you have made no mistakes in the program, it mist be the input signal.
before u buy a new optocoupler, I advise the following: measure the voltage over the input of the optocoupler. Does it get any voltage at all?
if so measure the voltage over the output, oventough it should pulsate at 100 Hz, you should be able to get a reading. If so, measure the voltage over the interrupt pin.
Are you sure all the connections are OK?
If that all fails get a new optocoupler

Hmmm I do get readings, some nearly non existent and some around 500mv.. But it does work now for some reason! If I use the code with the timer library it works but not with the other code. And it seems to work better on interupt 4 than 0.... Do you know why that might be? And lastly, is it possible to run multiple dimmers with different values at the same time? Thanks so much!
diy_bloke (author)  PrinsNathan2 months ago

I am glad you got it working. I do not know why the one code didnt and the other does, unless u are trying to have the arduino do something else in the mean time as well, then the timer is a better option. Also the interrupts should not make a difference. I take it u are using an Arduino Mega?
yes you can run different dimmers at the same time with different values. Currently busy with an RGB (=3 channel) dimmer

My apologies for the late response! I made my 'final' version! I used the photoresist technique to make a PCB, my first stab at custom PCB's and it was awesome. Now I'll add a cheap chinese Arduino pro mini, bluetooth connectivity, and a potentiometer and put it in a nice case... So I can change the value on my phone and manually! Thanks for the fritzing files btw :)


Hi. You should scrape off the "perimeter" track with a knife - otherwise not much insulation between primary and secondary as you see.

diy_bloke (author)  PrinsNathan1 month ago

looks great, thanks for sharing.
Chinese Arduino pro mini's are unbelievably cheap right now. I got them for 1.58 euro a piece.

ahmedmahrous2 months ago

can i use tic106 instead of tic206 ??

diy_bloke (author)  ahmedmahrous2 months ago

no the TIC106 is a thyristor, not a triac

What about BTA16
diy_bloke (author)  ahmedmahrous2 months ago

yes can use

Please tell me triac number that I can use it instead of tic206
diy_bloke (author)  ahmedmahrous2 months ago

i already did, below, i advised BT(A)136. BTA16 also good

AbuW2 months ago

hi, this board can work with raspberry pi? can show the connection for raspberry pi? thanks.

diy_bloke (author)  AbuW2 months ago

yes it can work with Raspberry PI
All you need is an interrupt Pin and and an output pin. You are free to chose those.
The old kernell in the raspberry didnt have interrupts I think but the new one has.
I like yr picture

AbuW diy_bloke2 months ago

thanks for the comment, can you guide which pin to use as interrupt and output pin? in raspberry pi GPIO 18 is PWM. thanks.

diy_bloke (author)  AbuW2 months ago

I know I answered this question but the answer seems to have not logged.
u are sort of free to chose but pin22 is often used for interrupts. for the output pin u do not need pwm, just a pin that can switch on and off

AbuW diy_bloke2 months ago

so, that means, gpio22 is the dimmer signal in right? and i will use gpio23 as zc signal out? which coding to run the dimmer program? care to explain it. thanks.

diy_bloke (author)  AbuW2 months ago

made a start with a program for raspberry, but dont have time right now to finish and test it, but this may help you along:

#!/usr/bin/env python

import time
import RPi.GPIO as GPIO

# handle the ZC event
def ZC_EventHandler (pin):
print "handling ZC event"
# set dimming in your main routine

# turn the TRIAC

# main function
def main():

# tell the GPIO module that we want to use
# the chip's pin numbering scheme

# setup pin 23 as an input
# and set up pins 24 and 25 as outputs

# tell the GPIO library to look out for an
# event on pin 23 and deal with it by calling
# the buttonEventHandler function


# set your timing variable here


if __name__=="__main__":

diy_bloke (author)  AbuW2 months ago

let me just add that the raspberry might be tricky to use for this as i understand the timing isnt always that easy to control.

diy_bloke (author)  AbuW2 months ago

I am not sure what you mean with Zero crossing out, but I presume you mean the signal to drive the Triac.. I think i may choose pin 18 for that but 23 is also ok.
Unfortunately i do not have time to write a program for every microcontroller that people want to port this to but you may find some help here:

the only thing that you would need to do in yr code is to wait for the zerocrossing signal, wait a desired time and fire the triac.
good luck :-)

ahmedmahrous2 months ago

please i cant find TIC206 in the stores ,, what can i use instead of it ??

diy_bloke (author)  ahmedmahrous2 months ago

oddly my replies seem to not register unless I post twice. I answered this but it has disappeared.
Anyway, if u cant find a TIC 206 basically every TRIAC that is suitable for your local voltage is OK. try a BT136

didin.ok made it!2 months ago

Thanks diy_bloke. This work with the last code !

diy_bloke (author)  didin.ok2 months ago

great. your setup looks good. thanks for sharing

Fr3dy made it!2 months ago

I really needed this circuit for a project.. fortunately you posted it here.. it works flawlessly with the last code.. thank you very much!!

photo 1.JPGphoto 2.JPG
diy_bloke (author)  Fr3dy2 months ago

I am happy you liked it Fr3dy. Always enjoy seeing pics showing what others made of it :-)

ksun43 months ago

hey guys! i really need help. the circuit and program code doesnt work with a 220volts AC DIMMABLE LED BULB. i really dont know what to do about it. it works perfectly with ordinary bulbs but i need to let it work with LED bulb for my project. please help me. my defend is in a week. please! please please!

diy_bloke (author)  ksun43 months ago

Hmmm seems my earlier reply didn’t store. It is however largely the same as my mail to you

ksun43 months ago

hey guys! i really need help. the circuit and program code doesnt work with a 220volts AC DIMMABLE LED BULB. i really dont know what to do about it. it works perfectly with ordinary bulbs but i need to let it work with LED bulb for my project. please help me. my defend is in a week. please! please please!

diy_bloke (author)  ksun43 months ago

you also may find this article interesting:

All in all I am not sure what your procet needs to adhere to, but if DC is an option I would go for that

1-40 of 387Next »