Dimmer-Arduino

Switching an AC load with an Arduino is rather simpel: either a mechanical Relay or a solid state relay with an optically isolated Triac.
It becomes a bit more tricky if one wants to dim a mains AC lamp with an arduino: just limiting the current through a Triac is not really possible due to the large power the triac then will need to dissipate, resulting in much heat and it is also not efficient from an energy use point of view.

The proper way to do it is through phase control: the Triac then is fully opened, but only during a part of the sinoid AC wave.
One could an Arduino just let open the Triac for a number of microseconds, but that has the problem that it is unpredictable during what part of the sinuswave 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 microcontroller) 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 after the zero crossing therefore gives a predictable level of dimming.

This circuit, it is an example.
0-crossing

Such a circuit 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 (see figure below), 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 steer an interrupt in the Arduino. sine Opening the Triac is done via a tried and tested opto coupled Triac circuit. For the Triac a TIC206 can be used, but a BT136 (a bit cheaper)  would probably work as well. With regard to the MOC3021: that has a forward voltage of about 1.2-1.4 Volts and a trigger current of 8-15 mA. An LED has a forward voltage of about 2Volts. Presuming a 5V digital steering signal that gives a resistor value between (5-3.2)/0.015= 120 Ohm and (5-3.2)/0.008=225 Ohm, making 220 a decent choice. In reality an LED may have a slightly lower forward voltage and a 330 Ohm resistor was found to work as well. It is important NOT to chose an opto-Triac here with an inbuilt zero-crossing filter. The MOC3041 or the MOC3061 for that matter therefore is not usable in this circuit. The MOC 3021 is. NOTE: some people have experienced flicker as a result of a weak input signal. As the voltage drop over an LED may vary depending on the color. it is safer to replace the LED by a wire bridge and increase the 220 Ohm resistor to 330 or 470 Ohm With regard to the 30k resistors: The 1/2 Watt should do it as the resistors dissipate some 400mW. If you think that is too much, replace the 4N25 with a 4N33 and the two 30k resistors with two 100 k resistors. Be careful though in using sensitive optocouplers as the 4N32 as they will easily be fully in saturation.

This is the complete circuit of the dimmer
dimmer2

All one needs then is the software. Fortunately someone already has done the basic work. I just adapted the code slightly:

/*
AC Voltage dimmer with Zero cross detection
Author: Charith Fernanado http://www.inmojo.com
License: Creative Commons Attribution Share-Alike 3.0 License. 

Attach the Zero cross pin of the module to Arduino External Interrupt pin
Select the correct Interrupt # from the below table:
(the Pin numbers are digital pins, NOT physical pins:
digital pin 2 [INT0]=physical pin 4 
and digital pin 3 [INT1]= physical pin 5)

 Pin    |  Interrrupt # | Arduino Platform
 ---------------------------------------
 2      |  0            |  All
 3      |  1            |  All
 18     |  5            |  Arduino Mega Only
 19     |  4            |  Arduino Mega Only
 20     |  3            |  Arduino Mega Only
 21     |  2            |  Arduino Mega Only

In the program pin 2 is chosen
 */

int AC_LOAD = 3;    // Output to Opto Triac pin
int dimming = 128;  // Dimming level (0-128)  0 = ON, 128 = OFF
/* Due to timing problems, the use of ‘0’ can sometimes make the circuit 
flicker. It is safer to use a value slightly higher than ‘0’
*/
void setup()
{
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
  attachInterrupt(0, zero_crosss_int, RISING);  
// Chooses '0' as interrupt for the zero-crossing
}
// 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

  // 10ms=10000us
  // (10000us - 10us) / 128 = 75 (Approx) For 60Hz =>65
  int dimtime = (75*dimming);    // For 60Hz =>65     
  delayMicroseconds(dimtime);    // Off cycle
  digitalWrite(AC_LOAD, HIGH);   // triac firing
  delayMicroseconds(10);         // triac On propogation delay
                                 //(for 60Hz use 8.33)
  digitalWrite(AC_LOAD, LOW);    // triac Off
}
void loop()  {
 for (int i=5; i <= 128; i++)
{
 dimming=i;
 delay(10);
 }
 }

Code below uses the timer function instead of delays
https://codebender.cc/embed/sketch:171819

dimmer

Print design. Mirrorred.
Dimmer-P1040650

On thing to consider is that if you are building a system in which you are not using a ready made PSU but rather supply your own PSU, built from a transformer and a rectifier, you could consider picking up the 0-crossing from the secundary side of the transformer.

A possible circuit could look like this:

voeding-big

E. ten Winkel

