Picture of Arduino controlled light dimmer

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. (I say Arduino, but 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.

Phase cutting
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.

Pulse Skip Modulation
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, I can't stress this enough DO NOT use e.g. a MOC3042
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 487Next »
EnoS1 made it!26 days ago

Thank you ... :)

but i used (30Kohm 1/4W) x4 ("+" x2 and "-" x2)

diy_bloke (author)  EnoS126 days ago

if that works for you,great. Thanks for the picture. I Always like seeing what people made of it :-)

looks great

OBS3551 month ago


This works well I am making my self a 4 ch dimmer and your code has helped me immensely,

My only suggestions is a resister and Capacitor should be added to your triac circuit to reduce noise emitted to mains and to limit triac misfire due to noise on the mains.

What my struggle at the moment is I want this to operate of a single button, I will post once I work it out. effectively One press will trigger light off (if light on) or Light on (if light off) and if held will dim up (if at 0 or last dimed down) or dim down (if at full or last dimed up).

This will mean you can us momentary buttons in single mech wallplates

diy_bloke (author)  OBS3551 month ago

I am happy you like it. With regard to the snubber network you suggest, I think I mentioned that somewhere in the article and yes that is a choice. I myself havent really felt the need for it as i havent experienced triac misfire. Audio is another reason why you could consider a snubber, but I hardly operate the two at the same time. But if you decide to regulate inductive loads it is a must.
Snubber netwoks can however also introduce problems.

It is certainly possible to use a button as you describe, but you may wonder if you want to sacrifice an arduino on that, as there are build in dimmers who do what you describe.
Good luck

The snubber network will also reduce interference with DTB, as DTB signals are unfortunately more sensitive then old school analogue.

Yes there are dimmers that do this already, but I also want to be control this via a webserver and have true feedback to the web surface and to the switches on the walls, and arduinos are relatively cheap in comparision to putting multiple dimmers at every switch, this would also drop the possibility of multiple switch locations to control the one light dimmer which I would like to do as well.

diy_bloke (author)  OBS3551 month ago

that is a good idea. I suddenly wonder... it should be possible to do this with an ESP8266 and skip the whole arduino and have a wifi enabled dimmer with a 3 usd microcontroller board

size and form factor and $$ plus the fun of doing it yourself is part of the idea. but as I sad thanks for your info it was helpful

diy_bloke (author)  OBS3551 month ago

True! :-)

Hi! I want to gather for 3-phase voltage electric motor, anyone faced, achievements have somebody?

PS I'm from Russia, I'm sorry for the translation!

diy_bloke (author)  ЛилияК11 month ago

nothing wrong with yr translation :-)
I do not know enough of 3 phase grids to be confident in giving advice.
Maybe one of the other readers can help

I'll wait for a line of thought. I would have to begin to understand, "zerocrossing" to do for each phase or single-phase programmatic delay for the rest?

diy_bloke (author)  ЛилияК11 month ago

I really wouldnt know. never worked with 3 phases

you will need to zero cross detect each phase that you wish to PWM the catch you will have is what is your application, as some motor circuits run additional components via a created neutral from a star winding configuration on the motor, I would suggest that unless you have had experience with three phase that you leave it alone, household voltages are a lot more dangerous than people seem to give it credit, and you will need to also think about how the motor starts ie is it capacitor start and run? or not etc, are the individual windings accessible to you? or are the using a centrifugal switch to change windings on start up etc, you need to make sure the motor is designed to do what you want to do, not all motors are designed equal.

OBS355 OBS3551 month ago

Check out the image attached, effectively three phase is just 3 single phase circuits with offset timings that give you access to 415v (if 220v grids) between any two of the phases.

stephanschulz2 months ago

about the trailing edge dimmer, would you have a schematic should the parts and configuration that would need?

i have tried the TRIAC dimming but am reading how trailing edge would be better for AC-LED light bulbs.



diy_bloke (author)  stephanschulz2 months ago

