Go Down

Topic: AC Light Dimming (Read 28271 times) previous topic - next topic


Hi Ryan fantastic!

Your code is much simpler and cleaner than my buggy suggestion!

Im looking forward to entering the fun when my semester examproject is finished in a week.

What features are you planning?



Thanks,  I want to try to be able to write it to be able to just set the dim value.  I am also still having problems with delays in the main program, then the interrupt won't fire.

It works great stand alone, but I need to do the testing when integrating it into a larger program that is doing other things.


Hi,  Here is the current schematic.  There is a bit of leway in what triac and opto-triac coupler you can use.  Each output is for 10A, so I would not put more than 2-4A on each output maybe 600W, plus remember to keep wire gage in mind!

The "AC_out" should be connected to the "hot" of the load and the neutral of the load connected to ac neutral.

If there is enough interest I might make a PCB and sell a kit with 4 outputs.


Great work - but some questions...

I have been looking around over at http://diylightanimation.com/ thinking about next Christmas and Blinky lights on my house. Being an Arduino guy I wanted see what was possible using it.  Your post is a godsend.  

The sample code you posted uses a delay and one output - but your schematic shows 4 triacs.  Do you use a if/else string to test some current counter to decide if one or the other Triac needs to be turned on?  Like setting one of the timer's up to decrement a variable and test it in a series of if/else in the main loop?  

In any event your example is quite a great start at what I want to do - which is something like the SSR4 @  http://diylightanimation.com/wiki/index.php?title=Equipment

If I could your code to play nice with Lady Ada's WAV shield (doubt it) it would be a kewl project.  


right now I only have dimming hooked up on one channel. Fir what I am working on I only need one channel to dim and 3 just on off so this will work great. My thought to do all 4 would be to do the math for the dim time in the main program. You would need to figure out which channel to dim first and do the additional delays after that.  It would take some playing around with.

I did some more debugging on the code yesterday and will post the changes. The only problem I am still havving is speed. It seems that the delays are consuming all my program time and I cannot get the dimming process to run in the "background"

Thanks for the support!


Over here they talkabout timer interupts.  http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1167000525.

I wish I understood all I read there,  but it would seem to be instead of using delay(), set up a timer to interupt around 15360 times a second ( 120 * 128).  Have the hardware interupt reset a value, and the timer increment it.  In the main loop test if the value is more than your dimming value and turn on the triac.  That way the interupts take care of the timing in the background, your code just tests for a value and compares it agains the dimming value[n]  which could be different for each output.  Send in the value from a computer with rs232 and voila - blinky ights.

Its something I am going to try - but I don't have an o-scope.

For inspiraration got to YouTube and do a search for Christmas - see what some people have done.
Keep up the good work. If you ever do a board - I am in for a few. Especially if it is a shield...


You read my mind,  I was actually reading up on the interrupts and was going to try to do that.  That seems like the ideal way to go.

I was lucky to be able to use an old scope from work, so I will give it a try.

A shield is a good idea...  that might be do-able  :)


Hi folks

I really love your project Ryan!

I was just wondering if some of you may know if this circuit will work with the Norwegian power supply (230 VAC, 2 Phase, 2 hot wires, no neutral ). At least I think that is what we are using[ch8230] =).  If not, does anyone know what has to be changed in the circuit to make it work?

Really appreciate your help people, and maybe someday I will be able to help you with your problems. (I am more of a software guy).

Greetings from Norway.


The triad control can work with 230, although I have not tested it. You may need to change some of the resistor values to optimize it.  

The only thing I am not sure about is the ZC detection. It should be ok but would need to be tested.


over at uC Hobby they had and article about Arduino interrupts and timers http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/

In the code examples they had this function that will set the frequency for timer2 - so if you call this with the number 128*120 it will set up time2 to call an interrupt routine ~128 times a second. Keeping that routine simple (short)maybe just incrementing a variable.

This any help?

Code: [Select]
#define TIMER_CLOCK_FREQ 2000000.0 //2MHz for /8 prescale from 16MHz

