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 414Next »
I am posting a better picture of the circuit. The arduino is not connected .. If you could see a problem please mention to be.. Thanks indeed
15 - 17:43.jpg

looks OK at first sight, please do the tests as i described and let me know the results.
You can also add the following to your setup and see what the lamp does:

void setup()
pinMode(AC_LOAD, OUTPUT); // Set the AC Load as output
for (int i=0; i < 10; i++) {
digitalWrite(AC_LOAD, HIGH); // triac firing
digitalWrite(AC_LOAD, LOW); // triac Off

Ok thanks a lot
This is the circuit
15 - 12:12.jpg
diy_bloke (author)  christodoulos.menelaou11 hours ago

it is quite a collection of wires and not easy to follow... but as far as I can see your optocoupler, th eone that needs to trigger the TRiac, doesnt get the required input. See that brown wire? that seems to be only connected at one side... as far as I can see

diy_bloke (author)  diy_bloke7 hours ago

though it could be it is connected to the LED, difficult to see

Ok thanks a lot ...the problem seams to be the load it safe to measure the voltsge over the "220" vlamps..?
diy_bloke (author)  christodoulos.menelaou11 hours ago

Well if you make sure not to touch anything with yr bare hands, it is safe, The reason I ask you to do this is to make sure that the board is getting 220 Voltage.
But if you think he problem is the load part, please just recheck the connections.
Even if you think it is OK, recheck again. I do not know how many times I was sure I connected something right, only to find several wiring mistakes when I really checked.

I made the circuit. But I have a problem. The LED dim OK but the lamp is always switched off. No flickering no dimming. Can anybody help me with this ?

Well that is odd, but here is what I like you to do:
-check and double check all your connections.
-make sure the lamp you are using works
-measure the voltage over the clamps that say "220V" in the circuit. Is that 220 V?
-remove your Arduino and put 5Volt on the entrance of your circuit.
-- both the LED and the Lamp should be full on. If the lamp is NOT on please check the voltage over the 1k resistor, remove the 5V voltage and check the voltage over the 1k resistor again and let me know if that is different.

I cannot stress enough to be careful when measuring when this circuit is connected to the mains voltage BE CAREFULL. This voltage can kill you.

Yes of course I have connected it to the main...I am in cyprus its 220/240 V ,50 hz
diy_bloke (author)  christodoulos.menelaou12 hours ago

well then the answer to your earlier question should be clear: you apply 5Volt to the entrance of the circuit, the optocoupler opens, the Triac gets triggered, the lamp gets current and burns.

diy_bloke (author)  diy_bloke12 hours ago

anyway, please go through the steps as i described and let me know what the result is

How a 220V lamp could be full on when applying 5 volts?
diy_bloke (author)  christodoulos.menelaou21 hours ago

I start to suspect you do not really grasp the workings of this circuit.
Have you connected this circuit to the 220Volt grid at all?

How a 220V lamp will be full pn when applying just 5V ?
andre663510 days ago

Hi, Thank you for your guide.

I was wondering if the phase control would work if I used 50W power resistors like this:

Or do I need to change some components in the circuit? I plan to use 3 x 50W power resistors connected in series. Even the incandescent bulbs are 90% heat source so I assume that there are no significant differences between the power resistors and the incandescent bulbs. At home I have 220AC.

Or is it better to use an on/off circuit to control the power resistors? I plan to build a cooker with arduino pro mini + lcd in which I will make fermented food like yoghurt, etc. I hope you can help.

Thanks again!

diy_bloke (author)  andre663510 days ago

Andre: I am not sure if i fully understand you. Do you mean you want to connect the power resistors in place of the lamp? (I presume you do).
Well, yes the phase control is working then as a lamp to this circuit just looks like a resistor.

You could use an OFF/ON circuit, but as it is AC, the dose of power is difficult to regulate as you do not know at what time you are switching if you do not have the zerocrossing.
Having said that.. many thermostatic cookers work with a simple OFF/On circuit and you could decide for that as well, with just a temperature sensor giving feedback. It might be the easiest in your case

Thank you for your answer. Yes, I plan to use the power resistors instead of the lamp.

Indeed, I will use a temperature sensor and then I will try to use the phase control or the OFF/ON circuit to control the power resistors.

I will test the phase control with an incandescent bulb to make sure that I'm doing it right and then I will switch the bulb with the power resistors.

Thanks again.

diy_bloke (author)  andre66359 days ago

the phase control enables you to let the yeaster/bioreactor simmer for a long time on a low temperature. With the on/off you can only send bursts of full power to yr yeaster/bioreactor. That might be OK for your use, especially if you have something to retain heat (i.e. isolated reactor or a heatsink around yr resistors)

diy_bloke (author)  diy_bloke10 days ago

forgot some interpunction that makes a sentence hard to read:
Well, yes: The phase control is working then, as a lamp - to this circuit- just looks like a resistor.

I can't make it work with attachInterrupt()... So, I can use it to switch a lamp ON or OFF using digitalWrite(AC_LOAD, HIGH) or digitalWrite(AC_LOAD, LOW), but when I use interrupt it stop working, i.e. regardless to dimming value, the lamp is always off:

int AC_LOAD = 3;

int dimming = 0;

void setup(){

pinMode(AC_LOAD, OUTPUT); // Set the AC Load as output

attachInterrupt(0, zero_crosss_int, RISING);


void zero_crosss_int() {

int dimtime = (65*dimming);

float propTime = 8.33;

delayMicroseconds(dimtime); // Off cycle

digitalWrite(AC_LOAD, HIGH); // triac firing

delayMicroseconds(propTime); // triac On propogation delay

digitalWrite(AC_LOAD, LOW); // triac Off


void loop() {

dimming = 64; // Any value does nothing, i.e. lamp is always off



I use regular 60W incandescent lamp. Am I doing something wrong? Or may be i did something wrong in pcb?

Photo Dec 27, 16 08 13.jpgPhoto Dec 27, 16 08 46.jpgPhoto Dec 27, 16 09 17.jpg
diy_bloke (author)  robert.tarasov.71 month ago

Robert, I am sorry you are having problems. As many have built it succesfully, and the software is tested... my firts guess is that somehow the arduino might not be piking up the interrupt signal. So there are a few tests you can do:
1 Check your PCB. Is really everything soldered correct ly and connected correctly? Dont think too soon it is OK. I have had veroboard circuits that i was sure I had soldered up correctly, only to find 4-5 mistakes when i really checked.
2 If it is correct, measure with a multimeter. First check if the bridge rectifier is indeed getting a voltage on the primary side and if it is giving off a voltage on the secundary side
3 Then check if your optocoupler gets any voltage. Also test the secundary side of the optocoupler. Though it is true that that only gets a pulse every 10mS, you should be able to measur something there.
4 Check if you made the connection to the proper pin on the arduino.

If that is all ok, write a small routine in the ISR (i.e. the zero cross interrupt routine) that increases a counter on every interrupt and output that counter to the serial port (in your main loop).
That way you can test if any zerocross signal is detected.

Let me know what the outcome is

Thank you very much for your tips! I'm learning now and this is my first "big" pcb :) I will report about results soon.

diy_bloke (author)  robert.tarasov.729 days ago

ok, check the missing connection i mentioned first :-)

diy_bloke (author)  robert.tarasov.71 month ago

I might have found your problem already. It seems that your 4N25 only has one connection on th eprimary side.You have that red wire bridge coming off the '+'... but judging fromth e underside of yr pcb that isnt connected to anything

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????

ซันซันแ, I temporarily suspended facebook but i will look into yr recent question.

merry xmas

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

TomazS1 month 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)  TomazS1 month 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)  TomazS1 month ago

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)  TomazS1 month 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

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

1-40 of 414Next »