I am not sure if trailing edge dimmers are better for AC lamps, perhaps, I just dont know for sure. I was working on a trailing edge dimmer, but it required conversion to DC, which is not a problem, but then PWM became another option. Anyway, havent finished it yet, you may want to look here for some ideas:

quocanh832 months ago

Can i change DB104 = DB107, 4N25 to 4N35?

diy_bloke (author)  quocanh832 months ago

yes you can. the DB107 can take 1000 Volt. that is more than sufficient

nodoubtman2 months ago

AC Light Control
Uses up and down buttons to set levels
makes use of a timer interrupt to set the level of dimming

#include <IRremote.h>
#include <TimerOne.h> // Avaiable from

const int RECV_PIN = 6;

IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long last = millis();

// Hex code for remote control buttons
#define POWER 0x10EFD827
#define A 0x10EFF807
#define B 0x10EF7887
#define C 0x10EF58A7
#define UP 0x10EFA05F
#define DOWN 0x10EF00FF
#define LEFT 0x10EF10EF
#define RIGHT 0x10EF807F
#define SELECT 0x10EF20DF

volatile int i=0; // Variable to use as a counter of dimming steps. It is volatile since it is passed between interrupts
volatile boolean zero_cross=0; // Flag to indicate we have crossed zero
int AC_pin = 3; // Output to Opto Triac

int dim2 = 0; // led control
int dim = 128; // Dimming level (0-128) 0 = on, 128 = 0ff
int pas = 8; // step for count;
int freqStep = 65; // This is the delay-per-brightness step in microseconds. It allows for 128 steps
// If using 60 Hz grid frequency set this to 65