//Setup Timer2.
//Configures the ATMega168 8-Bit Timer2 to generate an interrupt
//at the specified frequency.
//Returns the timer load value which must be loaded into TCNT2
//inside your ISR routine.
//See the example usage below.
unsigned char SetupTimer2(float timeoutFrequency){
 unsigned char result; //The timer load value.

 //Calculate the timer load value
 //The 257 really should be 256 but I get better results with 257.

 //Timer2 Settings: Timer Prescaler /8, mode 0
 //Timer clock = 16MHz/8 = 2Mhz or 0.5us
 //The /8 prescale gives us a good range to work with
 //so we just hard code this for now.
 TCCR2A = 0;
 TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20;

 //Timer2 Overflow Interrupt Enable
 TIMSK2 = 1<<TOIE2;

 //load the timer for its first cycle



To AdaRma

I am pretty sure the mains in Norway is similar to the Danish net. Which means that the voltage is 230 V all right but with only one hot pole (Phase). The other i 0V (zero) which is not ground!

Energy consuming machines like ovens etc. may use what we in Danish call "Kraft" which is two hot wires (phases each 230V from zero, but the sine-wave phase is a bit different, so the resulting difference between the two phases is 380 V)
This is however only awailable in special outlets. The usual wall outlets only contain one phase and zero.

The circuit should be the same, only the voltage dividing resistors should be recalculated.

I plan on doing something similar soon that is to be used on 230V. I will post my circuit when it is worked out. Probably next week or two.

Hilsen Niels


Thanks sydkahn  that is along the lines I was looking.  I have tried playing around with the Timer One (http://www.arduino.cc/playground/Code/Timer1) library.  This seems to work well and might do what needs to be done.  

Here is the difficult part...  At the zero crossing we need to start the timer to attach a function every 65 Microseconds that tests if it is at the proper number to fire the triac.  If it has reached the dimming value it should fire the triac and detach the interrupt timer function and wait for another zero cross.  However it seems that attaching and detaching the interrupt takes too much time and results in bad timing of the code.  

Does anyone have any ideas?


Jan 10, 2009, 11:21 am Last Edit: Jan 10, 2009, 11:23 am by sydkahn Reason: 1
If you are only going to dim one pin - why disconnect the interrupt?  Just set a boolean true and test first in the Timer ISR and exit if it is set.

Code: [Select]

volatile  int   int_counter  =0;   // to count timer ticks
volatile  bool triggered=false;  // have we turned on the triac?

//timer interupt service routine

ISR(TIMER2_OVF_vect) {  
 if (triggered) return; // the triac is already on - just exit
 int_counter += 1;     //bump the count
 if (int_counter == brightness_value) {  
   triggered=true;//remember that we have turned on the triac

//in the hardware ISR

Have the hardware interrupt reset the boolean and the counter is ready for the next cycle.

FWIW - I got some parts, finally, and am going to put this all together.  I even found someone with a very old scope, maybe there will be some waveforms for you to see.

Something else I have observed is that the opto only interrupts on the rising edge of the positive half of the mains swing.  If you trigger the triac during that phase, it will turn off as the wave goes negative and everything will wait until the net positive excursion of the mains. So at most you will only get 50% brightness.
Or am I missing something critical?  A lot of the app notes I see out there use a full wave bridge in front of the opto so it sees only positive swings.  

BTW i got the interrupt code @ http://gonium.net/md/2006/12/27/i-will-think-before-i-code/


I think I will try something like that. The problem I was finding is that if you do not reset the timer after every zero cross any timing error will begin to accumulate and even after a few seconds your dimming might not work anymore.

As far as the opto...  I need to update the schematic. Use a H11AA1 not a 4N25. Using this you will get the "pulse" I described before at every zero cross.


ITS WORKING!  Haha, yes I am a bit excited.  sydkahn, I used a bit of your advice as well.

Basically, I have one hardware interrupt fire at the zero cross and set a boolean to show the zero cross.  Then I have a software interrupt executing a function to dim the load running at a freq of 1/128 of the AC freq.  Check it out...


 AC Light Control
 Ryan McLaughlin <ryanjmclaughlin@gmail.com>
 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 <TimerOne.h>           // 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 = 10;                // Output to Opto Triac
int Dimmer_pin = 0;             // 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;    // Set the delay for the frequency of power (65 for 60Hz, 78 for 50Hz) per step (using 128 steps)
                     // freqStep may need some adjustment depending on your power the formula
                     // you need to us is (500000/AC_freq)/NumSteps = freqStep
                     // You could also write a seperate function to determine the freq

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, FALLING);   // 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.                                            
}                                                   // End setup

void zero_cross_detect() {        // function to be fired at the zero crossing                          
   zero_cross = 1;               // set the boolean to true to tell our dimming function that a zero cross has occured
}                                 // End zero_cross_detect

void dim_check() {                   // Function will fire the triac at the proper time
 if(zero_cross == 1) {              // First check to make sure the zero-cross has happened else do nothing
   if(i>=dim) {                     // Check and see if i has accumilated to the dimming value we want
     digitalWrite(AC_pin, HIGH);    // Fire the Triac mid-phase
     delayMicroseconds(5);          // Pause briefly to ensure the triac turned on
     digitalWrite(AC_pin, LOW);     // Turn off the Triac gate (Triac will not turn off until next zero cross)
     i = 0;                         // Reset the accumilator
     zero_cross = 0;                // Reset the zero_cross so it may be turned on again at the next zero_cross_detect    
   } else {
     i++;                           // If the dimming value has not been reached, incriment our counter
   }                                // End dim check
 }                                  // End zero_cross check
}                                    // End dim_check function

void loop() {                        // Main Loop
 dim = analogRead(Dimmer_pin) / 8;  // Read the value from the pot, scale it and save it as the dimming value
 analogWrite(LED, dim);             // Write the value to the LED for testing

Also, here is the update to the schematic...  

I am going to try to do the code to Dim all four channels, and maybe even get a PCB draw up to make an Arduino Shield for this.

Go Up