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 www.inmojo.com, 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

1-40 of 500Next »
diy_bloke (author) 4 days ago
I am not sure how thick yr traces are so I cannot comment on whether your pcb will hold 1 amp, but soldering a copper wire over the traces to the T1 and T2 of the TRIAC is always a good idea if you use a sizeable load
nrocy made it!5 days ago

Awesome instructable, thanks for all the extra detail and theory you put into it, I've learned quite a lot :)

I built one today and it works great! Used an IL250 and BT136 and etched your board design.

I plan to run a few bulbs in parallel, which I think should work fine so long as I stay under the 4A limit of the triac? I'm planning to write code for a pulsing/flickering effect, is there anything you think I need to consider?

Thanks again for putting this together :)

diy_bloke (author)  nrocy4 days ago

I am happy you like it and thanks for inclusing pics. I always enjou seeing what people made out of an ibble.
in spite of some people experiencing problems, it is a simple design that really cant go wrong. I have built 5 or 6 that all worked first time.
Running bulbs in parallel should be fine, indeed as long as you stay under your Triacs specs.
Code for pulsating should not be too difficult.
Write an alternating High and low value to the circuit and that should do it.
If you want the arduino to do anything else than regulate your light, I advise to use the timer rather than the delays in yr software

You're welcome :) One quick question regarding the track thickness - you mention that the load/mains tracks won't take 4 amps, is it worth just reinforcing them with wire anyway? I'm going to run maybe 200W total at 240V, will they cope with ~1A? Thanks!

what is yr question Rathore? :-)

inmojo7 days ago

The InMojo AC dimmer mentioned above is back in stock and can be found here:

I have a question, if i want to use this circuit with 120V 60Hz Which elements I should change

diy_bloke (author)  andresmora0517 days ago

that is ad extenso decribed in the instructable :-) and even answered inthe question right below :-)

Basically it comes down to adapting the resister values to the optocoupler and using 8.3 usec steps instead of 10 us

LxMantis1 month ago

Hello again diy_bloke! Thanks for this wonderful instructable! I do have a question, if I may. Since I am in the US (120 VAC/60 Hz), what are the main things I should be concerned when building this? My intuition tells me that this should be completely fine and your circuit should still work... what do you think?

Thanks in advance!

diy_bloke (author)  LxMantis1 month ago

the text mentions what changes you need to make. basically it comes down to adapting the resister values to the optocoupler and using 8.3 usec steps instead of 10 us

My apologies! I skimmed through the text and neglected that part. I will be more careful next time. Thank you very much!
diy_bloke (author)  LxMantis1 month ago

no problem. I have been known to overlook whats in front of me sometimes as well

EnoS1 made it!2 months ago

Thank you ... :)

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

diy_bloke (author)  EnoS12 months ago

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

looks great

OBS3553 months 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)  OBS3553 months 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

OBS355 diy_bloke3 months ago

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)  OBS3553 months 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

OBS355 diy_bloke3 months ago

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)  OBS3553 months ago

True! :-)

ЛилияК13 months ago

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)  ЛилияК13 months 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)  ЛилияК13 months ago

I really wouldnt know. never worked with 3 phases

OBS355 diy_bloke3 months ago

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 OBS3553 months 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.

stephanschulz4 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)  stephanschulz4 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:


quocanh834 months ago

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

diy_bloke (author)  quocanh834 months ago

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

nodoubtman4 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 http://www.arduino.cc/playground/Code/Timer1

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

nodoubtman4 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)  nodoubtman4 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)  nodoubtman4 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.McElhenny4 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.McElhenny4 months ago

yes, I describe that in the article

1-40 of 500Next »