Introduction: 3 Channel Dimmer/fader for Arduino or Other Microcontroller

About: I am a physician by trade. After a career in the pharmeceutical world I decided to take it a bit slower and do things I like. Other than my hobbies that involves grassroots medicine in S.E.&P Asia. I have buil…

In an earlier instructable I presented a simple AC TRIAC fader/dimmer that could be controlled with an Arduino. In various reactions I got, a number of people expressed their interest in a 3 channel RGB fader. But since I have not seen any results of those yet. I decided to try myself. I was actually triggered (no pun intended by coming across the T1M5F600 TRIAC (LiteOn) that was quite cheap (0.20 eurocents) and was specified as 1A 600V with a 5mA gate current. It is in a TO92 housing and 1A seems quite enough to switch a coloured lamp who usually dont have a high Wattage to begin with.

BOM:
For the dimmer
3x 470 Ohm resistor
3x MOC3021 (or other type without zero crossing)
3x1k
3x T1MF5F600 Triac
1x header 4 or 5 pin
4x connector
(and of course a red, blue and green lamp)

For the zero-crossing detection
2x 33kOhm resistor
1x10k resistor
1x IL251 bidirectional OptoCoupler or similar like EL814, LTV814

PCB

STOP: This circuit is attached to a 110-220 Voltage. Do not build this if you are not confident about what you are doing. Unplug it before coming even close to the PCB. The cooling plate of the Triac is attached to the mains. Do not touch it while in operation. Put it in a proper enclosure/container.

WAIT: Let me just add a stronger warning here: This circuit is safe if it is built and implemented only by people who know what they are doing. If you have no clue or if you are doubting about what you do, chances are you are going to be DEAD!


The circuit is quite standard. It consists of 3 Triac's that are triggered via a MOC3021. One can use other Triac optocouplers as long as they do not have an inbuilt zero-cross network as we want o decide ourselves when to open the triac.

The circuit that does the zero cross detection is kept quite simple: A bidirectional opto-coupler is fed with 220V AC where the two 33k resistors cause current limitation and a voltage drop. To be on the safe side Both resistors should be 1 Watt. It is possible to use highly sensitive optocouplers and thus limit the current needed and with that limit the dissipation in the resistors (e.g. by using a 4N33), but I do not know any of those that are also bidirectional. So if you'd want to use those, you would also need a bridge rectifier. See my other article: Arduino controlled lightdimmer

It is also possible of course to get the zero-crossing signal from the secundary side of a transformer.

Step 1: 3 Channel Dimmer/fader for Arduino or Other Microcontroller: the PCB

The PCB houses the 3 dimming circuits as well as the Zero cross detection. A PCB design is provided for direct toner transfer. The stocking of the PCB is quite straightforward. Note that the Triac, the M5F600 is in a TO92 case and the pin layout is different from most other Triacs in the TO220 casing.

Note that the IL250 should be mounted with its notch pointing down, whereas the other opto-couplers should be mounted with the notch pointing up

Step 2: 3 Channel Dimmer/fader for Arduino or Other Microcontroller: Software

This is just for demonstration or testing purpose.

As this program is just to illustrate, the loop cycles between 0 and 127..


/*
 AC Light Control
 Ryan McLaughlin <ryanjmclaughlin@gmail.com>
 with slight modifications
*/

#include <TimerOne.h>           // http://www.arduino.cc/playground/Code/Timer1
#define PINS 3
volatile int pinCount[PINS];    // make volatile to make available in interrupt
volatile boolean zero_cross=0;  // Boolean to store a "switch" to tell us if we have crossed zero
int AC_pins[] = {3,4,5};        // Stup the pin numbers
int AC_dim[PINS];               // Holds Dimming levels (0-128)  0 = on, 128 = 0ff
int freqStep = 78;              // 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
                                
void setup() {
  for(int a=0; a < PINS; a++) { //set the pins to output
   pinMode(AC_pins[a],OUTPUT);
   pinCount[a] = 0;             // keeps track of the time in the cycle 
   AC_dim[a] = 0;               // dimming level set to zero
  }
  attachInterrupt(0, zero_cross_detect, FALLING);  // Attach Interrupt to Pin 2 (interrupt 0) for Zero Cross Detection
  Serial.begin(9600);
  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() {        // function to be fired at the zero crossing                           
   zero_cross = 1;                // set flag to tell dimming function 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
   for(int a=0; a < PINS; a++) {
     if(pinCount[a] >= AC_dim[a]) {       // Check and see if i has reached the dimming value we want
       digitalWrite(AC_pins[a], HIGH);    // Fire the Triac 
       delayMicroseconds(5);              // Pause briefly to ensure the triac turned on
       digitalWrite(AC_pins[a], LOW);     // Turn off the Triac gate (Triac will turn off at the next zero cross)
       pinCount[a] = 0;                   // Reset the accumulator
       zero_cross = 0;                    // Reset the zero_cross so it may be turned on again at the next zero_cross_detect    
     } else {
       pinCount[a]++;                     // If the dimming value has not been reached, incriment the counter
     }    
   }
 }
}

void loop() {
  // This is simply making all outputs cycle through bright-dark, out of time with each other.
 for(int i=0; i<127; i ++) {
   for(int a=0; a < PINS; a++) {
      int ii = i+42;               //this is the bit that puts the blinking lights out of sync with one another
      if(ii > 127) ii -= 127;
      AC_dim[a] = ii;
    }
    delay(50);
  }
}

Step 3: 3 Channel Dimmer/fader: Tips for Software

though it may seem a bit daunting to develop software for a 3-channel dimmer, it is not so hard as long as you keep focussed on the essentials the software needs to do:
-Wait for the zerocross interrupt to happen.
-Wait a set time before tiggering the TRIAC
-Trigger the TRIAC

For 3 channels that is not much different, you just have to keep track of 3 time variables. You can do that with "delays" but that is rather complicated. You can do that with "micros" and keep checking the 3 time variables against the time passed since the zerocross. Finally, you can do it with a timer interrupt.

The way timer interrupts are normally used if you are dimming one channel, is to set the interrupt for the desired time and when the interrupt occurs to trigger the TRIAC. With 3 channels that is impossible because you do not have 3 timer interrupts.

A better way is to set the timer interrupt to occur for 78uS. That divides the period of a 50Hz grid frequency in 128 steps (remember the re are 2 zerocrossings per 50Hz period, so you have in fact a 100 Hz signal, thus 10mS to do the work in before the next zerocrossing interrupt. 10mS/128=78.125uS). For 60Hz a value of 65 would be good.
You then let the timer interrupt service routine set a counter that in fact counts the number of 78uS steps that have passed since the zerocrossing interrupt occurred.
Your 3 time variables -each for every channel- are expressed in a level between 0 and 128. In the main loop check those against the counter set by your timer interrupt and when it is at the desired level.... ignite the corresponding TRIAC.

If you have no idea how to set timer interrupts, check this article.