void setup() { // Begin setup
pinMode(AC_pin, OUTPUT); // Set the Triac pin as output

irrecv.enableIRIn(); // Start the IR receiver

attachInterrupt(0, zero_cross_detect, RISING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
Timer1.initialize(freqStep); // Initialize TimerOne library for the freq we need
Timer1.attachInterrupt(dim_check, freqStep); // Go to dim_check procedure every 75 uS (50Hz) or 65 uS (60Hz)
// Use the TimerOne Library to attach an interrupt


void zero_cross_detect() {
zero_cross = true; // set flag for dim_check function that a zero cross has occured
i=0; // stepcounter to 0.... as we start a new cycle
digitalWrite(AC_pin, LOW);

// Turn on the TRIAC at the appropriate time
// We arrive here every 75 (65) uS
// First check if a flag has been set
// Then check if the counter 'i' has reached the dimming level
// if so.... switch on the TRIAC and reset the counter
void dim_check() {
if(zero_cross == true) {
if(i>=dim) {
digitalWrite(AC_pin, HIGH); // turn on light
i=0; // reset time step counter
zero_cross=false; // reset zero cross detection flag
else {
i++; // increment time step counter

void test_dimmer(){
if((dim_ac>=128) || (dim_ac<=0))

void loop() {

if (irrecv.decode(&results))

if (results.value == UP){
// If it's been at least 1/4 second since the last
if (millis() - last > 250) {
Serial.print("Dimming up --> ");
if (dim<127)
dim = dim + pas;
if (dim>127){

if (results.value == DOWN ){
// If it's been at least 1/4 second since the last
if (millis() - last > 250) {
Serial.print("Dimming down --> ");

if (dim>5)
dim = dim - pas;
if (dim<0){
} // loop

there's not dimming at all, IR receiver doesnt work with this code..

Do you think you can help me?

thank you sooo much!


diy_bloke (author)  nodoubtman2 months ago

you may have a timer conflict. I am not sure anymore what timer the receive function uses, I thought it was timer2, but not sure.
But I do know the IRremote library to have many problems when timers are used. Check your library and if necessary change the timer

nodoubtman2 months ago

Hi! Nice project. I tried to use IR remote control library to use it. But when i start the program, the lamp is turning on, and i cannot do anything with the remote .

can you help?

thank you!

diy_bloke (author)  nodoubtman2 months ago

Hi Marc.
kinda hard to say whjat is wrong as I have no idea what your program looks like. can you dim the lamp without an IR remote, like with the program i provided, so at least it is clear the circuit is OK?
As far as the remote goes, start with printing the dim values to your serial port so at least you know the commands are received.

Thank you for answering diy_bloke, i can send you the program with the ir code, just let me know what is wrong?

Where can i send you my program? at what email address?

thank you so much! :)


diy_bloke (author)  nodoubtman2 months ago

just send it in a private message in instructables

yes your program works very well :)

I just want to create and IR dimmer with your program , is it possible?

thank you!


Ian.McElhenny2 months ago

Also how would the values be effected for a 120V 60Hz application? Like the 220 Vin resistors and anything else?

diy_bloke (author)  Ian.McElhenny2 months ago

yes, I describe that in the article

Ian.McElhenny2 months ago

What is the purpose of the LED in series with the 220 ohm resistor for the dimmer signal? Is that just for a reference to see if the arduino is sending a signal?

diy_bloke (author)  Ian.McElhenny2 months ago

indeed it is. Ofcourse not much will be visible if you are sending a short signal, but one can use the circuit also to just switch on or off and then the LED has an indicator function. Should be red or green, with blue and white the voltage drop may be too big

Murphi3 months ago

Hi, after reading several times the post I decided to do it, but I have two questions..

-For make the pcb, we must print at paper something like this:

But I don´t see in fritzing this option..I select the PCB tab in the soft and I see this:

If I print, it prints as the second capture, no? What I have to do to see tracks in black ?? Sorry, but it´s first time that I use Fritzing...

-The triac that I have is BTA12-600 BW (12a and up to 800v), and I guess it works fine, right??

Thanks and regards

diy_bloke (author)  Murphi3 months ago

Go to 'File-Export-As Image-pdf'
Then just print that pdf on some glossy paper.
depending on your settings you may get more than 1 pdf: for the coppertracks, for the silkscreen etc. Pick the one that is right. as the ink layer is put on the copper, make sure it is the one that is mirrorred. THAT IS USUALLY NOT THE ONE THAT IS CALLED 'MIRRORRED'
Th epicture you showed is just the component lay-out
BTA12-600 is fine

Murphi diy_bloke3 months ago

Ok, thanks for your advice..In fritzing I select "maximum" for width..

I will make the circuit tomorrow and the print monday or tuesday (I don´t have laser printer and the correct paper..)

Thank you for your support and regards.

diy_bloke (author)  Murphi3 months ago

good luck

diy_bloke (author)  Murphi3 months ago

just a final piece of advice: the standard coppertracks in Fritzing are a bit narrow. Make them wider by selecting a larger track width

Where do you connect the zero-crossing pin in arduino
diy_bloke (author)  Tanishq Jaiswal3 months ago

that depends on the interrupt you want to use, but the example program I provided it should be attached to D2

muhammad863 months ago

Hi DIY bloke.Thanks becoz it works and furthermore it works with a LED lamp(the dimmable one).However i realised that the i value should be from i=10 to i=120 for incandescent light. It should not go to i=128 becoz it will ficker at i=128.Somewhere from i=120 to i=125 should be a good point to end. Please correct me if i am wrong.This is what i observed.

diy_bloke (author)  muhammad863 months ago

That is good to hear. You are lucky on the LED lamp as some work and some do no.
With regard to the lowest and higehst values, yes, because the zerocross detection is not an ideal narrow pulse and the period length of the grid may vary, choosing extreme values like <5 or >125 may give some flickering.
If you have time, add a picture in the 'I made it' section. I always like to see how peoples makes turn out

MuratE13 months ago

can you show me links to Arduino?

diy_bloke (author)  MuratE13 months ago

u asked this also in a pm. i replied there

1-40 of 487Next »