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);
 }
 }

dimmer-print2

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

About these ads

114 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

    • 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 0f 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).

    • 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.

    • 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;
    }

    • 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!

    • 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

      • 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 …

      • 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 ..

    • Indra, yes you are understood wel. You need to connect the zero crossing to an interrupt of the microcontroller annd 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 an dyou 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
    }

    • 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

    • 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 ..

    • 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 ..

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

    • 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.

    • 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.

    • 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

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

    • 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

    • 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

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

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

      • 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.

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

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

      • 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.

      • 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

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

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

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

    • 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

    • /*
      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

    • 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.

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

      • 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

      • 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

      • 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

    • 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.

      • 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!!.

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

    • 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/

      • 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

    • 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.

      • 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

    • 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’

    • 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

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