170 thoughts on “Dimmer-Arduino

  1. Swell. I will try this. I am on 115V/60Hz
    You supplied the values for 60Hz but what to do with the 30K resistors? Should I double them or half them and what about their Wattage? Sorry, I am not so skilled

  2. Greetings, I have a few (silly?) questions; I’m a math guy and (so) I know nothing about hardware; my brother built the thing and it seems to work but I don’t understand why; it’s probably because I don’t know how a triac works …

    From the code I can see that half a cycle takes up 10000us and the AC_LOAD pin is set to high for 10us somewhere during that cycle. My calculator shows that (10000us-10us)/128 == 78.046875us so wouldn’t 78us be a better approximation instead of 75us? Why is the (half) cycle divided in 128 parts? Wouldn’t 256 parts be more ‘natural’?

    My other question deals with the processor itself: during that delay can the processor still be interrupted or is it effectively dead during that delay period? (sorry about this, I couldn’t find an answer from Google anywhere).

    Thanks for reading and

    kind regards,

    Jos

    1. Josh, apologies for my late reply. 78 uS may indeed be a better approximation, I have just picked a nice number close by

      wether 256 would be a more natural process is up to taste, even at 256 the speed of the change is more important than the size of the step. For my purpose steps of 128th seemed satisfying but there is no problem in taking smaller steps

      Thanks for your interest:-)

  3. No problem; in the mean time I took a diferent approach: your solution makes the processor wait until it has to send a pulse to the triac; in my application that’s a waste of time so I used a timer that interrupts the processor again when the time has come to send a pulse. I divide the 0.01s in 256 parts and prescale the timer by /8; that makes 20.000 ticks per 0.01 second and works very well while my processor can do other things; near the ‘edge’ of the zero crossing interrupt I simply leave the triac on or off because the current is too low to properly switch it to an on state (this value differs per triac type).

    1. That is in fact a good solution Josh. The example I gave was just that, an example. I have given a solution with a timer interrupt as well

  4. Has anyone else had problems with flicker / bright flashes from the bulb?

    I can’t seem to get a smooth transition, even with the ‘dimtime’ variable set to a fixed value I get the same problems where I would expect a steady brightness.

    I will be heading to a local Hackspace on Sunday (where they have an oscilloscope) and will post what I find out.

    1. I’ve had a look at my 5V supply on a scope. The waveform is terrible. No wonder the arduino gets extra rising edges. Made my own 5V supply and it works great now.

  5. That is an interesting approach. My program was just a quick way to show the workings but if yoy set a timer interrupt that would be more efficient.
    Would you care to share your code?

  6. I found a great deal of trouble with “flicker/bright flashes” at around 40-50% power. The way to fix this is to ditch the diode bridge as a zero cross detector. Instead drop the mains through 150K resistance (I used 3*47K in series) to directly drive the opto input, but (most important) put a diode in parallel with the opto input, reverse direction. So now on the positive swing of the live, the opto input is “lit”, and on the negative swing the parallel diode catches the voltage on the opto input from going more than ~.7volts reverse polarity, and the opto input is “dark”. Now you get a nice square wave on the opto collector or “the zero crossing out”, changing 0-5v and 5v to 0v etc. exactly with mains zero crossing.

    In the code, change:
    attachInterrupt(0, zero_crosss_int, RISING);
    to:
    attachInterrupt(0, zero_crosss_int, CHANGE);

    Now “void zero_crosss_int()” is called each zero crossing as before.

    I used a 4n33 instead of a 4n25. You may need lower than 150K if you are using a 4n25. Also I found that I needed a 2.2K pull up resistor on the opto collector (10k is far to high) to get nice sharp rises and falls. Having nice sharp rises and falls on zero crossings is key to avoiding “flicker” or miss-firing.

    I adapted the code to run on interrupt using timer 1, rather than putting in a delay in the zero_crosss_int service code. Here is my arduino leonado test code:

    char inByte;// character for serial receive
    volatile int vary = 19980; //..this is OFF value..200 = 100 uS with an 8 prescale, 100*200 = 10ms… but int response takes 10us so use 19980
    int AC_LOAD = 3; // Output to Opto Triac pin
    int dimming = 10; // Dimming level (0-100) 0 = off, 100 = on
    void setup()
    {
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(1, zero_crosss_int, CHANGE);
    // Chooses change as interrupt for the zero-crossing
    Serial.begin(115200);
    }
    // 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 setting
    // initialize Timer1
    TCCR1A = 0; // set entire TCCR1A register to 0
    TCCR1B = 0; // same for TCCR1B
    // set compare match register to desired timer count:
    OCR1A = vary;
    // turn on CTC mode:
    TCCR1B |= (1 << WGM12);
    // Set CS11 bit for 8 prescaler:
    TCCR1B |= (1 << CS11);
    // enable timer compare interrupt:
    TIMSK1 |= (1 < 0) {
    // get incoming byte:
    inByte = Serial.read();
    switch (inByte) {
    case ‘h’:
    dimming = dimming+5;
    if (dimming > 95) {
    dimming = 95;
    }
    Serial.print(“level is “);
    Serial.println(dimming);
    break;
    case ‘l’:
    dimming = dimming-5;
    if (dimming<10) {
    dimming =10;
    }
    Serial.print("level is ");
    Serial.println(dimming);
    break;
    case 'p':
    Serial.print("vary is");
    Serial.println(vary);
    break;
    case '3':
    dimming = 30;
    break;
    case '6':
    dimming = 60;
    }
    }
    vary = 200*(100-dimming)-20;
    }

    1. Thanks for yr suggestion. I have not had the problem you describe, yet I will keep it in mind and perhaps it will be of use in the future. If you do not use a bridge obviously the frequency of the signal gets halved

  7. some of the code did not get posted right. a chunk was missing in the middle. Here is the code again:
    char inByte;// character for serial receive
    volatile int vary = 19980; //200 = 100 us with an 8 prescale, 100*200 = 10ms interrupt = off… but int response =10us so 19980
    int AC_LOAD = 3; // Output to Opto Triac pin
    int dimming = 10; // Dimming level (0-100) 0 = off, 100 = on
    void setup()
    {
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(1, zero_crosss_int, CHANGE);
    // Chooses ‘1’ as interrupt for the zero-crossing
    Serial.begin(115200);
    }
    // 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 setting
    // initialize Timer1
    TCCR1A = 0; // set entire TCCR1A register to 0
    TCCR1B = 0; // same for TCCR1B
    // set compare match register to desired timer count:
    OCR1A = vary;
    // turn on CTC mode:
    TCCR1B |= (1 << WGM12);
    // Set CS11 bit for 8 prescaler:
    TCCR1B |= (1 << CS11);
    // enable timer compare interrupt:
    TIMSK1 |= (1 < 0) {
    // get incoming byte:
    inByte = Serial.read();
    switch (inByte) {
    case ‘h’:
    dimming = dimming+5;
    if (dimming > 95) {
    dimming = 95;
    }
    Serial.print(“level is “);
    Serial.println(dimming);
    break;
    case ‘l’:
    dimming = dimming-5;
    if (dimming<10) {
    dimming =10;
    }
    Serial.print("level is ");
    Serial.println(dimming);
    break;
    case 'p':
    Serial.print("vary is");
    Serial.println(vary);
    break;
    case '3':
    dimming = 30;
    break;
    case '6':
    dimming = 60;
    }
    }
    vary = 200*(100-dimming)-20;
    }

  8. second attempt at pasting code also seems to have failed. this bit is missing:

    }
    ISR(TIMER1_COMPA_vect)
    {
    digitalWrite(AC_LOAD, HIGH); // triac firing
    delayMicroseconds(10); // triac On propogation delay
    digitalWrite(AC_LOAD, LOW); // triac Off
    TCCR1A = 0; // set entire TCCR1A register to 0
    TCCR1B = 0; // same for TCCR1B
    }

    void loop() {
    if (Serial.available() > 0)

    insert above exact after text TIMSK1 |= (1 < 0)

  9. message to the moderator…

    I see other bits of code removed from the paste, so even my correction of missing code also has some missing code. I don’t think this posting blog is good for code. There is text in the upload being filtered out and removed by the web app for some reason. Send me an email if you think others would be interested in the timer code.
    Jeff

  10. Trying to use a timer instead of the delayMicroseconds but it keeps blinking.. Code is like this:

    int AC_LOAD = 3;
    volatile int dimming = 128;

    void setup()
    {
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(0, zero_cross_int, RISING);
    }

    void loop()
    {
    }

    void zero_cross_int()
    {
    setTimer(65*dimming);
    }

    ISR(TIMER1_COMPA_vect)
    {
    digitalWrite(AC_LOAD, HIGH);
    delayMicroseconds(8.33);
    digitalWrite(AC_LOAD, LOW);
    }

    void setTimer(int microseconds)
    {
    TCCR1A = 0; // set entire TCCR1A register to 0
    TCCR1B = 0; // same for TCCR1B

    TCCR1B |= (1 << WGM12);

    TCCR1B |= (1 << CS11);
    TCCR1B &= ~(1 << CS10);
    TCCR1B &= ~(1 << CS12);

    OCR1A = microseconds*2 -1;

    TIMSK1 |= (1 << OCIE1A);
    }

    Anyone has any ideia?

    Thank you!

    1. blinking may also be causef if the LED on the entrance takes away too much of a voltage drop. Replace the LED by a wire bridge and increase the value of the 220 oghm resistor to 330 or 470 ohm

      1. Thank you very much for your grate job. This circuit is what i long search and this post solved my problem (blinking on low power)!

      1. Hi, if it is not available then I will try to build my own . Could you pls share the complete circuit schematic … ? .. I need to build in the india so my AC supply will be 230v 50Hz …

      2. Che than, the complete circuit is in this article. In case it wasn’t clear, you need the circuit that u will see immediately above the program listing that has the 4n25, the moc3021, the bridge rectifier and the triac. Ad you can see a rally simple circuit. You will find the print design below the program listing and a picture of the components on the PCB directly below that. The values given are for 230 volts. Don’t hesitate to ask if you need to know more

  11. Hi, I have built this circuite my own , and now I want to program my atmel 8051 controller to receive drive the Zero Crossing signal , Dimmer SIgnal in .
    What I understood from the above program is , Connect these points to the micro controller pins , and use the above program . Zero Crossing signal o/p work as a interrupt to the controller and based on this it drives the Dimmer Signal out put ..
    Can I use above program directly with only giving proper pin detail in the program ..

    1. Indra, yes you are understood wel. You need to connect the zero crossing to an interrupt of the microcontroller and then have an output pin of the microcontroler switch the optoisolator/triac

      However, the program in itself will probably not work immediately as it is not pure C, but if you have a C compiler for yr 8051 and you use this program, you will get pretty far, by adapting the pins and making the language a bit more pure C.

      If you get it to work, I would be very interested for you to share it:-)

      Thank you for yr comment

  12. Thanks for the info , below is the code which I have written , let me test and post the result
    #include

    /*=============================================================================
    =============================================================================*/
    int dimming = 0;

    void delayMicroseconds(unsigned int i)
    {
    unsigned int j,k;
    for(j=0;j<=i;j++)
    {

    for(k=0;k<256;k++);
    }

    }

    void delay(unsigned int i)
    {
    unsigned int j,k;
    for(j=0;j<=i;j++)
    {
    for(k=0;k65
    delayMicroseconds(dimtime); // Off cycle
    P1 =0x01; // triac firing
    delayMicroseconds(10); // triac On propogation delay
    //(for 60Hz use 8.33)
    P1 =0x00; // triac Off
    }

    void ex0_isr (void) interrupt 0
    {
    Drive_Trac(); // Increment the count
    }

    /*=============================================================================
    =============================================================================*/
    void main (void)
    {

    /*———————————————–
    Zero Crossing Interrupt
    ———————————————–*/
    IT0 = 1; // Configure Zero Configure interrupt 0 for falling edge on /INT0 (P3.2)
    EX0 = 1; // Enable EX0 Interrupt
    EA = 1; // Enable Global Interrupt Flag
    /*———————————————–
    for Triac drive P1.0
    ———————————————–*/
    P1 = 0x00;
    /*———————————————–
    Wait forever.
    ———————————————–*/
    int i;
    for (i=5; i <= 128; i++)
    {
    dimming=i;
    delay(10);
    }
    while (1)
    {
    }
    }

    /*=============================================================================
    =============================================================================*/

  13. Hi, I tried my board with the code which I have written for 89V51RD2 .
    I receive the interrupt from Zerocrossing output pin …

    But load(bulb) which I am connected is not varying, its always ON .. if I remove the triac input signal also my load is ON”.. Is TRIAC is fine ?

    Since I did not get 30k(1.2 watt resistor) , I connected 33k(1/2watt) all other or normal resistor ..

    here is my delay and ISR code

    void delayMicroseconds(unsigned int i)
    {

    for(j=0;j<=i;j++)
    {

    for(k=0;k<256;k++);
    }

    }

    void ex0_isr (void) interrupt 0
    {
    P0 =0x01; // triac firing
    delayMicroseconds(10); // triac On propogation delay

    P0 =0X00;// triac Off
    }

    1. Indra, if you disconnect the Input to the optocoupler and yr triac is still on then obviously either yr triac has had it or yr optoisolator has a short. If you have used an ic foot for yr optoisolator I suggest you take out the chip and try again by just applying mains voltage again. If yr triac is off, your optoisolator has a short. If it is still on I expect a faulty triac

  14. Hi, Since I did not put any ic foot ,, I will try to replace the IC and check …

    I have some small question :
    I need to support four dimming Level for my load(bulb) .

    will be changing the Delay between triac ON and OFF work well ?

    Like below .

    //for Level: 0
    P0 =0×01; // triac firing
    delayMicroseconds(8); // triac On propogation delay for Level: 0
    P0 =0X00;// triac Off

    //for Level: 1
    P0 =0×01; // triac firing
    delayMicroseconds(10); // triac On propogation delay for Level: 1
    P0 =0X00;// triac Off

    //for Level: 3
    P0 =0×01; // triac firing
    delayMicroseconds(12); // triac On propogation delay for Level: 2
    P0 =0X00;// triac Off

    1. Better to first desolder one pin of the resistor.
      With regard to the 4levels you have to set those by setting the delay from the zerocrossing

  15. Hi, the problem was with optoisolator , I desolder 1k resistor .. now its not ON .. So as you suggested I will try replace with new …

    With regard 4levls , I did not understand “delay from the zerocrossing” .. Is it adding some delay in the interrupt routine which I get for the zero crossing output ..

    as per my code below , whenever I get the interrupt , in the interrupt routine I do the traic firing and OFF ..
    So where to add the dealy in order to have diffrent dim levels
    void ex0_isr (void) interrupt 0
    {
    P0 =0×01; // triac firing
    delayMicroseconds(10); // triac On propogation delay

    P0 =0X00;// triac Off
    }

  16. Hi Indra, good that you pinpointed the problem. Still, make sure that there werent any accidental shorts between the tracks from the optoisolator.

    With delay from the zeorcrossing I mean that the lightlevel of the lamp depends on where in the 50Hz (or 60Hz) cycle you send a pulse to the TRIAC.
    In a 50Hz cycle the time between two zero crossings is 10ms (8.33 in a 60Hz).
    So if you want to have yr lamp at half power, you need to light the TRIAC halfway through that cycle, which is 5 ms after the zerocrossing.
    If you want yr lamp at 25% you need to wait for 7.5ms after the zerocrossing before you trigger the TRIAC and if you want yr lamp at 75% strength you need to trigger the TRIAC after 2.5ms.
    (technically for the 25% and 75% it is an approximation as the area under the curve is not liniair, but it is close enough for illustration of the proces).
    You have to add that delay in your program before you fire the TRIAC. Where exactly is up to you, but what I have done in my Arduino code is that in my interrup routine, I multiply a stepvariable with a steptime. Subsequently I set that variable in my main loop.
    Since you only want 4 levels, you could set a bigger steptime e.g. 2.4ms and multiply that with a variable.
    Subsequently in your main loop you set that variable to say 1, 2, 3 or 4

    You could also set the total dimtime directly in your mainloop as follows:

    interrupt {
    delayMicroseconds(dimtime);
    P0=0x01;
    delayMicroseconds(10); //propogationdelay
    P0=0x00;
    }

    loop {
    IF condition dimtime=9000;
    ELSE IF condition dimtime = 7000;
    ELSE IF condition dimtime= 5000;
    ELSE dimtime=2000;
    }

    I have chosen 9000 rather than (10000-10) because the 4N25 that gives the zerocrossing is not ideal in a sense that it does not give a very narrow pulse. For normal dimming purposes that is not a problem, but it could give some timing issues when you regulate close to the edges of your cycle

    Hope that helps, good luck. dont hesitate to ask if you need further help

  17. Hi,
    Thanks for the detailed information . I tried as you said above .

    instead of the dimming at constant level .. My load(bulb) blinks at the rate which I added as dimtime …

    this is my delay code .
    void delayMicroseconds(unsigned int i)
    {

    for(j=0;j<=i;j++)
    {

    for(k=0;k<256;k++);
    }

    }

    Isr(){
    delayMicroseconds(2000);
    P0=0×01;
    delayMicroseconds(10); //propogationdelay
    P0=0×00;
    }

    Pls help if I have made any mistake in the setting proper delay ..

    1. As I am on mobile I can only give a short reply but frankly I do not understand your code. If ‘i’ is yr dim time then what is the function of the 2000 and what is the function of ‘k’?
      I am not sure how yr delay is called by what yr function seems to do is to just repeat the 2000microsecond delay a number of times. If it is the zero crossing that calls this then maybe the first time us OK but after that there is no proper relationship with the zero crossing anymore and you even pass into the next phase of the cycle. Seems to me that j and k serve no function at all but that you need e.g. i * 2000 in which i is between 0 and 4.

      Just look at the example i gave in my reply of Oct 11, 2013 @ 09:24:11 That shows the structure you have to follow. Your code -sorry to say- is completely wrong, it will make yr TRIAC go haywire

  18. Hi , Thanks for your help .. I am able to get four DIM level ..
    But suddenly load flickers .. as you had suggested I replaced 220ohm with 440ohm and 1kohm , but still I get the flicker during normal operation ..
    Pls suggest how to overcome this flicker ..

    Since my 8051 out put pin was not able to drive the MOC3021 , I used a tip122 to drive .. as below

    And below is my complete code

    /* Dimmer.C – Keil MCBx51 Evaluation Board with 80C51 device*/

    #include
    #include

    int dimming = 0;
    unsigned int j,k;

    void delayMicro(unsigned int us)
    {
    for( ; us>0 ; us–)
    {
    _nop_(); // if crystal is 11Mhz crystal then delay is ~1 microsecond

    }
    }

    void delay(unsigned int i)
    {

    for(j=0;j<=i;j++)
    {
    for(j=0;j<=256;j++)
    {
    for(j=0;j<=256;j++)
    {
    }
    }
    }
    }
    void Drive_Trac(void)
    {

    delayMicro(dimming);
    //delayMicro(550);
    P0 =0xFF; // triac firing
    delayMicro(10);
    //(for 60Hz use 8.33)
    P0 =0x00;// triac Off
    }

    void ex0_isr (void) interrupt 0
    {
    //P1 = ~P1;
    Drive_Trac(); // Increment the count
    }
    /***********************************************************?
    void main (void)
    {
    //P1 =0x00; // triac firing
    P0 = 0x00;

    /*———————————————–
    Zero Crossing Interrupt
    ———————————————–*/
    IT0 = 1; // Configure Zero Configure interrupt 0 for falling edge on /INT0 (P3.2)
    EX0 = 1; // Enable EX0 Interrupt
    EA = 1; // Enable Global Interrupt Flag
    /*———————————————–
    for Triac drive P1.0
    ———————————————–*/

    /*———————————————–
    Wait forever.
    ———————————————–*/

    while (1)
    {
    dimming=380 ; //Level 1
    delay(9999);
    dimming=450 ; //Level 2
    delay(9999);
    dimming=500 ; //Level 3
    delay(9999);
    dimming=550 ; //Level 4
    delay(9999);

    }
    }

  19. Hi Indra, thanks for your code. But to be frank, it is hard to say. I keep giving an example that as far as I can see is just simple and should work
    Your routine Drive_Trac seems completely unnecessary as you can set that in your interrupt routne. Now you just have yr interrupt routine call another routine. Your delay function seems a bit too complicated and can be simplified. P0=0×FF ofcourse switches on the entire port but as I do not know on which pin yr TRIAC is, I left it as such, but I think you could write e.g. P0_1=1.
    I am not a C programmer but why dont you try something like this and see what happens:

    /*===================================================*/
    void ex0_isr (void) interrupt 0
    {
    int dimtime=(75*dimming);
    delayMicroseconds(dimtime);
    P0=0×FF;
    delayMicroseconds(10); //propogationdelay
    P0=0×00;
    }

    void msdelay(unsigned int value){
    unsigned int x,y;
    for(x=0;x<value;x++)
    for(y=0;y<1275;y++);
    }

    /*===================================================*/
    void main (void)
    {

    /*———————————————–
    Configure INT0 (external interrupt 0) to generate
    an interrupt on the falling-edge of /INT0 (P3.2).
    Enable the EX0 interrupt and then enable the
    global interrupt flag.
    ———————————————–*/
    IT0 = 1; // Configure interrupt 0 for falling edge on /INT0 (P3.2)
    EX0 = 1; // Enable EX0 Interrupt
    EA = 1; // Enable Global Interrupt Flag
    P0 = 0×00; //all pin of PORT0 declared as output
    /*———————————————–
    Wait forever.
    ———————————————–*/
    while (1)
    {
    for(i=5;i<128;i++){
    dimming=i;
    msdelay(10);
    }
    }

    /*===================================================*/

    In C programs you cannot be sure of delay, cause it depends on compiler how it optimize the loops as soon as you make changes in the options the delay changes.

    you will have to use Timers for making exact delays….. it does not matters whether u need delays of ms , us or even seconds…

    below is a function of 1 second using timers…..

    // the idea is to make a 50ms delay and run it 20 times as 20x50ms= 1000ms= 1sec

    delay_1s() // timer of 1 sec
    {
    int d;
    for(d=0;d<=20;d++)
    {
    TMOD=0×01;
    TL0=0xFD;
    TH0=0x04B;
    TR0=1; // start timer.
    while(TF0==0); // run until TF turns to 1
    TR0=0; // stop timer
    TF0=0; // reset the flag
    }
    }

  20. Hi , Thanks your patience and nice reply … I tried with oscilloscope to cross check the proper delay .. seems like I have set the proper delays

    Now I made a program , wherein User will send his desired level ( as a UART commands , like SET L1 , SET L2 etc ) .. Its working well .

    But still I have some problem , i.e when the load is at some level , it glows properly until it receives the next UART command from the user .
    During at some level the load suddenly blink ( sudden blink) to high level and come backs to same level .. This is not related to my delay or period , it happens irregularly, and quite often . Is it because of some problem in my circuit ? I tried changing all the resistor values but still this continues ..

    1. I dont think it is a fault in yr circuit, might be spikes on yr psu. If you share yr code with me I will check.
      Try and run the code that I gave you and see how it behaves

  21. Greetings and thanks for sharing. It is not clear to me how do you limit the current going into the Triac’s gate. It’s a 1K resistor (AC to to Moc3021), which would let approx I_G=215mA go through, i.e., -220+V_R+V_{TM}+T_{GT}=0, where R=V_R/I_G. Both the triacs you mentioned have a way lower gate sensitivity than this. Is it probably 10k? Thank you

  22. True, sometimes higher values of the resistor are used and I think I even have seen 68 or 100k. However, there isn’t 220 volt over the triac as it is in series with the load. That probably isn’t a very high resistance, depending on the wattage of the load so yr calculation comes close for those moments the triac is off. The 220 volt is however an average. If you want to switch on the triac close to the zero crossing, the numbers are different.
    Rather than to start calculating I have chosen an existing design that I know worked well. I am sure though that a higher resistance would also work. I may try once what the max resistance could be. If you find out I wld be happy to hear.

  23. What’s the max current load of this? I’m designing a control for a 5500W heating element at 240VAC (~23Amp max) that I want to be able to adjust from 0V (off) to full power (on) at 240VAC. The 240VAC PSU has two hot lines at 120VAC 180deg out of phase. Is this the sort of circuit that should be used for this application? Do I have to use different components with a higher current rating?

    1. Tony, I am not proficient enough in 2 or 3 phase ac to safely answer this question. In principle this circuit is suitable for any load as long as the right triac is chosen. You say you have 2 power lines 180 degrees out if phase but do I understand you also have a neutral feed thus 3 wires going to yr device? If it is only two lines then the only thing that counts is the fact there is a zero crossing. If you have 3 lines than it is a bit more complicated but yes in principle 2 circuits could be the answer however I do not have experience with that. Maybe someone else here can help

  24. Thank you for your reply. After some readings I have finally understood the criteria to choose the value of the resistor R_G. When placed in this circuit, it will have two main effects:
    a) It will limit/provide the current going into the gate of the triac (I_{GT})
    b) It will cause the voltage to drop when the triac is on (V_R)

    The lowest value this resistor may have (for 220 V AC) is R=220*sqrt(2)/I_{TMS}, where I_{TMS} is the maximum peak current allowed in the photocoupler’s phototriac. These are surge values, thus they are transient and account for a limit before breakdown. Therefore in your circuit R would be R=220*sqrt(2)/1=311.12 or 330 ohms, since the MOC3021’s I_{TMS}=1A. This is consistent with I_{GM} which is the peak gate current of the TIC206. In your schematic you use 1K which would limit the current to 311mA.

    This “surge” case may take place only when a pulse is received by the phototriac and it is able to conduct I_{GT}, and of course for a line value of 220*sqrt(2). Charge will then accumulate in the triac’s gate until V_{GT} gets build up and the the triac gets activated.

    In quadrant I, ( V_{GT} and A1 are more positive than A2) in order for sufficient charge to build up and V_{GT} in the main triac to bee reached, the voltage across the triac must equal V_R+V_{TM}+V_{GT}
    Of course V_R=I_{GT}*R . Commonly, V_{TM}+V_{GT} will both account for approximately 3V (datasheet). At the same time, the resistor must provide sufficient current to the Triac’s gate, let’s say a minimum of 25 mA (sensitivity of the Triac), thus

    V_{triac}= 330ohms*25mA+1.3V+1.1V=10.65V and
    V_{triac}= 1k-ohms*25mA+1.3V+1.1V=27.4V (the value in your circuit)

    Which is the voltage needed to activate the triac. Therefore, the smallest the resistor the less voltage is required to switch on the main triac. What goes on afterwards is mainly that there is a voltage drop across A1 and A2 and therefore the phototriac voltage and current will drop causing turn-off state (of the phototriac). The main triad will be kept switch on if the holding current I_H is respected. When the load current is below I_H, the main triac is switched off until a pulse from the photodiode is emitted again in order to polarize V_{GT} and build the required charge in the next cycle. Q1 and Q3 are the quadrants for this setup. Cheers.

    1. Thanks Mali for sorting it out and writing it down clearly. Seems to some extend you may have read the same articles at I did and that made me decide to just use a tried and rested design:-)

  25. Hi,

    I’m a rookie programmer/arduino user and I’ trying to set up this dimmer to let the light fade in/out. I got the dimmer itself set up properly and it works perfect with the code above.

    Now I’ve been trying some things myself in the code but I keep getting the ‘name lookup of ‘i’ error message when I verify it.

    I only adjusted the loop of the original code like so:

    void loop() {
    int x = 1;
    for (int i=5; i > -1; i = i + x){
    if (i == 128) x = -1;
    }
    {
    dimming=i; // Line of error message
    delay(10);
    }

    }

    I looked up the error code on the web and I found out it has something to do with a ‘i’ too much or something but I figured the ‘i’ for dimming is still the same as the ‘i’ in de for loop.

    1. This error means that u make multiple declarations of i. Where u do this is not immediately clear to me, but your code looks a bit odd. As it is not immediately clear to me what you are trying to achive it is hard to say what you need to change.
      The problem however is in the ‘;’ after your ‘x=-1’ in your if statement There should not be a semicolon after an if statement. There should be a semicolon after your action which is defined by brackets around it
      If x=-1 is the action following your condition, there should be brackets around it. Where your end bracket should go depends on what you are trying to achieve. However it seems you have just randomly added an if statement without taking the structure of the existing “For” loop into consideration.
      Not sure what you are trying to achieve, but removing the semicolon makes yr error go away, but then ofcourse asks for a semicolon. removing ‘x=-1;’ in total makes the code run error free.
      So, ergo you have structured your if condition wrong. An if condition should look like this:
      If (condition) {action;}
      Whereas you have:
      If (condition)something;
      existing action of FOR loop

    2. Your variable ‘i’ is declared in a for-loop, so it’s local to that loop, i.e. it can’t be used outside of that loop; the remedy is simple: declare it outside of the loop, e.g. where you declared ‘x’ is a fine place.

      kind regards,

      Jos

  26. Thanks a lot for sharing this! Also the comments and support you give are really helpful!
    @Mali, also thanks to you for your detailed explanation on the value of the resistor between the moc and triac:-) If I understand correctly, the smallest value is better as then the voltage accross the triac is lower and less power will be dissipated? Is there a chance that surge currents will break the R with lower value?

    I’ve seen similar circuits where they also add a resistor between the triac’s gate and T1. Does this sound familiar to anyone? Why would this be needed? I’ve been breaking my head about it, but the only reason I can think of is that it is only needed if the max gate current is lower than the moc’s max terminal current? So typically not needed then … Can anyone confirm?

    Now, I’ve built the circuit with R 330 and also a similar resistor between G and T1. I notice that when I connect the circuit to the mains, the lamp flashes and even my arduino picks up false interrupts (I use an additional interrupt to control the lamp with a switch). Anyone noticed this as well? How can I resolve this? The load is purely resistive (traditional light bulb).

    Looking forward to your response, many thanks again:-)

    1. Thanks bigWcharley
      Mali’s comment was useful indeed. I basically chose a value that is safe for the MOC and the gate.
      I know the circuit you refer to, I always presumed it was to get a tighter definition of the voltage on the gate coz it makes a voltage divider but I am not sure. Youe explanation could be true as well
      With regard to the flickering, does this only happen when you add that extra resistor?
      The flashing of the lamp maybe caused by your arduino picking up false interrupts, but why it picks up these (i.e. what is the reason) i dont know. Perhaps someone else kmnows

  27. Hello,
    I do not understand what have I done but it working.

    //—————————————
    int AC_LOAD = 3;
    int dimming=8;

    void setup()
    {
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(0, zero_crosss_int, RISING);

    pinMode(4, INPUT);
    digitalWrite(4, HIGH);

    pinMode(5, INPUT);
    digitalWrite(5, HIGH);
    }
    void zero_crosss_int()

    {
    int dimtime = (75*dimming); // For 60Hz =>65
    delayMicroseconds(dimtime); // Off cycle
    digitalWrite(AC_LOAD, HIGH); // triac firing
    delayMicroseconds(10); // triac On propogation delay //(for 60Hz use 8.33)
    digitalWrite(AC_LOAD, LOW); // triac Off

    }
    void loop() {

    if (digitalRead(4)==LOW) dimming++;
    delay(10);
    if (digitalRead(5)==LOW) dimming–;
    delay(10);

    if (dimming>120) dimming–;
    if (dimming<5) dimming++;

    }
    //——————————————————————

    Thanks for this project, kind regards

    1. Scott, I am a bit surprised as it seemed obvious from the circuit, but maybe i wasnt clear:
      resistor 10k, 1 k, 220R, 30k, 30k
      4n25
      MOC3021
      Bridge rectifier
      TRIAC such as TIC206
      LED red

      You could skip the LED and make the 220R resistor a 470 R

  28. Hello,

    Great post. I cannot make it work, tho. I believe my problem is with zero crossing detection (when I try to set +5V to triac manually the light lights up correctly, so circuit for this works just fine).

    For bridge rectifier I use DF08, is it proper BR for this? As you mentioned above I ofcourse use 4n25 together with two 30k resistors (230V 60 Hz).

    I tried to monitor serial port (it should display ‘1’ every interrupt), and I got result only when plugging 230V cable and unplugging it.

    Sorry for my newbie question but I’m really new to electronics.

    Thank you in advance for your reply,
    Tom

    1. The dfo8 should be ok but just dou le check the pin layout to see if it is compatible. Can u measure any output on the zero crissing pin?

      1. As I mentioned before when I try to measure output on zero crossing pin I get +5V only when I’m plugging and unplugging 230V, otherwise nothing happens. I checked all the pins bunch of times and all seems to be connected fine. As I can see you are using double phased rectification, DF08 is single phase, does it matter?

        Maybe I should post photo of circut on breadboard if it’s not too much for you?

      2. Yes go ahead and post that. The rectifier shld be a bridge. I had the impression the df08 is a bridge. Is it inserted the right way? Is the 4n25 inserted the right way?

  29. Yes, both 4n25 and df08 are inserted the right way. Could you tell me what BR you used in your case?

    Please send me an email, as I cannot put photos here, I will send them back in reply to your email.

      1. Yes, this is 100% 4n25. It has LTV 4N25 written on it, which I believe LTV is code of producer (Lite-On)

    1. I have a datasheet. Not sure what the single phased would refer to as it is a normal bridge rectifier. Anyway, your setup seems ok.
      Have you used the program exactly as I provided it (with INT 0)?
      What I like you to do is to measure the voltage on the primary side of the optocoupler.
      If your setup is OK, your software is ok, your wires are OK, then I start to think maybe your 4N25 is not OK.
      After you do the measurement can you exchange it for another one?

      1. Ok, I just measured what’s going on right after BR, and it gives no votage. That’s where the magic happens – as I mentioned before only when I plug in and plug out the 230V cable I get some results – when I tried it with multimeter I see that even when showing 0 when I plug in or out the cable multimeter shows -0.00V for a little while.

        In fact I don’t know how is it possible because I already changed DF08 two times. I changed it again and… It works. First one I just know I broken, second one seemed fine!

        I will finish the circuit for dimming and will let you know if everything works ok.

      2. ok seems you got the problem there. Odd, but I have had components come right from the shop and be faulty too. cant hurt to check

      3. Yes, it works fine now. I have other problem now, and it maybe something in the code. I’m trying different values for dimming:

        When I set int dimming to 25 it seems to have max power, when I set it to 23 it starts blinking badly. When I set dimming to 128 it has about 1/2 power, 149 is value that lights it just a little bit.

        It seems like proper values are between 25 and 149. Why is that?

      4. Do you mean the values for ‘i’ ??
        If so that would surprise me. The limit values are theoretically 0 and 128 because the software takes 128 steps of 75 microseconds (for 50Hz). That is an approximation as it could theoretically go to 133 (as 133.3*75 is 10000us =100Hz).
        Poland -where you are- has 50 Hz frequency so the 75 us in the software should be right. Are you sure you didnt by accident select 60Hz?? In that case at 128 your timing would be 8320 us and thus the lamp would not be fully off.
        If indeed you would have it at 60 Hz then at 149 your timing would be 9685, which is indeed almost off.

        If indeed you correctly selected 50Hz then a ‘dirty’ psu may cause havoc as that can couse extra spikes that trigger the interrupt.

        So first I would check yr software to see what timing you have selected.

  30. Hi Arduino, you wrote:

    On thing to consider is that if you are building a system in which you are not using a ready made PSU but rather supply your own PSU, built from a transformer and a rectifier, you could consider picking up the 0-crossing from the secundary side of the transformer.

    Is it possible connect to 4n25 and 1k directly to BR1 and thus to eliminate BR2?

    1. Dear Assaf, My answer is ‘it depends’
      You cannot pick up the signal directly from BR1 because there is a smoothing capacitor over the + and – lines and therefore there is just no zerocrossing signal left anymore.
      What you could do is to take it off BR1 and then use a diode before you feed the voltage to the smoothing capacitor.
      However, as the bridge is a cheap component, I decided to just keep it clean and separate the two signals completely.
      I trust that answers your question:-)

  31. I am interested in doing this project. I just got a Spark Core and it works great. So that’s my plan — to adapt this project for the Core. My question isn’t about that, though (not yet). Rather, it’s in regards to the wires to the dimmer. My current dimmer has two wires. One from mains and one to the load (and a ground). How would I be able to do this project (which looks like it needs two wires from the mains and two out)?

    1. Christoph i am not entirely sure what u are asking but I will try to reply. I am not sure if u are saying yr current dimmer has two wires + a third one or that it really has 3 wires, but it isn’t that important. In fact a dimmer can be seen as a device that is in series with the load. Therefore only two wires are enough: one coming from the mains and one going to the load. The load of course has another wire going back to the mains.
      The dimmer for the arduino is in fact the same but it has a circuit added to it to it to detect the zero crossing. That needs both wires from the mains. The load as u can see has one wire directly going to the mains but it is just connected via the PCB from this circuit. Does that clarify or do u need some more explanation. Don’t hesitate to ask

  32. Seems to me that the top half of the circuit would be redundant if you operated the triac’s PWM at >480hz, because you’d be averaging 8 or more samples of load on the sine wave – the beat pattern would be above the level of discernment of human persistence of vision.

  33. Hola me gustaría saber si tienes el programa en el caso si coloco un potenciómetro, para realizar la regulación de la ampolleta. Gracias

    1. /*
      AC Light Control

      Updated by Robert Twomey

      Changed zero-crossing detection to look for RISING edge rather
      than falling. (originally it was only chopping the negative half
      of the AC wave form).

      Also changed the dim_check() to turn on the Triac, leaving it on
      until the zero_cross_detect() turn's it off.

      Ryan McLaughlin

      The hardware consists of an Triac to act as an A/C switch and
      an opto-isolator to give us a zero-crossing reference.
      The software uses two interrupts to control dimming of the light.
      The first is a hardware interrupt to detect the zero-cross of
      the AC sine wave, the second is software based and always running
      at 1/128 of the AC wave speed. After the zero-cross is detected
      the function check to make sure the proper dimming level has been
      reached and the light is turned on mid-wave, only providing
      partial current and therefore dimming our AC load.

      Thanks to http://www.andrewkilpatrick.org/blog/?page_id=445
      and http://www.hoelscher-hi.de/hendrik/english/dimmer.htm

      */

      #include // Avaiable from http://www.arduino.cc/playground/Code/Timer1

      volatile int i=0; // Variable to use as a counter
      volatile boolean zero_cross=0; // Boolean to store a "switch" to tell us if we have crossed zero
      int AC_pin = 11; // Output to Opto Triac
      int POT_pin = A0; // Pot for testing the dimming
      int LED = 3; // LED for testing
      int dim = 0; // Dimming level (0-128) 0 = on, 128 = 0ff

      int freqStep = 65; // This is the delay-per-brightness step in microseconds.
      // It is calculated based on the frequency of your voltage supply (50Hz or 60Hz)
      // and the number of brightness steps you want.
      //
      // The only tricky part is that the chopper circuit chops the AC wave twice per
      // cycle, once on the positive half and once at the negative half. This meeans
      // the chopping happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply.

      // To calculate freqStep you divide the length of one full half-wave of the power
      // cycle (in microseconds) by the number of brightness steps.
      //
      // (1000000 uS / 120 Hz) / 128 brightness steps = 65 uS / brightness step
      //
      // 1000000 us / 120 Hz = 8333 uS, length of one half-wave.

      void setup() { // Begin setup
      pinMode(AC_pin, OUTPUT); // Set the Triac pin as output
      pinMode(LED, OUTPUT); // Set the LED pin as output
      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);
      // Use the TimerOne Library to attach an interrupt
      // to the function we use to check to see if it is
      // the right time to fire the triac. This function
      // will now run every freqStep in microseconds.
      }

      void zero_cross_detect() {
      zero_cross = true; // set the boolean to true to tell our dimming function that a zero cross has occured
      i=0;
      digitalWrite(AC_pin, LOW);
      }

      // Turn on the TRIAC at the appropriate time
      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
      }
      else {
      i++; // increment time step counter
      }
      }
      }

      void loop() {
      dim = analogRead(POT_pin) / 8; // read dimmer value from potentiometer
      analogWrite(LED, dim); // write dimmer value to the LED, for debugging
      }

  34. My circuit is working fine.
    I am using bta16-600b triac for dimming 200watt bulb. The supply voltage is 230v ac 50hz. The bulb only takes 0.800ma of current and still my triac get heated after 30minutes . What may be the cause? I am using the same code as on this forum.
    gate resistor = 330R.
    Driver moc3021
    controller attiny85
    regards.
    prb

    1. Pratik. It is hard to say if indeed there is something wrong. But let me check first if I understand u correct: did u really mean 0.800mA ? Or 0.800 A= 800mA?
      800mA is still a sizable current a d even if there would be say 20 volt over the triac that is still the equivalent of a small soldering iron. So maybe all is OK. Hard to say how hot ‘hot’ is. The temperature is also depending on the dimming level of the lamp. U may want to try see if there is a difference between full power and say 3/4 power

  35. Hi buddy,
    Thanks for reply.
    Extremely sorry its 0.800Amp i.e 800mA.
    Temperature of the triac falls between 50-60 C On -FULL-POWER.
    I am using attiny85 to control the triac which takes commands from master arduino using I2C protocol. I have prepared a very small circuit which gets fitted inside the switch board. So because of the temperature rising I am afraid that inside the switch board the temperature will increase more and it may damage the triac. To avoid the damage i used max amp triac bta16-600b. I thought the 0.800A current would be nothing to this triac.
    I examined that if i use moc3041 z-c-driver the triac stays warm45-50c. Is there any problem in the firing of triac.(in the above code).
    What if i added inductive load like ceiling -fan i am afraid my triac will get toasted😦. By the way i have added varistor in parallel with the triac.
    Was your triac also heating.

    1. Indeed 800mA is nothing for this triac. Yet if u do not cool it they can still get quite hot. I dont dare say if 50-60 degrees is u usual. That the 3041 keeps the triac cooler is indeed amazing. Is that when both are on full.power? Timing could be an issue. How big are the resistors u have used to cut the voltage towards the optocoplw therhe 4n25?

      1. Greetings,
        I am using 1 watt 1206 smd 120k resitor on parallel. That makes 2 watt and 60k.
        Today i simultaneously tested triac ckt with moc3041 and triac with moc3021. The moc3041 triac was little bit cooler than 3021. Load was 2 table fans. Firing timing of triac is making it warmer. But still it performs well. I have noticed one starnge behaviour in moc3021 triac ckt is that at dimming values 115-117 light bulb goes dim and high continously like a heart beat. My attiny is running on 8mhz internal oscillator and is calibrated by tinytuner.
        I am not good at timers. Can we use atattin85 timer 8bit tough for precisely firing the triac. Can you support with the code.
        Also can we detect the zerocrossing detection just by using one optocoupler like pc817 and a diode.
        Regards,
        Pratik bhagat

      2. I am wondering if the timer of the attiny is perhaps a bit off. As I am about to sleep I will reply more later

      3. Pratik. Just to make sure we are on the same page: the 3041 has an inbuilt zerocross detection and the 3021 has not. That is why in this circuit I use the 3021, because the user determines when the phase will be cut. The 3041 will always fire at the zerocross.
        For testing purposes ofcourse the 3041 cab be used but because it will always fully open the triac, it is not surprising that a triac with a 3021 will get a bit warnmer than with a 3041 because with the 3021 I expect you are in fact regulating the TRIAC and therefore it is actually ‘working harder’ while with the 3041 the Triac will be On or Off.
        When you are detecting the zerocross yourself -as in this circuit- the problem is that it is not an ideal detector: rather than giving a steep pulse exactly at the zerocrossing, it will be more like a slope that starts to rise slightly before the zerocross and is going down after the zerocross. The quality of this peak is to some extend defined by the value of the resistor in your AC line: if the value is too big, you get a wide peak if it is too low, your optocoupler will die.
        With regard to the timer: I presume you are using the internal oscillator. I take it that should be precise enough for timings in the micro and milisecond band. In order to use timer for firing, I presume you want to set a timer and then use the timer interrupt to fire the triac rather than to let the attiny wait via a delay. That is definitely possible check here for some arduino based code: http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/step6/Arduino-controlled-light-dimmer-The-software-III/ (it is my article on instructables).

        with regard to just using an optocoupler and a diode for the zerocross detection, well yes and no. I have seen people do this, but you will be missing a zerocross each cycle, it is better then to use an AC optocoupler like the LT814 or the IL251 or H11AA11, because they will react to the full sin wave.

        So to summarize… that your TRIAC is getting hot is difficult to assess for me as it is not uncommon. but I cannot assess that it is “too hot”
        With regard to your triac getting hotter with a moc 3021 than with a moc 3041 that is not beyond expectation.
        With regard to your lamp flickering at a level of 115-117 (does it behave normal at 120?) I would guess that that indeed is a timing problem and that at that level you seem to already be at the limit of your time period, rather than that happening at 128. If I am not clear, I will explain: on a 50Hz sinus you have 10ms to do all the dimming you want. The software divides that period in 128 steps, but ofcourse if the detection of your zerocross is a bit off it could well be that the new zerocrossing is there already, switching of yr triac, while you still try to have it fire. I would sooner expect this if the interrupt is set to detect the FALLING Flank rather than the rising (as I set in my program).

        But I have the following advice for you about what you could try:
        Try two things, pick the one that i seasiets for you first:
        – lower the value of your 60 k resistor, make it 40 k and see what that does to your situation
        – change the edge detection of your interrupt to FALLING FLANK and let me know if the effect you see at 115-117 now happens sooner (say at 110) or not
        Good luck

  36. Hey all.. i started here as i am having trouble with a soldering station project – Arduino Mega, with thermocouples on MAX6675’s, a PID and PIDautotune libraries, and TRIACs. I know i should put up the whole project somewhere (Fritzing, Arduino, here??? – tell me).
    It is rather ambitious, with 128×64 GLCD, soldering iron and hot air pistol. There’s independent temperature control for the two tools and also air flow control for the hot air pistol.
    All is actually remarkable good, allowing for a first run and the integration of all ^ those things… but the TRIAC’s fire only on some cycles of the mains.

    EL817 bipolar OC and bridge rectified ac transformer seconday as interrupt for zerocrossing – this starts a counter (the period of which is that of the last period, known, counted microseconds()… ) and that count is divided up and these division are counted again to determine when the respective triac is fired. The lcd is only updated (not redrawn, only written to) and the thermocouples only read once ever 250mS.

    Right up at the top of this thread it was mentioned that the code in the given example would take about 10000uS to execute – that’s the duration of a half cycle of mains at 50Hz. I think that is the route of my problem. Do you think?

    My arduino code is 27704bytes compiled… wouldn’t that mean it takes about … something uS to loop?, at 16MHz atmega clock. whatever some is, it’s too many to keep up with the mains? ¡!¡!¡! Doesn’t seem right .. 16MHz is lots quicker (a lot of ips) than 10mS half cycle.

    My thought is, if the loop takes 10mS to… loop, then that would dictate when (and if) a TRIAC is fired and not the requisite “counter” to dictate how long the TRIAC is “on” for.

    the sketch is here:
    http://www.eightyeight.co.uk/solderstation/solderstation_v209j_ino.html
    and the schematic is here:
    http://www.eightyeight.co.uk/solderstation/Solder%20Station%20v208k_schem.pdf

    pic of the ‘scope (oscillogram!!?) showing TRIAC fire pulse overlayed on the waveform accross the OC LED, drops near enough at each zerocrossing – this is all good. ISR call on falling edge, ‘scope trigger is on the source:

    there at 50% power, spikes are well placed at mid way along the half cycle – but not every half cycle has one! The fire pulses are irregular so far as i can tell, no discernible repeating pattern – certainly never twice the same relative waveforms on the ‘scope.

    And here, the 80% power, same, well placed “fires”, but missing some:

    The proximity to zerocrossing of the zerocrossing pulses (interrupts) isn’t what i need to resolve yet (if at all) – i need to find the missing spikes

    the schematic isn’t ideal as i’ve been tortured (only a litte bit, its good for being free) by fritzing – eg. the bridge rectifiers BR1 and BR3 are just “connectors” on the schematic- but the + is labelled (BR2 was in an earlier version for a dc motor air pump, gone). And the part numbers are missing or wrong. eg MOC3023 and TRIACs BT137. And R31, accross the LED in the zerocrossing optocoupler EL817is not fitted – but a 1nF cap is mounted as a single stage LPF (with the dropper R’s) for the 50Hz sine – there was a perfect ‘joggle’ at every zerocrossing where the sine would whip about and cause two zerocross firings. – so R31 is a 1nF capacitor – again fritzing parts limitations. That was added as a retrofit and isn’t the cause of the missing doodahs.

    After all this message, my question is only really – will the absence of (perhaps 50% of) the TRIAC fire-drive pulses be because the Atmega has been doing other stuff and missed them? Or any other ideas?

    If there’s more interest i’d gladly do more to publish this as a project…

    But wouldn’t be much good while not working!!

    -great tip for GLCD users: use a PC’s IDE harddisk (R.I.P.) IDC ribbon cable and use the Arduino Mega’s Digital Pins along the short edge. Only requires that the arduino config file is amended, that GLCD library config file is commented into the sketch, you have to nominate the D I/O pins (alternate pins so you get only one of the rows) but all’s good from there…. This may affect speed of the loop though too, as some of the pins will be in different ports meaning extra compiled lines I know.

    Greg

    1. Ambitious project Greg.
      With regard to the circuit… i find Fritzing horrible for circuit. I ise sPlan7
      With regard to your question, hard to say. It is difficult to get a full grasp of yr software, normally if it is an interrupt, a busy processor should be interrupted.
      I can also not imagine what the processor shld still be busy with in one cycle but not in the next cycle.
      If I look at yr scope images, you have included pics with early and with late phasecutting and the problem is similar which means it isnt a timing issue. As far as a few waveforms can show a pattern, the losing of trigger spikes seems to be fairly random.
      Now you have used a sinle phase rectification which may cause yr problem and also your code is not really optimal so your uP might indeed still be doing things while the next interrupt is already there.
      I’d say begin with using a double phase rectifier and see what that does and in the mean time try optimize yr code.

      1. many thanks for your thoughts, i think i am going to have to get my head round port manipulation to drive the output pins at their register bits (is that bitbanging them – or not quite?).

        double phase rectifier? not sure exaclty what you mean by that. Maybe my bridge rectifier (or their symbology) in Fritzing has misled you. The ac is full-wave (bridge) rectified, so the zc detector does get positive and negative going half cycles which are not differentiated. I think lost pulse are very random (from long timebase on ‘scope).

        I have just today cut the sketch right back to bare essential to test only the zc and triac firing of the code and run that – it did exactly the same thing – missing pulses. But, when i deleted the digitalWrite(triac_pin, LOW) from the loop() (where they followed the set HIGH 3uS earlier in the if.. tests) and moving that LOW write to the zc ISR, every pulse is fired properly… and is a really good square wave which always ends at the next zc and starts at the variable according to the set pot analogue input. I’ve only done it on one output pin – not all three at once. I might do the three. Also, I might send micros() to the serial monitor to see how many uS’s it get through – trouble with that: serial output is really really time consuming (even at 115200bps) in comparison so it greatly influences the loop time.

        I’m only using interrupts to mark the start of the half cycle and to simultaneously start counting 1/100’s of the half cycle. The triac firing is not interrupt driven- because so far i haven’t coded for the three triacs that each have different drive (power) requirements, would be three more timer overflow interrupts.

        i am going to re-code a lot i think.

        and i’ll try sPlan7, not met it before and i need it!

        Thanks again. I’ll post back here when there’s more.
        -i thought i might do a video of the sq. wave pulsestart moving along the ac half cycle sync’d perfectly with twiddling the pot – but arduinos driving the servos and leds and curtains on youtube in response to Wii nunchucks and .. well, anything . . . the price of fish.. are all much more impressive. tehe there’s a bloke uses arduino to feed his dog with a phone eh!!.

      2. thanks for the feedback,
        feeding my dog with an arduino and a phone… now there’s a thought. got to get myself a dog i guess

  37. Hola estoy tratando de ocupar el circuito dimmer con la librería , para la regulación de una ampolleta, pero ademas mi proyecto tiene un servo motor con la librería , el problema es que las dos librerías no me funcionan juntas.
    ¿Existirá alguna solución para que estas dos librerías funcionen juntas?

    1. Daniel,
      Thanks for your comment. Perhaps it could be that my Spanish is not fluent enough but I understand you mean that the library for the dimmer doesnt work with the library for the servomotor, and wether there will be a solution to make these work together.
      In fact… there is no library for the dimmer, so I presume you are just working with the library for the servo and that you have some problems getting that to work.

      Well, if your program is based on the example program I provided, please understand that that is just an example code. It is far from optimal as it spends much of its time in a waiting loop.
      The cycles of the Mains AC that this program is watching are 10mS each (from zerocrossing to zerocrossing). that goes for 50Hz, it is 8.3 mS for 60 Hz.
      If you let the light burn low, that means you have along ‘dimtime’ that time is ‘wasted’ by the program by making the processor wait til it is time to fire the TRIAC.
      Then there is not much time left to do other stuff.
      A better solution therefore is to not use this example program, well at least not ‘as is’, but use a timer interrupt to keep track of the ‘dimtime’
      You will find the basis of such a program here: http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/step6/Arduino-controlled-light-dimmer-The-software-III/

      1. Muchas gracias por responder. Al momento de colocar mi pregunta se cambiaron algunas cosas.
        Ocupo la librería TimerOne para la regulación de una ampolleta y ademas ocupo la librería Servo para el control del giro del motor, al compilar el programa en Arduino no arroja ningún error, el problema es que solo funciona uno de los dos, pero no los dos al mismo tiempo.
        Al parecer tienes razón sobre que las dos librerías usan TimerOne y por eso solo funciona una.
        Muchas gracias por sacarme de dudas.

  38. i use dimming code but output voltage wasn’t decrease but it up and down sometime decrease sometime increase what i choose to do

    help me please

    1. could you be a bit more specific? what output voltage are you talking about? The best measure is still to just attach a lamp and see if it dims.

      1. Hi Arduino, What component the T1 represents, on the AC side (3 leg transistor)
        could you please provide me with the list of components
        thanks in advance
        Jason (jas1903@live.com)

  39. Hi again, just found out T1 is (TIC206).. Another question though, can i control the brightness of AC bulb using (DC volt range from 0 v to 5 v0. I have got another arduino setup for this purpose and the output DC voltage is sensitive to darkness). I want to combine these two …
    thanks
    Jason

    1. One can control the brightness off an AC bulb with just about anything as long as you have the right circuit for it, or the right software. With this circuit you cannot directly control the brightness with an DC signal, but ofcourse you can read the dc signal with the arduino and then translate that to a value for ‘dimming’

    1. Jason, I am sorry but i do not really understand what you are asking. In this system nothing is sensitive to darkness or lightness as there is no sensor that picks up any lightlevel. The system has no idea if it is day or night

  40. Hi Arduino,
    I am building the design, but from the dimmer-print2.jpg and the orientation of the DF08 BR it looks like the minus of the BR is connected to pin 1 of the 4N25, which is the anode. Is that correct? In you schematics you connect the plus of the BR to the anode…

    Cheers,
    Emile

    1. The plus needs to be connected to the anode. I am on mobile right now so it is a bit difficult to check the print but that shld be correct

    2. Emiledem, in fact you are right, there is a mistake in the PCB. I have tried to modify the PCB to take out a zenerdiode that wasnt necessary. Obviously i made a mistake. That original PCB is here:

      pcb
      In fact, with the PCB that has the mistake, the signals between the bridge and the optocoupler need to be crossed.
      Thanks for pointing it out

      1. Hi arduino, I have made the circuit but it does not work. Two suspects are: 1 I connected the two ac terminals of the triac wrongly. (don’t know if that matters). or 2. My 4N25 optocoupler is broken. If I disconnect it, I measure 200 V DC with my simple multimeter, but if I connect it, I measure only 1 V DC. That seems very low. At the output pin of the 4N25 I measure 0.3 V DC. Since I don’t have a scope. Do you know what values I can expect here?

      2. It doesnt really matter that you changed the ac terminals, It seems your 4n25 mihjt be broken.
        Where exactly do you measure the 200 olt and the 1 volt? on te primary side? I can understand the 200Volt but the 1 Volt is a bit low but passable.
        0.33 Volt on the primary side is also a bit low, but remember that the output only becomes high every 1/100 th of the period, so what you are measuring is an average.
        There are a few things you need to do to test where the problem is:
        attach yr arduino, forget about the zerocrossing and write a high to the pin connected to the MOC3021 and see if the lamp lights.

        Then make a program with a simple interrupt routine that increases a counter and print that counter to the terminal. See if that works.

        That way you can test if the interrupt circuit is working and if the triaccircuit is working.

        let me know

    1. Starngely enough my earlier reply hasnt come true.
      yes you can use it for 3 loads.
      You need to keep 3 variables that are basically timings. from the zerocrossing you have to delay till yr shortest time and then fire the Triac. then you have to wait further till your next to shortest time and then fire that Triac. Then wait till yr longest delay is reached and fire the last triac.
      Realise though that yr processor will mostly be waiting. With one load you can use a timer but u do not have enough loads for 3 timers.
      What you could do is generate an interrupt say every 1ms and then increase a counter that u compare to the desired dimming values. At least yr processor will not be tied up all of the time

    1. nitin
      in itself that is not really a problem. Use the library of Ken Shirriff and attach an IR receiver.
      Then choose how you want tr program to react.
      There is a problem though as the reading of the receiver takes a bit of time and is interrupted by the zerocrossing interrupt. So you will need to cancel the interrupt when you are receiving the infrared code

  41. Yahoo, I got it working! It took me while to figure things out. Turns out that I had to use a scope to find the root cause. The voltages at the output of the 4N25 were present, and appeared at a 100Hz cycle (http://i.imgur.com/afX39uu.jpg), so that was OK. Also the microcontroller was working OK and the input to the MOC3021 was present at 100Hz (http://i.imgur.com/Ph5WFW9.jpg) But what was wrong then? The lamp flickered a lot and for a large part of the duty cycle it did not even light up at all. Only at the start and end of the duty cycle, I was able to make the lamp light up. Measuring with the scope showed that only one half wave of the sine was conducted, and then not every cycle, but very intermittent (http://i.imgur.com/aXqquf5.jpg).

    After a long way of debugging, adding a snubber circuit and trying to figure out what was wrong, I finally had the answer: I did connect the two terminals of the triac wrongly. In the datasheet it is noted that at least 5mA of current flow is needed to trigger the TIC206. This current flows from the gate to T1 terminal of the triac. The MOC3021 is responsible for connecting the gate to a current supply. At first I had the AC line connected to the T1 of the TIC206, but if the MOC3021 now connects the gate to T1, there cannot be any electrical potential difference (with respect to T1), and hence no triggering of the triac. After I swapped the AC line to the T2 terminal of the triac and the AC neutral (via the lamp!!) to T1, the circuit started working.

    Thanks for your tips on how to debug the circuit! I was really happy with the idea to count the interrupts. That way I was able to rule out the 4N25 as a possible cause, without even using a scope.

  42. Hi Arduino,
    Yahoo, I got it working! It took me while to figure things out. Turns out that I had to use a scope to find the root cause. The voltages at the output of the 4N25 were present, and appeared at a 100Hz cycle (http://i.imgur.com/afX39uu.jpg), so that was OK. Also the microcontroller was working OK and the input to the MOC3021 was present at 100Hz (http://i.imgur.com/Ph5WFW9.jpg) But what was wrong then? The lamp flickered a lot and for a large part of the duty cycle it did not even light up at all. Only at the start and end of the duty cycle, I was able to make the lamp light up. Measuring with the scope showed that only one half wave of the sine was conducted, and then not every cycle, but very intermittent (http://i.imgur.com/aXqquf5.jpg).

    After a long way of debugging, adding a snubber circuit and trying to figure out what was wrong, I finally had the answer: I did connect the two terminals of the triac wrongly. In the datasheet it is noted that at least 5mA of current flow is needed to trigger the TIC206. This current flows from the gate to T1 terminal of the triac. The MOC3021 is responsible for connecting the gate to a current supply. At first I had the AC line connected to the T1 of the TIC206, but if the MOC3021 now connects the gate to T1, there cannot be any electrical potential difference (with respect to T1), and hence no triggering of the triac. After I swapped the AC line to the T2 terminal of the triac and the AC neutral (via the lamp!!) to T1, the circuit started working.

    Thanks for your tips on how to debug the circuit! I was really happy with the idea to count the interrupts. That way I was able to rule out the 4N25 as a possible cause, without even using a scope.

  43. Hi Arduino,
    Yahoo, I got it working! Thanks for your tips on how to debug the circuit!

    BTW, I also submitted a comment earlier describing my troubles and the solution. Maybe that one ended up in spam?

  44. I don’t suppose anybody on this thread has converted the “C” code to PicBasic? I’ve read a lot of what’s contained in this discussion and it’s very good and very well presented. I’m not much of a “C” coder. Seeing the code in PicBasic would really be beneficial to me especially the zero crossing detection. I would like to use a GPIO pin of a PIC to detect the zero crossing instead of an external interrupt pin but either is fine. Any help you can give would be greatly appreciated!
    I know there a lot of “information” out there on the Web…but this is the best I’ve discovered thus far!

    Thanks!
    HJ

    1. I am not familiair with PIC basic but it shouldnt be to hard to write code for this circuit as all you need is an interrupt and a time function.
      I have written a program for a PIC in C though and you will find that here. As I have no PIC, I have not been able to test that program, so it may need some tweaks:

    1. What a coincidence, you are married to Oleg who sells them on ebay and you just happen to find his products there:-)
      Just be straight and say: if you don’t want to make these yourself for 3 USD, I will sell them to you for 30+5 USD S&H

  45. I found the previous two replies so funny😉
    completely unrelated: I use the original code from this blog article and changed it a bit: after a zero crossing interrupt a timer is started, so that in the near future the triac is pulsed. All works fine for a couple of years now; I don’t understand why this article still receives so many replies …

    kind regards,

    Jos

    1. Thanks Jos. In the article I did on instructables I have done a program that works with timer interrupt rather than delays as well. That in fact is a much better way as it frees up the processor to actually do other things.

      With regard to the previous comments:-) I really dont mind if someone posts a link to his own commercial product saying: “Hey, if you dont like soldering yourself, I sell these” but to pretend you accidentally stumbled upon them on ebay while it is yr husband making, selling and advertising them is kinda ridiculous. Same happened on instructables.
      I leave it up to anyone if they want to fork out 35 USD to buy or 3 USD to make

  46. My brother bought the hardware for me from a Chinese company and I doubt he even paid $3 for it ,,, I still don’t fully understand how triacs work; conceptually it’s ok but when i start reading about those four phases, I put on my silly face😉

    I follow your series of articles, just to see how you solve the problem of dimming those darn leds (I know conceptually about PWM signals, but for the gory details, it’s my silly face again …)

    kind regards,

    Jos

    1. :-) yeah I know that feeling. Someone explained the 4 quadrants once and then it made sense but the next day i thought ‘wait a minute, how was that again..’
      LED dimming with is actually quite simple: Leds are on/off for a specific time, but the change is too fast for our eyes: we just see the average brightness between ON and OFF.

      If you buy the hardware in China, it might be less than 1.50 USD. I just got it local and then it came to about 3 USD

  47. Dear Mr. ArduinoDI, i’d like to ask some question, i combined your program with fuzzy logic to control 1000watt heater, that has fuzzy output in range 0-128 (where 0 is 0v and 128 is 220), i use 2 microcontrollers, the master microcontroller is for fuzzy and the fuzzy output will be sent to the second microcontroller that willl control the AC Voltage to heater by using serial. here’s the program for the second microcontroller: i modified your program a bit.

    String inputString = “”;

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

    void setup()
    {
    Serial.begin(9600);

    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(0, zero_crosss_int, RISING);
    }

    void zero_crosss_int()
    {
    int dimtime = (75*dimming); // For 60Hz =>65
    delayMicroseconds(dimtime); // Off cycle
    digitalWrite(AC_LOAD, HIGH); // triac firing
    delayMicroseconds(10); // triac On propogation delay
    digitalWrite(AC_LOAD, LOW); // triac Off
    }

    void loop() {
    while(Serial.available() > 0)
    {
    int inputChar = Serial.read();
    if(isDigit(inputChar))
    {
    inputString += (char)inputChar;
    }
    if(inputChar == ‘#’)
    {
    int lang = inputString.toInt();
    lang=128-lang; //is used to reverse the value of dimming, so 128 will be on and 5 will be off
    if(lang<=5)
    {lang=5;}
    dimming=lang;
    inputString = "";
    delay(35);
    }
    }
    }

    the problem i have here is sometimes i saw the lamp flicker or flashing brightly (i tested it with a lamp before proceeding to use a real heater) and also sometimes the lamp was off for short priod of time. i don't know why. maybe you can give me a clear explanation for my problem. im looking forward to your reply. thank you so much.

  48. Dear Mr. ArduinoDI, i’d like to ask some question, i combined your program with fuzzy logic to control 1000watt heater, that has fuzzy output in range 0-128 (where 0 is 0v and 128 is 220), i use 2 microcontrollers, the master microcontroller is for fuzzy and the fuzzy output will be sent to the second microcontroller that willl control the AC Voltage to heater by using serial. here’s the program for the second microcontroller: i modified your program a bit.

    String inputString = “”;

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

    void setup()
    {
    Serial.begin(9600);

    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    attachInterrupt(0, zero_crosss_int, RISING);
    }

    void zero_crosss_int()
    {
    int dimtime = (75*dimming); // For 60Hz =>65
    delayMicroseconds(dimtime); // Off cycle
    digitalWrite(AC_LOAD, HIGH); // triac firing
    delayMicroseconds(10); // triac On propogation delay
    digitalWrite(AC_LOAD, LOW); // triac Off
    }

    //the data that is sent from serial is like 128#

    void loop() {
    while(Serial.available() > 0)
    {
    int inputChar = Serial.read();
    if(isDigit(inputChar))
    {
    inputString += (char)inputChar;
    }
    if(inputChar == ‘#’)
    {
    int lang = inputString.toInt();
    lang=128-lang; //is used to reverse the value of dimming, so 128 will be on and 5 will be off
    if(lang<=5)
    {lang=5;}
    dimming=lang;
    inputString = "";
    delay(35);
    }
    }
    }

    the problem i have here is sometimes i saw the lamp flicker or flashing brightly (i tested it with a lamp before proceeding to use a real heater) and also sometimes the lamp was off for short priod of time. i don't know why. maybe you can give me a clear explanation for my problem. im looking forward to your reply. thank you so much.

    1. It is hard to say what the cause is. I personally never experienced any flickering, but there are a couple of possibilities:
      The PSU: if you have a ‘dirty’ powerline there can be unexpected triggering.
      Another issue is the frequency of the powergrid. Where you are situated that is 50 Hz, so you have the right setting, but the value of 75 for the 128 steps is an approximation. in fact it should be 78.125. That means that at 128 steps you are only at 9600 usecs in your 10ms cycle so there is 400 us left in which the the TRIAC could ignite, making it sometimes hard to go all the way to zero. That is basically done as a bit of a safety because the frequency of the grid isnt always stable: if the period becomes a bit smaller, you may still be waiting to fire your Triac, while there is already a new zero crossing that resets the waiting period: So yr lamp will not light. Then when the frequncy settles again, it suddenly will light again.
      A third factor is that the zero crossing sync isnt exactly at the zerocross: it is a small pulse that starts before true zero cross and ands a short while after zerocross.
      As I have chosen for ‘RISING’ in my attach interrupt, the actual trigger can come slightly before the true zerocross. Usually that is not a problem, but if the resistors that connect the bridge rectifier to the grid have a high tolerance (i.e. value could be too high), the pulse around the true zerocross can become a bit wider that could affect the timing as well.

      So my advice is the following:
      Make sure you have a clean PSU, put a decent smoothing capacitor on it, or just to test… use a battery to feed yr circuit and see if that gives improvement.
      Change the “attachInterrupt(0, zero_crosss_int, RISING);” into attachInterrupt(0, zero_crosss_int, FALLING);¨
      see if that improves your situation.

      Also, if you want to use a purely resistive impedance heater then there is a possibility to do away with the entire zerocross stuff and use PWM (need a completely different circuit then). Look for “AC dimming with PWM and Arduino” in this blog.
      Pls let me know if it solved yr problems

      1. i guess the problem is on the resistor that connects to the diode bridge, i used 100k resistors instead of 30k. i guess there’s a difference when i changed them back to 30k, but the resistors became really hot, thats why i still used a high resistance resistor such as 100k.

      2. indeed. When the resistors are 100k the zero cross pulse becomes wide, i.e. starts earlier if you then would open the triac, it would be switched off a fraction later when the real zero crossing hits the triac.
        I have written a bit more extensively on this in Instructables, with the 33k resistors on 230 Volts the dissipated energy is about 700mW, which is 350 mW in each resistor. Yes that can make them a tadd warm.
        If you insist on using the 100k resistors you can choose to use a Falling Edge interrupt, but you will have a shorter interval to regulate. What you could also do is to either measure (with a scope) or try (by trial and error to find out how wide your pulse is, or in other words how long it occurs before the actual zerocrossing. If you know that, you can include that interval in your software. Suppose it comes 150us too early. Then whenever you catch an interrupt, you know you have to wait 100uS before you shld open yr triac to get full blast

  49. hello there
    A great approch by you…
    I am facing one problem
    here with wht 6th pin of MOC3021; u connect it with phase of the mains through 1K resistor…
    in my case it’s not working with this connection
    If I am connecting that pin with 5V external power supply; then it’s working proper
    so tell me the reason why it’s not working with phase and why it’s working with the external 5V…

    Thanks

  50. hello there
    according to your connection I connect 6th pin of MOC3021 with the phase of main through 1K resistor.
    It’s not working in that case.
    If I connect that pin with the external 5V DC power supply then it’s working fine for me…
    Tell me the reason in that both case..
    Thank you

    1. That is hard to say. For one thing, I am not sure how yr complete connection is now. Are you telling me you connected the + of an external 5Volt source directly to pin 6? Where did you connect the ground? I hope and prey not to your Arduino ground because you have created a life threathening situation then.

      Nevertheless, the problem seems fairly simple: if your triac gets triggered by an external power source, but not via its connection with the 1k resistor, then there must be something wrong with that connection. Please UNPLUG your circuit from the mains and check if yr 1 k resistor is OK (put a multimeter over it) and see if the connections are well soldered to the right points.
      If you are sure it is, then try the circuit again. If it isnt working, measure the voltage over pin 4 and 6 and over the resistor: please be very carefull while measuring on a live circuit connecte to the mains

  51. hello mr. i have tried to apply your circuit to 220v lamp and it works really great. but when i change the load into 1000w heater 5A, the triac became really hot. is it normal? or should i add more components to the circuit? please help me
    thank you so much

    1. Well obviously when using 1000W there is a load more dissipation than when using 220 Watt, but the ‘problem’ is also in the fact that a heater has coils and therefore might not be a pure ‘ohm’ impedance, which increases the dissipation as voltage and current might be a bit out of phase.
      As long as you have a TRIAC that can sustain the dissipation and a good heatsink you are probably OK, but in reality this circuit is mainly aimed at dimming lamps.

  52. Hello .. Nice tutorial .. There .. I first found it on Instructable ..
    I want to have multiple outlets controlled separately .. By that I mean:
    My current setup is this:
    > I have made 3 such circuits ..
    > Connected each of the zero-detection pins to separate interrupt pins on Arduino Leonardo (Pin 7 -> int pin 4, Pin 2 -> int pin 1, Pin3 -> int pin 0 ).
    > Attached output pins from Arduino to three Digital Pins(10, 6 and 8)
    > All the 3 AC inputs for the lamps are from same source.
    > Wrote 3 different Service Routines.
    > Trying to address them individually by a variable, separate for separate routines (int dimming).
    > My intent persay is to have one blinking, one dimming ete. at the same time ..
    > How do I achieve this?

    > this is my test code:
    http://pastebin.com/raw/PmmWkwQf

    In case you miss it:
    int AC_LOAD = 10; // Output to Opto Triac pin
    int AC_LOAD_TWO = 6;
    int AC_LOAD_THREE = 8;

    int dimming = 128; // Dimming level (0-128) 0 = ON, 128 = OFF
    int dimmingTwo = 128;
    int dimmingThree = 128;

    void setup()
    {
    pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
    pinMode(AC_LOAD_TWO, OUTPUT);// Set AC Load pin as output
    pinMode(AC_LOAD_THREE, OUTPUT);// Set AC Load pin as output

    attachInterrupt(4, zero_crosss_int, RISING); // Choose the zero cross interrupt # from the table above
    attachInterrupt(1, zero_crosss_int_two, RISING); // Choose the zero cross interrupt # from the table above
    attachInterrupt(0, zero_crosss_int_three, 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
    {
    int dimtime = (75 / 3 * dimming); // For 60Hz =>65
    delayMicroseconds(dimtime); // Wait till firing the TRIAC
    digitalWrite(AC_LOAD, HIGH); // Fire the TRIAC
    delayMicroseconds(10 / 3); // 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 zero_crosss_int_two() //function to be fired at the zero crossing to dim the light
    {
    int dimtimeTwo = (75 / 3 * dimmingTwo); // For 60Hz =>65
    delayMicroseconds(dimtimeTwo); // Wait till firing the TRIAC
    digitalWrite(AC_LOAD_TWO, HIGH); // Fire the TRIAC
    delayMicroseconds(10 / 3); // triac On propogation delay (for 60Hz use 8.33)
    digitalWrite(AC_LOAD_TWO, LOW); // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
    }

    void zero_crosss_int_three() //function to be fired at the zero crossing to dim the light
    {
    int dimtimeThree = (75 / 3 * dimmingThree); // For 60Hz =>65
    delayMicroseconds(dimtimeThree); // Wait till firing the TRIAC
    digitalWrite(AC_LOAD_THREE, HIGH); // Fire the TRIAC
    delayMicroseconds(10 / 3); // triac On propogation delay (for 60Hz use 8.33)
    digitalWrite(AC_LOAD_THREE, LOW); // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
    }

    void loop() {
    //dimming = 30;
    blinky();
    dimmingTwo = 0;
    dimmingThree = 0;
    //delay(10);
    }

    /*code for delay less blinky*/
    long previousMicros = 0; // will store last time LED was updated
    long interval = 100000; // interval at which to blink (milliseconds)

    void blinky() {

    unsigned long currentMicros = micros();

    if (currentMicros – previousMicros > interval) {
    // save the last time you blinked the LED
    previousMicros = currentMicros;

    // if the 1st AC LOAD is off turn it on and vice-versa:
    if (dimming == 0)
    dimming = 128;
    else
    dimming = 0;
    }
    }

    1. I am happy you found my circuit of use. You may have noticed I also published a three channel version of this board:-)
      I think you have made it a bit complicated for yourself by using 3 zero crossing interrupts: you only need one as the the zero is crossed at the same time.
      Once you have that point in time, you need to keep track of 3 counters (either hard- or software) that trigger the TRIAC at the right time. If you want to blink, you need a setting for the intensity as well as for when to blink.
      With regard to the latter, the blinking, you have done that in yr code with the known currentMicros-previousMicros structure, but in fact you can do that for the 3 intensities as well.
      Obviously you cannot use delayMicroseconds(dimtime) as during that time your arduino is just waiting
      So I would set up 3 different currentMicros cycles, one for each channel and then add one for the blinking. The blinking ofcourse can also be done by a counter in yr ISR, which is increased every 1/100 sec (10ms), but for longer times that might become a high number.
      So in principle you would have something like this:
      starttime=micros();// at zerocross
      if micros()-starttime>=interval1 =>fire TRIAC1
      if micros()-starttime>=interval2 =>fire TRIAC2
      if micros()-starttime>=interval3 =>fire TRIAC3
      Now ofcourse thisis stil a bit rough, you still have to Switch off the Triac and then make sure they don’t fire again during the cycle (although that would not be such a big problem)

      I see a mistake in yr program though as your interval is stated to be 100000ms (=100 sec). However, you use it to compare microsecs, so in fact it becomes 0.1 sec).

      I trust that helps you on your way. If not, let me know.

      1. Hello I persuaded in the direction you guided me to and this is completely fresh code I wrote today: http://pastebin.com/raw/PCgA3Raq .. This is for parallelBlinking.
        2 Loads blink at one speed and 2 other loads blink at another speed.

        It works

        Now I want to set a fade in and out to the 5th Load I have, while others are blinking at their prescribed separate speeds.

        How do I achieve it ..

        I saw using timers but if you can edit the code a bit and show me(explain me) how to add the fading part, It would be really helpful.

      2. Ofcourse it works, I advised it:-)
        Jokes aside though, I am happy you were succesful.
        Fading the 5th lamp isnt really hard.
        presumed you have a variable -called ‘interval5’- that determines the firing of the 5th TRIAC.
        You have set up an if statement again -similar to the ones you have done before- that checks if the interval has passed.
        and then in the loop you put a for loop that increases or decraeses that interval.
        so in the ISR you have:
        if micros()-starttime>=interval5 =>fire TRIAC5

        and in the loop:
        for (byte x=0;x<=;++x)
        {
        interval5=x;
        }
        or something similar/
        The '10' is just a randomvalue i picked. It depends on the number of steps you want to take, considering you should not have an interval that is larger than 10ms/8.3ms (for 50 resp 60Hz).
        ofcourse instead of such a straightforward linear fade, you may also decide to give it a more natural fade, kinda like a breathing light by giving it a sinusidal increase/decrease. Google 'arduino breathing light' to get the function for that

  53. Okay I think it’s a bit complicated:

    First of all I’m trying to avoid use of any delays() and not writing anything in ISR.

    I’m using a boolean flag to check for the ZeroSignal reception. As soon as it is received, the flag turns true and then in loop all the triac triggering initiates.

    There I use the micros() function to avoid delay and it was easy to set diff interval of delays between different LOADS.

    Now for fading effect what you did in ISR was this:
    int dimtime = (75*dimming); // For 60Hz =>65
    delayMicroseconds(dimtime); // Off cycle
    digitalWrite(AC_LOAD, HIGH); // triac firing
    delayMicroseconds(10); // triac On propogation delay.(for 60Hz use 8.33)
    digitalWrite(AC_LOAD, LOW); // triac Off

    then you changed the dimming in loop like this:
    for(int i = 0; i= 8.33))
    {
    // fixed interval

    loadStateThree = LOW; // Turn it off
    previousMicrosThree = currentMicrosThree; // Remember the time
    digitalWrite(AC_LOAD_FIVE, loadStateThree);
    }
    else if ((loadStateThree == LOW) && (currentMicrosThree – previousMicrosThree >= 65 * intvvv))
    {
    // variable interval
    loadStateThree = HIGH; // turn it on
    previousMicrosThree = currentMicrosThree; // Remember the time
    digitalWrite(AC_LOAD_FIVE, loadStateThree);
    }

    }

    and then in loop(along with the blinking functions):
    if (allowChanges) { //after detecting zero crossing.
    blinkyOne(interval);
    blinkyTwo(intervalTwo);
    fading(intervalThree); //**
    allowChanges = false;
    }
    and out of this “if” statement I’m running for loop continuously to change the variable “intervalThree”.

    Here’s the complete code for you to check: http://pastebin.com/raw/C12FNGFu
    Note: Also there is another fading function below in the code commented for you to check.

    So when I run the above sketch, The LOAD remains static at some intensity and doesn’t fade out.
    What am I doing wrong? If you can edit the code and see. Also if I do not remove the delay() from within for() loop, every thing just blinks at a constant speed .

  54. Forgive for the previous comment :: It seems some line were missing: here’s the better situation explanation and question:

    Okay I think it’s a bit complicated:

    First of all I’m trying to avoid use of any delays() and not writing anything in ISR.

    I’m using a boolean flag to check for the ZeroSignal reception. As soon as it is received, the flag turns true and then in loop all the triac triggering initiates.

    There I use the micros() function to avoid delay and it was easy to set diff interval of delays between different LOADS.

    Now for fading effect what you did in ISR was this:
    int dimtime = (75*dimming); // For 60Hz =>65
    delayMicroseconds(dimtime); // Off cycle
    digitalWrite(AC_LOAD, HIGH); // triac firing
    delayMicroseconds(10); // triac On propogation delay.(for 60Hz use 8.33)
    digitalWrite(AC_LOAD, LOW); // triac Off

    then you changed the dimming in loop like this:
    for(int i = 0; i= 8.33))
    {
    // fixed interval

    loadStateThree = LOW; // Turn it off
    previousMicrosThree = currentMicrosThree; // Remember the time
    digitalWrite(AC_LOAD_FIVE, loadStateThree);
    }
    else if ((loadStateThree == LOW) && (currentMicrosThree – previousMicrosThree >= 65 * intvvv))
    {
    // variable interval
    loadStateThree = HIGH; // turn it on
    previousMicrosThree = currentMicrosThree; // Remember the time
    digitalWrite(AC_LOAD_FIVE, loadStateThree);
    }

    }

    and then in loop(along with the blinking functions):
    if (allowChanges) { //after detecting zero crossing.
    blinkyOne(interval);
    blinkyTwo(intervalTwo);
    fading(intervalThree); //**
    allowChanges = false;
    }
    and out of this “if” statement I’m running for loop continuously to change the variable “intervalThree”.

    Here’s the complete code for you to check.

    Note: Also there is another fading function below in the code commented for you to check.

    So when I run the above sketch, The LOAD remains static at some intensity and doesn’t fade out.
    What am I doing wrong? If you can edit the code and see. Also if I do not remove the delay() from within for() loop, every thing just blinks at a constant speed .

    1. I presume the code u want me to check is linked in the comment u wanted me to disregard.
      with regard to using delay in an ISR, the simple code i give in the article is just a demo, I would advise a timer, but I thought were alrwady past that with u checking for intervals, so maybe I do not see yr point there.
      Anyway, I will check your code, but just a question, you do a check in your fading code to see if the cycle already has past. Why is that? Also, what exactly do you man with ‘the load remains static at som eintensity” Do you mean that it fades a bit and then no longer? At what intensity is that? One thing i can say already is that your code doesnt have a delay in the for loop so most likely the intensity level will already have changed unpredictable before it gets to the new trigger period. putting in a delay you say causes ‘everything to blink’ you say.What is ‘everything’? is that just the one lamp or all the lamps?

  55. Hello. Nice tutorial!
    I have an idea of making arduino controlled dimmer for my home lighting. But there is a small problem… All neutral wiring goes directly to light source, so I have only two wires in switch place: one coming from mains and another going to light source.
    I am using Krida electronics dimmer controller (http://www.inmojo.com/store/krida-electronics/item/ac-led-bulb-dimmer-controller-arduino/) which, I suggest, is simmilar to yours.
    There is scheme which I tried to connect: http://we.tl/DCx2W2BGF4 , but (no surprise) it didn’t work properly. On max power I have only about 50% of light intensity and noticable blinking.
    I’ve used similar code as yours.
    If you have any suggestions, please let me know.
    Thank you in advance!

    1. yes i know the inmojo dimmer, basically there isnt much difference between all these dimmers:-)
      I found the connection diagram you linked to a bit hard to comprehend but is its quite clear it is wrong. Not sure whi drew it but it predictably wont work.

      But let me first make sure I understand the wiring you have, as i understand you want to dim an existing lamp right?
      you have two wires going to yr switch, one that carries electricity to one side of yr switch (in EU that wire would be brow) and one wire from the other end of yr switch than needs to carry the electricity from the swith to the lamp. In EU this wld be a black wire.

      The neutral (in EU a blue wire) is not in yr switchbox, but goes to the lamp seperately.

      Though with a regular triac dimmer that would not be a problem, with the dimmer u are using it is because that needs both mains wires (live and neutral) to get the zero cross signal. The ‘solution’ you send is based on the idea that the neutral is connected to the other side of the lamp and therefore also coming to the return wire from the lamp to the switch. That is true but the problem is that the lamp is in between so at a certain moment the circuit will just not receive a decent zerocrossing signal anymore.

      Yr circuit does need a full live and a full neutral wire. So what you could do is pull anextra wire. You could get that from the lamp, or maybe there is a mains outlet close by that you can pull that wire from, provied all your points are connected via conduit pipe. All depends a bit on the switch situation in yr home. Feel free to send a picture

      1. Thanks for your breath answer. Everything is exactly as you’ve told, but problem is that its no way to get neutral wire without breaking the wall… What about triac dimmer? Could this work with it?

      2. Well my dimmer is also a TRIAC dimmer the sole reason it needs both the neutral and the phase wire is to detect the zero crossing.
        a non automated TRIAC dimmer (with a knob) doesnt need that.
        I understand you dont like breaking any walls and ideally you shouldnt have to because there should be a conduit in your wall for cabling.
        Anyway, there still are other solutions:
        1- pick up the zerocross signal somewhere else. If you feed your arduino with a PSU… that somewhere is plugged into a 220 V socket. you could pick up the zerocrossing puls even from the secundary winding of the transformer
        2- use a standard dimmer with a knob and regulate that with a servo or steppermotor
        3- use the circuit here

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s