Dimming 230V AC with Arduino

I am going into the point immediately.The proper way to control dimming 230v AC, is through phase control with a Triac: the Triac then is fully opened, but only during a part of the sinus AC wave.3f9a9
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 for a number of microseconds delay starting from the zero crossing therefore gives a predictable level of dimming.

I have discover two ways so that to dimm a incadence 230V bulb using Arduino. Both uses a triac but the main difference is how arduino should understand the zero cross detection of AC. This method is called AC phase control. It is the method used in many light dimmer and heater and motor power control circuits. For a better understanding we will name the first  way software way and the second  analog way .

Software Way

Let’s explain in details the operation.


The zero-crossing detection circuit provides a 5v pulse every time the ac signal crosses zero volts. We detect this with the Arduino and leverage interrupts to time the trigger circuit precisely in synchronization with these zero-crossing events. The method for power control is shown in the diagram below.


Once a zero crossing is detected, the triac remains off for a controlled amount of time (t1) . The longer this time is, the less power the ac circuit receives. Once the “off-time”, t1 has elapsed, the microcontroller turns on the triac by applying a voltage to the gate (shown in red). Once turned on, the triac will remain on even after the gate voltage has been removed. It will turn off if the gate voltage is zero the next time the ac wave crosses zero. Because of this, we do not need to take care to turn the triac off when the ac signal crosses zero again. All we need to do is to ensure that the triac gets turned off inside of the period of ½ wave (t3). The duration of the gate pulse (t2) is determined by a minimum requirement of the traic. If this pulse is too short, the traic will not fire Once the second zero crossing occurs, sice there is no voltage on the gate, the triac remains off until triggered again in the next ½ cycle. The net result here is that we “chop” parts of the wave out resulting in lower average power. This is essentially how one accomplishes “PWM” control of an AC wave.

We will be using interrupts and the arduino timer to precisely control the timing of the triac gate. The AC signal is 50 Hz. What this means is that the AC signal crosses zero, reaches peak positive voltage, crosses zero, reaches peak negative voltage and returns to zero 50 times each second. The period (length of time this takes) is 1/50 or 0.02 seconds (20 milliseconds). A half cycle (the time between two zero-crossings) occurs in 10 milliseconds. This is t3 in the figure above.

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.

You can modify the circuit for controlling motors too. It consists of an additional resistor and capacitor. The gate current is below 15mA. If you are using a less sensitive triac to control the inductive load, reduce the resistor from 2.4kΩ to 1.2kΩ, providing more current to drive the triac and increase the capacitor to 200nF. This snubber circuit is there to protect the triac from the high voltage generated from an inductive load. The feedback may cause some problem for non-inductive load. The small leakage can be significant enough to turn on small load (for example a lamp).

Ok, let’s to software stage now

What the software needs to do is to detect the zero crossing, and then wait for a set amount of time on that sinuswave to switch on the TRIAC.

In Europe we have 50 Hz

50Hz is 50 waves per second.
Each sinus wave thus takes 1000ms/50=20ms  (miliseconds)

As there are 2 sinuspeaks in a wave that means that after every zero detection there is a 10ms period that we can regulate. This t3 at the diagram.
As we are using TRIACs, what the software needs to do is to wait for the zero point at the sinuscurve, take note of that and then wait a specified amount of time within that 10ms period to send a pulse to the TRIAC.
If it sends that pulse at 5ms, the lamp will only burn at half power.

We will use  an interrupt to tell the program that there was a zero crossing.After the zero crossing is detected the program needs to wait for a specified amount of time and then switch on the TRIAC.


Interrupt driven:
To use an interrupt, first we need to set that up. On the Arduino that is as follows:

void setup()
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
  attachInterrupt(1, zero_crosss_int, RISING);  // Choose the zero cross interrupt 

What this says is that the interrupt is attached to interrupt 1, it goes to a function called “zero_crosss_int”  and it reacts to a rising flank on the pin.

Interrupt 1 is pin 3 of arduino

In the Zero_cross_int function that the program jumps to after the interrupt  we determine the time we need to wait before firing the TRIAC. We will also add a bit of functionality. We don’t just want one level set that the lamp burns on, we are going to add some functionality to regulate the light level in steps.

For that I have chosen the fully arbitrary amount of  128 steps. That means that every step is 10ms/128 = 10000us/128=75us (in fact it is 78, but I get to that later). The total dimtime then is calculated from 75x(1 to 128). The number between 1-128, which determines our level of dimming, we assign to the variable integer ‘dimming’

int dimming = 128;

void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
  int dimtime = (75*dimming);    // For 60Hz =>65   
  delayMicroseconds(dimtime);    // Off cycle
  digitalWrite(AC_LOAD, HIGH);   // triac firing
  delayMicroseconds(10);         // triac On propagation delay (for 60Hz use 8.33)
  digitalWrite(AC_LOAD, LOW);    // triac Off

What happens here is that the program first calculates the dimtime (=time to wait before the triac is fired)
It then waits that amount of time, subsequently waits that amount of time and fires the Triac. The Triac will switch off again at the following zero crossing, but we are going to already write a low on the TRIAC pin to avoid accidental ignition in the next cycle. We need to wait a bit however to know for sure the TRIAC is on, so we wait 10us
That also is the explanation why I am using 75 rather than 78 for my steptime as 10000-10=75

The only thing then left to do in the main program is to set the level at which we want the lamp to burn:

void loop()  {
  for (int i=5; i <= 128; i++){

What happens here is a simple loop that regulates the lamp up in a 128 steps

We can use a timer to determine the amount of time to wait.

As discussed in the previous theoretical page, the software is fairly easy.
If you want to develop your own software all you need to do is:
Wait for the zerocrossing
Wait a specific time between 0 and 9090 microseconds (9090=10.000-10)
switch on yr TRIAC
Wait for about 10us (that is the time you need to make sure the TRIAC is on)
switch off yr TRIAC (in fact, you only remove the triggersignal to the TRIAC, the TRIAC will stay on till the next zerocrossing)

At 50Hz that interrupt is every 10ms or 10.000uS

As the program varies the dimming from Full to Off in 128 steps (that is just a choice that was made, could be 100 steps as well), at 50 Hz we need the steps to be 75 uS .

It works as follows:
The interrupt function”zero_crosss_int” gets called every time a zero-crossing is detected, which is 100times/second. It’s only function is to set the time that the Triac is switched on to the value of the variable ‘dimming’
In the main loop of the program the actual value of that variable is set

int AC_LOAD = 3;    // Output to Opto Triac pin
int dimming = 128;  // Dimming level (0-128)  0 = ON, 128 = OFF

void setup()
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
  attachInterrupt(0, zero_crosss_int, RISING);  // Choose the zero cross interrupt # from the table above

//the interrupt function must take no parameters and return nothing
void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
  // Firing angle calculation : 1 full 50Hz wave =1/50=20ms 
  // Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle) 
  // For 60Hz => 8.33ms (10.000/120)
  // 10ms=10000us
  // (10000us - 10us) / 128 = 75 (Approx) For 60Hz =>65

  int dimtime = (75*dimming);    // For 60Hz =>65    
  delayMicroseconds(dimtime);    // Wait till firing the TRIAC
  digitalWrite(AC_LOAD, HIGH);   // Fire the TRIAC
  delayMicroseconds(10);         // triac On propogation delay (for 60Hz use 8.33)
  digitalWrite(AC_LOAD, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC

void loop()  {
  for (int i=5; i <= 128; i++){

About the software:  theoretically in the loop you could let variable ‘i’  start from ‘0’. However, since the timing in the interrupt is a bit of an approximation using ‘0’ (fully on)  could screw up the timing a bit. the same goes for 128 (Full off)  though that seems to be less critical. Wether ‘5’ or perhaps ‘1’ is the limit for your set up is a matter of trying, your range may go from e.g. 2 to 126 instead of 0-128.


Let’s make a break now and continue later with the analog way

IMG_1425 (Custom)

Analog way

Have you ever imagine that the same result ,could be achieves using a 555 timer? An yes, 555 is worthy one of the most populated IC! Take a look ..

In other words ,the only difference with the software way ,is just the manner we ‘ll make the microcontroller , to realize the zero cross detection.


R1 Resistor 10 KOhm 1/4 Watt 5% Carbon Film
R2 Resistor 1 KOhm 1/4 Watt 5% Carbon Film
R3 Resistor 4.7 KOhm 1/4 Watt 5% Carbon Film
R4 Resistor 100 KOhm 1/4 Watt 5% Carbon Film
R5 Resistor 10 KOhm 1/4 Watt 5% Carbon Film
R6 Resistor 1 KOhm 1/4 Watt 5% Carbon Film
R7 Resistor 4.7 KOhm 1/4 Watt 5% Carbon Film
R8 Resistor 1 KOhm 1/4 Watt 5% Carbon Film
R9 100 KOhm Linear multiturn precision potentiometer
R10 Resistor 1.5 KOhm 1/4 Watt 5% Carbon Film
R11 Resistor 1 KOhm 1/4 Watt 5% Carbon Film
R12 Resistor 1 KOhm 1/4 Watt 5% Carbon Film
R13 Resistor 1 KOhm 1/4 Watt 5% Carbon Film

C1 1000 uF 16V Electrolytic Capacitor
C2 0.1 nF Ceramic Capacitor
C3 1 uF 16V Electrolytic Capacitor

D1 1N4001 General Purpose Diode Rectifier
B1 2W10M Single Phase 2 Amps Silicon Bridge Rectifier

Transistors – TRIACs
T1-3 BC548 Switching and Applications NPN Epitaxial Transistor
T4 BT136D Sensitive gate TRIAC

IC1 555 Timer
MOC3021 Random Phase Optoisolator TRIAC Driver Output

The mains AC voltage is transformed to 9VAC through the transformer. the signal is rectified with a full-wave bridge rectifier. Immediately after the rectifier, the signal is driven to the zero-cross detection circuit. A large capacitor (C1) is used to smooth a part of the rectifier’s output power. This will be used as the power supply of the rest of the circuit. The diode D1 is very important. Without this diode, the signal that is driven to the zero-cross detection would be smoothed as well, and the zero-cross detection would be impossible.
The output of the zero-cross detection is directly sent to the trigger input of the 555 timer. The control voltage is driven to the input 5 of the 555 timer. The rheostat R9 is used to control the maximum delay of the 555 timer, so that with maximum control voltage, it will NOT exceed the length of a semi-period, otherwise the light will be turned into a crazy-disco-light.

The output is then inverted with the transistor T3 and the signal is driven to the P gate of the optocoupler. the optocoupler is used to have complete galvanic isolation between the control circuit and the power circuit. The power circuit uses a BT136 TRIAC to control the load. This TRIAC is capable of driving a 4 amperes load at 600 volts. Feel free to use a more powerful TRIAC.

[embedyt][/embedyt] [embedyt][/embedyt]

In the next video you can see ,how i control the AC phase to the incandescence lamp, changing the output  voltage from a power supply to control voltage input to pin 5. What you to the oscilloscope ,is the collector of T3. Buttttt,what happens to triac ,you have to look at the first video at the the of the article! 
If you wonder ,if we could control the AC phase of the lamp just only driving with PWM the optocoupler or directly the gate of the triac , the answer is NOOOOO. We must now,when AC signal pass thorough zero!! We have to know that, because immediately after zero cross detection the bulb should be controlled.

Both ways ,works perfect!! If you are familiar with programming and you don’t want too many components on your circuit ,you may follow the first one. If you don’t have programming skills and if you are old fashion electronic ,second way i sthe best. I have to admire second way is more efficient. I have change R9 to 20k and i control voltage input from 0-10V .This is very nice ,because it may be used with dimm able  fluerecent lamps 0-10DC Volts. Plus, it can be easily converted to 0-5V ,so that use it with a microcontroller , using PWM .The but thing  is that it need a transformer  .

Every way you change, good luck

181 thoughts on “Dimming 230V AC with Arduino

  1. Thankyou for such a wonderful explaination. I am making the project of controlling ac fan with arduino. can you give me the ic number of the triac to use? my motor rating is 220v ,50 hz, 0.5 amp. Further more what modifications do i make in the circuit?


    1. Hello ,triac is BT138, it can hadle much more current from youyr motor, you don’t have any problem.You don’t have to do any modification ,just only take care with 230Volt!

  2. Thank you for sharing…
    btw how to control multi AC dimmer with arduino?
    can you give me some explanation to controll it and carculate the time cycle?

  3. First ,try to make one circuit to work and after that try to add 2 or more circuits. With thoso component you use it should work.
    Try to invert the pins of the triac not the gate the other two. If you own an oscilloscope it would be very useful.

  4. thanks for this great explanation.

    i am now wondering how one would go about controlling up to 10 different lights with different dimming speeds.
    using a delay in the zero-cross interrupt wouldn’t work since not all 10 lights should be on the same amount.

    i have this code but again it dims all lights the same amount:
    void zero_crosss_int() // function to be fired at the zero crossing to dim the light
    // Ignore spurious interrupts
    if (sinceInterrupt < 2000){
    thePeriod = sinceInterrupt;
    sinceInterrupt = 0;
    timer0.begin(triacFire, theOffset + dimming); // 4 mSec

    void triacFire()
    for(int i=0; i <pinAmt;i++){
    digitalWrite(AC_LOAD[i], HIGH); // triac firing
    delayMicroseconds(8.33); // triac On propogation delay
    for(int i=0; i <pinAmt;i++){
    digitalWrite(AC_LOAD[i], LOW); // triac Off

    1. i think i solved it.


      #define NUM_LEDS 30 //30

      int PinInt1 = 2;
      int AC_LOAD[30] = {
      5,3,4,1,6,7,8,9,10,12,24,25,26,27,28,29,30,31,32,33,14,15,16,17,18,19,20,21,22,23}; // Output to Opto Triac pin

      elapsedMicros sinceInterrupt;
      unsigned long thePeriod = 0;

      volatile unsigned int timerFire_cnt = 0;

      int fadeDirs[NUM_LEDS];
      int fadeValues[NUM_LEDS];
      int fadeSteps[NUM_LEDS];

      int maxVal = 250;
      int minVal = 0;

      unsigned long fadeTimer;

      IntervalTimer timer0;

      void setup(){


      for(int i=0; i <NUM_LEDS;i++){
      pinMode(AC_LOAD[i], OUTPUT); // Set the AC Load as output
      pinMode(PinInt1, INPUT);
      attachInterrupt(PinInt1, zero_crosss_int, RISING); // Choose the zero cross interrupt # from the table above

      timer0.begin(timerFire, 40);

      for(int i=0; i<NUM_LEDS; i++){
      fadeValues[i] = 0; //random(maxVal);
      fadeDirs[i] = 1;
      fadeSteps[i] = random(1,35);//20);

      void zero_crosss_int(){
      // Ignore spurious interrupts
      if (sinceInterrupt maxVal-10){
      //at the end/ near the end of 1/2 sinus curve turn pin off that triggers triac
      for(int i=0; i<NUM_LEDS;i++){
      digitalWrite(AC_LOAD[i], LOW);
      for(int i=0; i fadeValues[i] ){
      //for first part of 1/2 sinus cruve triac is off, then we turn triac on by setting pin high
      //triac automatically turns on next zero crossing
      //but not arduino pin
      digitalWrite(AC_LOAD[i], HIGH);

      }//end for


      void loop(){



      void autoFading(){
      if(millis() – fadeTimer > 20){
      fadeTimer = millis();

      for(int i=0; i= maxVal){
      // if( i == 0) Serial.println(“>”);
      fadeDirs[i] = -1;
      fadeValues[i] = maxVal;
      if(fadeValues[i] <= minVal){
      // if( i == 0) Serial.println("<");
      fadeDirs[i] = 1;
      fadeValues[i] = minVal;



        1. i bought only one because i wanted to have a reference design that i knew would work. now i am building my own. just wondering how many watt this board can handle.

          1. Bt 136s with a good heatsink can handle around 10A. It means 2Kw.
            And as i can see copper traces are 105um, those are enough for 10A.
            Usual copper trace are 35μm ,but 105μm are enough for 10Amps

  5. May I know Software Way component T1’s spec and what resistor should be used to replace 30k 1/2w if main ac is 110V thanks

  6. T1 specs depend on the load you want to handle. Most usual are bt136, bt138 or if you want more amps, you can use another one.
    30k 1/2w are not critical, try 15k 1/2w

    1. Hi,

      Why 1/2W ? I think that very litle current is involved here, so 1/4W would be enough… no ?

  7. thank you for sharing
    in the software way how the arduino knows that zero crossing is detected ?
    and if zero detection circuit is used at which pin is the zero crossing is attached to ?
    is declared in this line : attachInterrupt(0, zero_crosss_int, RISING); ?
    thank you for your help

  8. @nahdi11
    in my code this is the pin that detects the zero crossing:
    int PinInt1 = 2;

    everytime a zero crossing is detected an interrupt function gets called:
    attachInterrupt(PinInt1, zero_crosss_int, RISING);

  9. please are you saying we can’t directly send pwm to the opto isolator or triac without a zero-crossing circuit?

    1. Yes exactly ,you cannot send directly to opto ,because opto is a switch that triger the gate of the triac, donot confuse it with the base of a transistor that you can manage the current with pwm at its base ,it is not the same.

  10. i have been using the inmojo circuit with 25 channels controlled from one teensy micro controller.

    most things work great.

    now i am trying to dim AC dimmable LED light bulbs. which works but there is a faint flicker, that is most visible in the lower brightness range.
    i opened up a “professional” chauvet dmx 4 channel dimmer and see a bunch of parts that i do not use. 275 acv 0.1 uf capacitor,
    big wire coiled around metal rings, etc.

    what could i do to smooth out the flicker?

    thanks. stephan

  11. my 2nd question would be if the control voltage input to your 555 can be a pwm signal?

    since i want 25 separate dimming channels but my interval interrupt timer i use to determine if the triacs should get triggered seems to be too slow. so, maybe using one 555 per channel will remove the need for a super accurate timing from the micro controller’s side.


    1. I have notice that flickering but i could not find any solution trying only with arduino,i ‘ll tell you the solution i found. As for your 4 channel dimmer those big coils with capacitors are used for exactly this purpose. I have not search circuits with coils but i am sure there are a lot ,so that flickering will be total disappeared .
      I have a big circuits for birds and i will upload it in next days , in that circuit i didn’t want flickering at all because for birds it is annoying.
      (answer for the second question)
      I used an arduino with a code that in a period of 30 minutes, halogen lamp was lighting from 0% to 100 % and the opposite at the night. (So it is the same for you if you dim it with a potetiometer). I drive pin 5 of the 555 .with a pwm analog output from arduino. I have to admire there was a very little flickering .so i add a capacitor after the resistor that goes to pin5 in 555. Now it is perfect ,even if lamp light 1% or 99%.. The circuit is big because both arduino and 555 timer are associated , but who cares it is working perfect. I will upload the circuit in eagle ,maybe you confuse but try to focus to the connection of arduino and 555 timer
      The capasitor i am saying is c11 at the eagle circuit

      1. thanks so much.
        the schematic has lots of parts in it. do you think you can send me one with only the basic parts needed? basically remove that is not needed. i see there is something about a battery, an atmega, relay etc.
        that would be so great.

        i like the idea of using the 555 to get the zero crossing, since i have seen that my teensy micro controller sometimes seems to misses a zero crossing which causes the lights to flash randomly.

        thank again for your great website and efforts.

          1. hi.
            did you have time to put together this simplified schematic and parts list?
            what is the VCC value?
            what if i need to use a ac to ac transformer with a value other then 9vac?

  12. Hello , i have done it it is here,

    I think i have forgot to mention something here, that i didn’t for that circuit . Because of lack the the optocoupler 4n25 as shows the upper diagrams above,detection of zero cross detection would be impossible.
    Instead 4n25 ,the diagram i have upload ,detect the zero cross detection ,through transformer and net R1,R2,R3,R4,R5,R6,C2,T1,T2. Transformer unfortunately is a must ,because eventually with transformer help and resistors arrives at pin 2 of 555 timer, the signal for detecting zero cross detection.
    Transformer is not used to power supply the circuit,it is only for zero cross detection, so consumption for this is only 50 mA, you can use from 5-15 volts.
    12 V is the power supply for the circuit

  13. my next step is to make a PCB with a few of the 555 on it, so make a multi channel dimmer with it.
    i want to avoid using too many parts.
    do you think i need c11 for every 555 or could i use one for all the 555s?
    ideally i could use 5v as the vcc supply and not 12v, but i guess i would need to change a lot of the resistors. right?

  14. Easy thing , look here you can use it with 5V
    555 timer can operate from 4.5 V. I suppose you have to use as many C11 as your 555 timer.
    I would highly recommend ,not to make pcb now because you may have to change something, you ‘d better place 2 pieces of 555 timer and 2 zero cross detection and check if everything is working fine
    When everything is fine, try to make the pcb with as many as 555 you nedd and use smd 1206 series components so that be small size. C11 220μF smd is really small

    1. thanks.
      this new schematic has only one npn transistor.
      i guess one is enough. why did you have 2 in series anyway?

  15. When i was trying to make this circuit, i was experiment with 12v. The waveform at the oscilloscope was clearest using two transistors ,i didn’t try it with one transistor. Maybe with 5v , one transistor is enough, i have test it only with 2 transistors, but i suppose it will work with only one.

    1. Hey Stephan that is wonderfull, i have never thing directly connect 230 volt on optocoupler because i would like to eliminate as much as i can high voltage.But i admit with this way you have take out especially the transformer and some more component. I like also the idea SJ5 to switch if you want external pwm or use 555 as voltage controller. Very nice.
      But at optocoupler OK2 you’d better to connect the pull down resistor at pin 5 and also trigger out pin 5. It is not bad but it is a npn transistor and out can be taken from collector. I may have free time to test it.

      1. @pantelis

        about OK2 pin5.
        the trigger line goes to the 555 and this signal i think needed to be low most of the time with peak towards 5 volts.
        if i put the trigger line on to pin 5 (like i did on OK1) then the wave signal will be most of the time high with short moments of low.

        i tired this and think it did not work to trigger the 555s correctly. i basically tired to repeat what you did with your 555 circuit.
        but i am open to hearing other experiences.
        so far everything works for me.
        i made a 20 channel dimmer 🙂

    2. Hi Stephen,

      Couple of question.

      1. Can we control 555 with arduino PWM.
      2. Do you have 555 dimmer design with less components as you were discussing.


  16. Dear Pantelis,

    I have constructed simple dimmer as follows on this page:

    I want to use it in the circuit for ac motor speed controll. I have programmed arduino in a way that led inside of the optocopler dims acording to the pwm output. But that of course does not work very well with motor, it pulses. What I wonder is, if it is possible to drive the power of ac motor acording to the pwm output (arbitrary from 0 to 255)? Would it help to change to the non-zerodetection optocoupler? Because as I understand, potentiometer for istance, works in that fashion. Or am I wrong?

    For instance, how would I change the potentiometer (470 kohm) for optocupler and control it with 0-255 pwm output. (

    I hope that is not overwhelming:)


    1. Hi Tomaz, that code in the page is just for blinking a led for example every 1 second. it is not going to operate the motor.i cannot understand why this page say control a motor with this way, it is not going to operate ever.You have to go at examples of arduino ide and find an example that with a potensiometer you can have pwm out. The use the schematic with optocoupler and connect the output of arduino to optocoupler

  17. Good work ! Especially the 555’s solution. It’s worth to remember that not only micro controllers can do the job !

      1. wrong approach of the point. i have no intent to be an inventor .I don’t care about prize. I made this because i need this and i present it here all collected. You may find other stuff here from other sites

        1. i agree with pantelis.
          sure the design has been mentioned in many other places. but for some reason a nice discussion thread started here about this circuit. this discussion and info sharing that really matters.
          thanks again pantelis.

  18. @pantelis happy new year to you too.
    now i will duration test the dimmer.

    one improvement for the future is to figure out how to use AC capacitors and choke coils to eliminate the very faint flicker and also make it not create interference for RF signals.

  19. i want a pwm time delay program for dimming circuit using microcontroller(msp-exp430g2) can u help me

  20. Thank you for sharing your project.
    I’m trying to build a AC dimming circuit, and your post is helping me a lot.
    One thing I could not figure out was the connection between the triac and moc3021.
    I’m looking at both the schematic and the picture of your breadboard, and I think pin1 should go to LOAD, pin2 goes to both moc3021 through 1k resistor and AC, and ground pin goes to another side of moc3021 according to the schematic. But, I think the circuit in the picture is different, so I’m a little bit confused.
    Can you help me out?

  21. Dear @pantelis ,
    Nice tutorial.
    We r also working on similar project.
    Only i have a confusion…
    What you have explained in top image is looks like to be PPM as u have shown a pulse
    whose position changes for achieving triac triggering angle
    But in entire discussion and in video you r showing which is PWM in which width of pulse varies.

    This is really confusing..

    Can you explain this?

    1. Hello . the first smal picture is about what happens on the bulb, AC phase change. The second big photo that looks like PWM is ,exactly in gate of the triac. At the start of time t1 is when gate of triac is triggered.
      Look here you may understand it better

  22. i dont know, if it a silly ques.. but how do u connect the circuit with arduino? i mean zero crossing out to which pin and dimmer signal in to which pin??

  23. Thank you for sharing .. How did you decide the triac which you used in project ? How many watts bulb did you use ? If you tried to control more than one bulb , is it necessary to change triac that you used ?

  24. as mentioned in an earlier post (
    i managed to get this dimmer to work with a 555 timer for the zero crossing.

    the dimmer works fine.
    i now tried connecting a 25m long cable between dimmer and AC LED light bulb (
    and the light starts to flicker.
    i am wondering why this does not happen with a shorter cable?
    would a snubber circuit light discussed here help (

    thanks for any input.

  25. that is strange. I suppose something is happening with that led bulb,that very expensive lamp. I am using around 5 meters cables for incandescence and fluorescence lamp and i have no flickering at all.
    If that problem was happening to me with incandescence lamp , i would increase the capacity of your circuit of C2. I start with 100μF and i had too much flickering . Maybe 470μF would be a good idea to test. Prefer one capacitor there , not two parallel

  26. you mean the C2 in this circuit:

    i will try that for sure.
    but how would the C2 capacitor value on the DC side have any influence on the AC side?
    What does a long cable introduce, more impedance?
    When i put a 500K resistor in parallel with the LED bulb the flicker is gone, same happens when putting a halogen bulb in parallel.

    but that’s just a hack.

  27. Yes i mean c2. The output of arduino (and the same i suppose your microcontroller) is PWM at 490 Hz. Of course this frequency is very high and we should not notice any flicker, but i don’t know why we notice flicker, it is sure mater of 555 timer ,maybe the architecture of it. That is why we add that capacitor there, we have to reduce the ripple of pwm.
    You can test this if it is easy for you , disconnect the input put of 555 timer (pin 5) with moc3021 and add on this pin the positive of a regulated power supply and the negative of power supply to negative of the circuit. Play with your power supply from 0-5 volt .You should not notice not even a little flicker.
    It is what i am doing here but from 0-10 volts

  28. ok will try that too; tomorrow.
    i hope you don’t mind but i kind of started using this thread to document my findings.

    for now i stopped using my dimming circuit and am doing my tests with the chauvet dmx-4.
    this means non of the 555 stuff applies here.

    this 25m cable with the LED bulb flickers, not matter if i keep the cable straight or coiled up:

    i did a silly test.
    i thought. if the long ac cable causes a flicker, what would haven if i made the cable run dc voltage.
    so, i broke one bulb apart and separated the base and the LEDs from one another with a 25m cable.
    the base has this circuit inside:

    i learned that a 25m run of 240VDC does not cause a flicker, and makes for a nice dimming curve.

  29. yes thats exactly, does not matter the length , it should be problem if you carrying too much current , but other problems , not flickering.
    I had repair a long time ago , some pcb leds that are placed on stairs in apartment houses.. After years, light is very low and flickering, the circuit is similar to yours and the problem was that capacitor. Mine, there was two capacitors. It is not the same problem as you, because i suppose your bulb are new, but i want to point out that this full Chinese way to light leds from 230V is ridiculous and danger!!!There must be a switching power suplly there for many reasons, not just i rectifier!

    1. i am slowly feeling the same way, that a lot of the problems i am seeing and might be seeing in the future are coming from the cap, res and rectifier inside the bulb.
      i am already starting to look at Triac Dimmable Offline LED Driver like the LM3450AEV230V30.
      it just means i can’t use these light bulbs and would have to make my own.

      i will report back ones i played around with C2.

      thanks again for all your input.

  30. i tried replaying C2 with a 1uf, 33uF, 100uF, 220uF, 470uF cap but all still produce a flicker when using the 25m cable.

    i found a hack that stops the flicker. i took 120K resistor and connected it in parallel with the LED light bulb. I guess it allows some currently to still leak and help close the TRIAC. ?

    I found a possible explanation for what i see:
    “At turn-on, an LED load presents relatively high impedance, so input current may not be sufficient to latch the TRIAC dimmer. In order to insure that IL is achieved, a bleeder circuit is typically added to the LED driver input stage. In the simplest form, the bleeder is a simple RC combination that insures a pulse of current when the input voltage is applied.”

    I am also reading that a trailing edge dimmer might work better for LEDs. It is also called a soft dimmer because it allows the current to slow rise.
    The TRIAC circuit use in this thread is a leading edge dimmer, which means (if i understand it correctly) when the TRIAC turns on a large voltage jump occurs which LED circuits might not like.

    1. As far as i can understand , using a smaller load ( your lamp is 9W ) for example 5W lamp there will be any flickering ,really interesting information.

  31. Thanks for sharing this Excellent article. Looking forward for more such posts.

  32. Hi there

    I have started on a project with the raspberry pi… I was wondering since the RPi is not good at keeping timing like the arduino… My solution idea is to use a 555 timer to keep the dimming but use PWM and the GPIO ports to control the 555 timer… Do you think this is possible and how would i link the pi or Arduino to the 555

    Kind regards

    1. Hmm, i don’t know how exactly you can do this rpi,i think it is very easy with arduino and i have not search for it with rpi

  33. I try to following your analog idea, may I ask why you choose 1uF cap for 555 timer?

  34. Hi, I have three questions to the software way, it might be stupid but I can’t find answers :

    1. Why there is 220V instead of 230V on input?
    2. “to a bridge rectifier that gives a double phased rectified signal ” Can you show me the plot of this signal, because I don’t know what does “double phased rectified signal” mean
    3. When you say “bridge rectifier” you mean this? :

      1. Thank you, I have more questions. How does “zero crossing detector” works? What happend if the Voltage is 0? What happend if Voltage is more than 0? Where current flows in this two cases (left side of 4N25) and what Voltage value is there? Why there is 10K resistor?

  35. I would like to build the circuit the analogue way. I have basic knowledge in electronics. Can someone explain to me what the control voltage being fed to R13 really is?

    1. Yes,in simple words , variable voltage at this pin, result dimming the lamp.
      When voltage at R13 is 0V dc, lamp light 100% and when voltage is 5Vdc, lamp light 0%.
      In this way changing the voltage with arduino help from 0-5Vdc, we can manage diiming a lamp that is connected to 230VAc.

    1. i cannot understand why channel dimmer costs 20$. All those components does not cost more than 5-7 $. This a good reason for DIY pcb !

  36. Can someone explain to me what that control voltage input really is. The one that is feeding R13.

    1. Yes,in simple words , variable voltage at this pin, result dimming the lamp.
      When voltage at R13 is 0V dc, lamp light 100% and when voltage is 5Vdc, lamp light 0%.
      In this way changing the voltage with arduino help from 0-5Vdc, we can manage diiming a lamp that is connected to 230VAc.

  37. Can we use this circuit for incandescent, fluorescent lamps, neon lamps, halogen lamps, LED? What should i do for 220 VAC ceiling fans?

  38. Hi. First of all, congratulations for your excellent work. I have tried to replicate your Arduino based circuit, two simple for loops in the main loop (one from 5 to 128, the other from 128 to 5). I keep getting an annoying flicker (the bulb is off for a very short, but noticeable time) when the bulb is at full brightness and start to decrease its intensity. Could you help me with my problem ? Thanks in advance.

      1. Thank you for your response. If I understand it right, the use of C11 also involves the use of a classic 555 timer besides the Arduino. Is there a way to get rid of the flicker by just adjusting the software or maybe adding a few discrete component ? I also don’t understand the main reason that causes the problem (maybe by understanding it, I could find a way to fix it). Thanks again.

        1. Mmmmm, stephan schulz above instead 555 timer, use moc3021 zero cross detection circuit, because he did not want to use transformer, resistors,capac,555…. but he had the same flickering as with 555 timer.
          I am not sure about it , but i suppose is obliged to the frequency of pin 9, on atmega 328p It is 490 Hz ,may at 980 Hz this problem is a little eliminated
          I have not search more about it, but i would also like to be notified about it ,if you find something new . You have a look at here for start!

  39. Thanks for sharing information about Ac dimmer ,
    i want to controlle the speed of fan with arduino ,so what Ic’s, i shoud use and what pins of arduino mega conect to pcb

  40. Can i make all the connections including the bridge rectifier part on a breadboard , before i implement it on a pcb?
    will a breadboard handle ac line voltage?

    1. I am sorry for this cunctation . Yes, you can do it but use a small incandescence lamp ,around 100w . But may bridge rectifier does not comi into breadboard ,because it has thick pins.
      But anyway you have to take measurements for high voltage!!

    1. Below the last photo ,there is BOM (bill of materials) . The most important are moc3021 and bt136, other are resistors,capacitors and semiconductors

  41. Im currently working on a project of controlling a treadmill motor. Is this possible with this circuitry? Whats the current rating of this circuit. And what triac should i use?

  42. I did it analog way.but didn’t work it.I want to no voltage controlling line positive voltage or negative.and what is the source of voltatage control.thank you

  43. I did analog way.but it doesn’t work. I want know how to give control voltage.this signal is negative or positive ifvwe give the control voltage another source what is the signai in and common. Thank you

  44. with inductive load why 2k7 resistor burn? have any solution ? please let me know.

  45. Thanks for the beautiful tutorial. There is one thing I don’t understand:
    First you show this code:
    void setup()
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(1, zero_crosss_int, RISING); // Choose the zero cross interrupt

    Then you say: “Interrupt 1 is pin 3 of arduino”

    But your final code states:
    void setup()
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(0, zero_crosss_int, RISING); // Choose the zero cross interrupt # from the table above

    So what pin do I have to connect the zero cross detection to? What pin of the Arduino is interrupt 0?

  46. Hi friend there is always a confusion about pins, look at this photo

    Interrupt 1 is pin 3 of arduino” i mean int1 is in pin 3

    1 or 0 are the names i have give, you can name pin 2 or 3 everything you want for easy understanding

    1. Thanks for your quick reply! That picture helps a lot! Do you know about the interrupts for the esp8266-01? I couldn’t find a similar picture for the esp 🙁

    1. It would be awesome if you built the same circuit using the esp! Imagine what you could do adding wifi to this circuit. You’ve then created your own domotics. Using a raspberry pi you can use them with apple homekit and dim your light using your iPhone!
      If you’d like some more info on how to connect the esp to homekit, let me know.

      1. I really appreciate your help! I ‘ll have you in my mind. Indeed there hundreds of thing that could be done to make our life easier ,that is exactly where “internet of thing” refer on. Hundreds of automation ,remote connectivity and bunch of things. I have in mind to test and present some very useful application of those thing in the future!

  47. Hi,excellent explanation and the circuit is working superb.
    But I want ‘0’ to be OFF and ‘128’ to be ON
    so what changes I should do in my code .
    Help appreciated

    1. No actually instead of using For loop I’m just giving direct value as “dimming=30” etc… and it works
      so now for this how should I change ‘0’ to be OFF and ‘128’ to be ON
      I even tried by changing high to low and low to high and in this condition the bulb will be full ON without dimming .
      Any more ideas?

    1. What hunch I’ve if I use this is the signal will be inverted but not in the exact way I want,it just inverts LOW to HIGH or vice-versa.
      And I’m not using the analog circuit,I’m using the first circuit with TRIAC & Optocoupler.
      Instead of inverting signals is there any other way to do this?
      because inverting is not a correct method to do (similarly as writing a program in a opposite way saying when you get a value of 100 (nearly OFF) as dim then the bulb should dim to the value of 30 (nearly ON) etc… )

  48. May be you can change the code adding a fuction if..then or case..break but there you need to try and error testing until find it

  49. hi,
    how to connect the 220V and Load connections to relay, so that i can operate and dim using relay
    need a quick reply

  50. There are few bugs in article, mostly why 75 value is used instead 78.

    For eg:
    “Wait a specific time between 0 and 9090 microseconds (9090=10.000-10)
    switch on yr TRIAC”

    10000-10 is 9990, not 9090

    1. Hi, you are not going to realise any difference even if you use 75μS or 78μS , we are talking for μS not even milli , so everything you use is exactly the same, using 75μS we give more time to triac for safety, which maybe is better. Also take into consideration frequency range, in Europe for example it can be 48,5-51,5 Hz, so nothing is so accurate

  51. Hi #Pantelis,
    According to your description of circuit and the attached images I’ve a doubt,
    comparing both the circuit image and the triac sensitive/nonsensitive image
    the load in circuit image has been connected to TRIAC pin 1 and pin 2 to AC Live
    and in triac sensitive image the load is connected to TRIAC pin 2 and pin 1 to AC Live
    so which one is correct ?
    and according to MOC3021 datasheet the load is shown to TRIAC pin2
    and another doubt is in your description you said if the gate is less sensitive then 2.4k should be changed to 1.2k but according to MOC3021 datasheet it is total reverse?

    can you please verify and correct me this doubts
    because I’m totally stuck at this where my 1.2k resistor is getting burnt
    Thanks in advance

  52. Hi,
    It’s a well described post and i like it
    but I’ve some doubts in the first circuit image and the second triac sensitive/non sensitive circuit image
    where in the first circuit image the LOAD is connected to MT1 and in TRIAC sensitive image the load is connected to MT2 of TRIAC .
    So what is the correct way of connecting the load ?
    and even in your description you said that if gate is less sensitive then 2.4k should be done to 1.2k but according to MOC3021 datasheet application circuits its totally reverse
    and even in that datasheet the load is connected to MT2 of TRIAC
    waiting for a quick reply
    because my project is being delayed as the resistor connected to MT2 is getting burnt
    please help me through this
    Thanks in advance

  53. Hi friend first of all what is your load, is it a lamp or motor??? If it is a motor and especially if it need enough amps to start you should better look for snubber circuit and generally adapt your circuit for motor

    I have made exaclty this circuit and it works

    If you have made this

    and R1 is very hot ,you maybe ned to replace moc3021. I had a quick search in internet and i saw someone use R1 2.4kohn 1W .
    I have never connect a motor to the circuit ,i have test it only with lamps, everything you do take care with 230V !

  54. Thanks for the reply
    My load is lamp
    and even I’ve snubber circuit too to run inductive load fan
    So you say that MOC3021 is damaged?
    what are the cases where MOC3021 can be damaged?

  55. The capacitor 0.2uF/0.1uF is this 275vac one or else a normal ceramic 0.2uF cap ?

  56. Thanks great article. Tried to make the restive load circuit using a micro controller but am getting this output on the scope using a Watt incandescent:

    Do you have any thoughts on what could be causing that offset?

      1. I think it is the zero cross detector too, I think that it cant accurately get a reference and due to that it has a starting delay offset, The code i am using is the same as you mentioned in the article above

        My zero cross detector works using a k814p with 2 x 1 watt 68k resistors to limit the input current to the leds. it starts off on low and when the zero crossing is received it goes high to 5volt. Does this sound correct?

        also great article, really explains about how the timing works to control the phase angle 🙂

  57. I am also using the BT137-600 Triac, Is this suitable for this application?

  58. Hi friend, BT137 is ok you don’t have any problem . I think problem is with k814p, it is for AC input and aplication is for phones and fax. Are you using full recifier bridge or you have remove it. k814p could be possible to make those strange things if you have use 4n25 or el817

    1. That is a good point, I will remove it and try use a 4n25 with a full bridge rectifier

        1. Hi Friend

          Using 4n28 with GBPC610, and 68k 1 watt resistor and i still get same problem of offset, i looked at datasheet and 4n28 is similar to 4n25, any idea on why the offset?

          1. indeed it should work properly with those components. As i see again your waveform I realise you have something reversely. Where are you measuring this waveform, also are you using internal pull up ?

          2. friend, i am measuring this waveform where the load connects to the triac gate. I am using a pull up resistor for the zero cross but have also tried it with a pulldown, but the result on the load is the same. i will take photos of the zero cross detector output

  59. friend, my output of my zerocross looks like this:, i am using 1n41n8 diodes with for my bridge rectifier and 2 x 1 watt 33k resistors. the resistors are placed before the bridge rectifier with one on the active and one of the neutral. i am using the same software you that you have mentioned above.

    to make sure that i am reading the scope properly, how did you measure the voltage waveform? as in how did you setup your scope to be safe to read AC signals, i wonder if my scope is referencing wrong. i use a PC oscilloscope and just touch the tip of the probe to the load pin.

    thanks for the help

  60. Uhhm i see,your zero cross detection signal is ok(I suppose you live in Europe & have 50 Hz), so your recrifier and 4n28 is ok. You have to look at moc3021 or triac, if you have replace them.You may also reverse gate and anode 1 with the output of moc3021.I am meausering at the gate of triac.
    Setting for my oscilloscope is for measuring ac voltage(I own Atten ads1102cal). I have no idea about pc oscilloscope.For safe measuring ac singal ,search in youtube writting <>
    where did you use 1n4148 ,this is no suppose to use for mains

  61. Hi thank you for the tutorial, … i”m trying to controle a lamp using the same circuit in the software way, … with Opto-triac MOC3020 and a triac BT136, …. and it is not working properly, is there any probleme with the choice of that triac, and what were the criteria of your choice,
    PS : i’m just trying a simple commande circuit with a push button before adding the zero-cross detection and the control part

    1. Hi, i choose bt136 because it can handle enough current. Without connecting moc3021 with this topology, just pushing the button is not going to work. Yoy cannot push the buton so quickly to simulate network herz,try it with moc3021

  62. Hi, thanks for the great explanation! I wanna ask, have you ever use MOC3041? It has a zero crossing detector in it. But, I’m not really sure how to use this opto-triac. Thankss

    1. Hi , that is great, i didn’t know about that , i already order it and waiting to test it, I wouldn’t like to start theory without handle it because it is connectied with mains but i suppose it shouldn’t be hard to use it

  63. Hi, Pantelis thanks for your good work. i also make this project using 555 timer but when i use a variable resistor on pin 5 to VCC its work fine. but when i want to use an arduino to control ta pin 5 voltage instead variable resistor its not working. can you tell me where is my mistake. i am connecting arduino pin 9 to pin 5 of 555. (i also check arduino pin 9 voltage output its ok).

  64. can’t it be done that way ?

    void setup() {

    // put your setup code here, to run once:


    void loop() {
    delay(5); // firing angle 90 degree for 50 Hz supply

    // put your main code here, to run repeatedly:


  65. Can i give 230v AC to bridge rectifier??
    Or do i need transformer to step down the voltage?

  66. My LED does not actually come on or fade with the AC lamp. I know its not critical but it’s still strange

  67. If i want to control two AC devices, could I just use the same circuit twice. Would it conflict in any way using one AC supply for the two devices and trying to fade or control the intensity of both

  68. I hope someone can answer me pleeeease. First. Thank you for making this. It will be so usefull in my apartment. I do my circuits in Tinkercad and I really cant find these elements.

    And the BRIDGE is also gone? But that i can “build” with Zeners

    Should I use another software in the future?

    Hope you can help a newbie!

  69. Hi friend, unfortunetly i don’t use Tinkercad and i don’t know anything about it, i have not any simulator ,i make my circuit on a breadboard, maybe someone else may help you

  70. Thank you for all this information it is really very helpful. I am using the Software way circuit. I used it to control AC Fan & Its working perfectly.

    But there is little problem,the 30k resistors connected before the rectifier are getting too hot. I used 33k 1/2W resistors. They are so hot that can not be touched.

    Any solution for this?

    1. Hi , that is due to the type of your rectifier .Those resistor is not so critical , so you can use 47Kohm or even 1 watt

  71. hello I tried above code it works perfect but when i simulate it in proteus software the pin 3 of arduino does not give output..
    i am new at programming .. whether I have to change in program or something else?
    really appreciate your work..

  72. i have see many bugs using simulators, i have never found an reliable arduino simulator, still searching. I don’t think you should change something in the code, i suppose there are many bugs in simulators, may i am wrong..

  73. Hi I do not understand where to fed the output of zero crossing, can you elaborate a bit more about that?
    thanks in advance!

  74. Hi ! Can you share a code which you have been tested on hardware or any other simulator.
    I am very confused.I have copied a code of Robert Twomey from your given link but it only cuts a positive half of sin wave. Moreover I am not a good at programming but unfortunately my project is based on programming. So it will be very helpful if you upload a tested code.

  75. Hi friend, unfortunately i don’t have any code at this time , it has been a long time i did this , but if i find free time i will make it and i will upload the complete code

  76. Hi !
    I wanna ask if i want to control the rms voltages across capacitor(capacitive load) , whether I have to use the same snubber or some other modifications?
    Thanks in Advance.

    1. hmm, i don’t know about capacitive load but i think you should add a more effective filter ,this snubber in the schematic is really poor, i will not even use this for inductive load. In my opinion filters generally is a must and you should use the best of.

  77. Very interesting article…
    The delay() in the Arduino code which controls the dimming period, can this be set up a higher time than the Zero Cross? Meaning that the amount of power will last longer and after the delay another loop will occur?

  78. Hi, interesting article… thanks for this.

    Quick question, the delay() on the Arduino code, would this work if the time is higher than the interrupt timer?

    I want to achieve a gradual increase of the power but at steps of 1min of times. Can this be done?


  79. Hi ,zero cross detection is standart. With int dimming = 128 you control when triac is going to fire up.
    I don’t understand “”if the time is higher than the interrupt timer?””
    But you can add gradual delay of 1 min step through dimming controlling triac delay

  80. Thanks, so in the loop() the delay would be 1000 and this would not cause conflict with the Interrupt?

    As far as I know the Interrupt has priority over any other functions, so each time there is a Zero Cross the program will trigger the Interrupt function and that will start again the loop function, starting the dimming from i=5.

    What I want is to keep the dimming effect let say for 5 min where it will be Extreme Bright and then reverse it. So the full cycle would be 10 min.

    Is that possible?

  81. Don’t complicate interupt fuction with delay. Interupt is there just only so that controller knows when hapent a zero cross detection. When a zero cross detection occurs ,then with loop fuction and delay we start dimming the light.
    You want 100% full light for 5 minutes and 0% off light for 5 minutes?
    Or the first 5 minutes dimming from 0-100% and the other 5 minutes from 100% dimming to 0%.
    Is that you want correct?

  82. Yes, that is the effect that I would like to get… 5 min from 0-100% and 5min from 100-0%.

    So coding the delay on the loop for (2350)millis, the dimming effect would stay on the “i” value and the interrupt would still work?

    I thought that the delay been longer than 10ms (time of 1/2 cycle 50HZ), this could cause that the interrupt function not to work and therefore the zero cross int.

  83. No , the delay a0 in the loop fuction does not affect zero cross detection
    the delay in loop fuction for i , is want you need. The interupt fuction should still work , dealy in loop fuction does not have to do with zero cross detection.
    So , you have to experiment with delay in loop fuction

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2024 Electronics | WordPress Theme: Annina Free by CrestaProject.