loading

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.

<p>Hi! Disclaimer: I am a new with arduino and these kind of things. I would like to ask how would I be able to control dimming easier with these? I am working on a project where I will be dimming an incandescent bulb, an AC motor, and a water heater. However, the dimming values will be dictated by the arduino using conditions that I will set (using fuzzy logic) and with the reading of my temperature and humidity sensor. I am already a bit familiar with PWM dimming and I find its codes more understandable than interrupt so I'm hoping to make the codes a bit similar to that. Also, will the interrupt affect the countdown timer I will be using as a background function? How can I make each channel work independently of the others? Sorry for the questions if it seems so much. I just really need help understanding these things. Thank you!</p>
<p>You are partly right. Indeed the software is more complicated than for instance with PWM, but the channels do work independent from eachother (but not independent from the interrupt).<br>If you use this circuit you will have to deal with interrupts.... or you need to frequently poll the zerocross pin, but that is less reliable.<br><br>if you want to stick to PWM........ you can use my <a href="https://www.instructables.com/id/AC-PWM-Dimmer-for-Arduino/">pwm dimmer</a>. However that is only suitable for DC devices, so it is going to be difficult for an AC motor.<br>in that instructable however I discuss a circuit by Ton Giesberts, that as far as I know can be used for AC, but in my opinion is not as ideal (for DC) as my circuit.<br>So you could decide to build my PWM circuit for your incandescent bulbs and Giesberts circuit for your motor. What your heater needs I am not sure</p>
<p>Thank you so much for the response! :) </p><p>I think the heater runs with the same concept as the incandescent bulb since the bulb lights up by heating the filament inside. </p><p><br>Can Giesbert's circuit handle the motor if it will be running continuously for 24 hours for several days? Same question for your PWM dimmer. The device I will be building should run 24 hours for 21-30 days straight. Thank you! </p>
<p>My dimmer cannot handle any ac motor as it is DC.<br>Whether Giesbert's circuit can depends largely on the demands the motor is having. What is the wattage of the motor?</p>
<p>sorry for the misinformation. the motor is just a blower fan type, around 40W</p>
<p>then giesbert's circuit should have no problems with it</p>
<p>Hi! I have a problem. i use your code with 220VAC 50Hz, then the lamp always blink. What should i do to make it smoother.</p>
<p>is it just on one lamp or on all 3?</p>
On all lamp.
<p>is it at all levels?</p>
<p>yes. in void loop() i'm try to use this code</p><p>void loop() {</p><p>int i;</p><p>if(Serial.available()){</p><p>i = Serial.parseInt();</p><p>Serial.println(i);</p><p>}</p><p>for(int a=0; a &lt; PINS; a++) {</p><p>int ii = i+42; </p><p>if(ii &gt; 127) ii -= 127;</p><p>AC_dim[a] = ii;</p><p>}</p><p>delay(50);</p><p>}</p>
<p>ok what happens if you run my original code</p>
<p>when i use PINS 1<br>it's smooth dim 1 lamp.<br>but in original (PINS 3)<br>it's blink all 3 lamps.</p>
<p>Hmm, odd, works fine here. I am sure you tried 'PINS 2' What is that result?</p>
<p>Lamp 1 smooth but Lamp 2 have a few blink. </p>
<p>ar. i know why it's blink. i write &quot;Serial.println(i); &quot; at line under AC_dim[a] = ii;</p><p>then the lamps is blink.</p>
<p>yes indeed. Happy that got solved</p>
<p>i have a few questions. Is it possible to use Serial.read() in this code.</p>
<p>your orgunal code is work. but when i adapt with BH1750FVI.h or Serial.print .<br>all the lamps are blink. </p>
<p>Can i use different dimming level to the lamps</p>
<p>yes</p>
<p>All lamps are blink.</p>
<p>Done ! with some adaptations, thank you for the help ! <br><br>pcb made by laser, chimistry perchlorate, and for hole with drill.</p>
<p>looks good! I looking to do the same with for 120V/60Hz AC mains. I'm wondering if you can help me understand the sizing of R1,R4,and R6 (the 1k resistors on the detector side of MOC3021)</p><p>I have looked a several data sheets on different optoisolators to get an understanding but the nomenclature isn't very clear. </p>
<p>check this : </p><p>http://www.uploadarchief.net/files/download/gate_r.pdf</p>
<p>I am sorry I overlooked your post. I am not sure what you are asking. Isnt 1 k working for you?</p>
<p>just for information, I use BT131-600 on 240vac, on some other web project , they use 2 resistor in serial , 380r+330r ( 710r ) .. d'autre encore - ( 450ohm)<br><br>the resistor depend of the bt131 and moc3021<br><br>for gate resistor check this :<br><br>http://electronics.stackexchange.com/questions/53500/optotriactriac-how-do-i-calculate-the-gate-resistor<br><br>warning with &quot;inductive load &quot; , you have to add 1 resistor and condo </p>
<p>yes correct</p>
<p>looks very neat and straight. What TRIAC's did you end up using?</p>
<p>Hallo,</p><p>ich would like to build it with 4 Lamps and between the two colors, is it dark?</p><p>Do you have a video for me?</p>
I never made it with 4 lamps so i cannot send you a video of that. I have done one with 6 lamps but sold that<br><br>Not sure what you mean with 'between the two colors' But wether a lamp is on or off depends on the software
Do you have a Video of the 3 or 6 lamps? <br>I'd like to know if its dark when the color changes. So the Room will be dark when the color changes.
<p>no I never bothererd to make one.<br>whether or not the room is dark all depends on whether you light the lamps, so maybe I do not fully understand your questions</p>
Hai, i little bit confused. I've got a project that i want to control 3 phase induction motor using triac dimmer. But i still confused about the schematics. Is that i should to make 3 zero crossing detector for each phase? And how about the sketch coz i'm not a code wizard. I really need your help and reply. Thanksssss
<p>NovaeE1 Thanks for your reaction. <br>I am not familiar enough with 3 phase current to give you advice here, but I think that if 2 phase current only needs 1 zero cross detector, you wouldnt suddenly need 3 for 3 phase current. Basically 1 zero cross detection would do as the other phases are 120 degrees away so they can be easily calculated..and you coukd try it that way.Ofcourse in that case this circuit will need to be altered as such that the 3 channels are fed seperately from each phase. But as I said, I dont know enough about 3 phase to advise you. Maybe it is enough to regulate 2 channels in relation to the other, after all that is how it happens with 2 phase.<br>You may consider a commercially available 3 phase solid state regulator from Fingla or Teledyne <br></p>
<p>Hi. Can i replace the : </p><p>IL251 with an H11AA1 ?</p><p>Thank you!</p><p>Marc.</p>
<p>the H11aa1 is also a bipolar/AC optocoupler so yes you can. But check the pin out to make sure you have the right connections</p>
<p>Thank You :)</p>
<p>my pleasure</p>
<p>Please share your Fritzing. Thanks</p>
<p>i think my dropbox and google was full. pm meyour emailaddress and i will send it. You could also just give it a go yrself. as you can see it is a real easy design</p>
Please email me quocanhcgd@gmail.com. Thanks. i m a newbie. So i can't design it.<br> When i make follow yours here https://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/.<br>i change 4n25 to 4n35 and DB104 to DB107. Its not work. Because in my country don't has ICs in your case. <br>Thanks for help.
<p>the 4n35 and DB107 shouldnt be the problem</p>
<p>Hi! I'm trying to dim 2 lamps with a pot, but the lamp is flikering when i turned the pot too low... is it normal? what would be the correct code?</p><p>here's my code in loop()</p><p>--------------------------------------------------------------</p><p>const int pot_pin1 = A0, pot_pin2 = A1;</p><p>int pot_value1, int pot_value2;</p><p>void loop(){ <br></p><p>pot_value1 = analogRead(pot_pin1);<br> pot_value1 = map(pot_value1, 0, 1023, 0, 128);<br> pot_value2 = analogRead(pot_pin2);<br> pot_value2 = map(pot_value2, 0, 1023, 0, 128);<br> <br> AC_dim[0] = pot_value1;</p><p>AC_dim[1] = pot_value2;</p><p>thank you so much! :)</p><p>marC:)</p>
Hi! Thank you very much for your reply, here's the full code :<br><br>/*<br> 2 Triac DIMMER<br> AC Light Control<br> Ryan McLaughlin <br> with slight modifications<br>*/<br><br>#include // http://www.arduino.cc/playground/Code/Timer1<br>#define PINS 2<br>volatile int pinCount[PINS]; // make volatile to make available in interrupt<br>volatile boolean zero_cross=0; // Boolean to store a &quot;switch&quot; to tell us if we have crossed zero<br>int AC_pins[] = {5,6}; // Stup the pin numbers<br>int AC_dim[PINS]; // Holds Dimming levels (0-128) 0 = on, 128 = 0ff<br>int freqStep = 65; // Set the delay for the frequency of power (65 for 60Hz, 78 for 50Hz) per step (using 128 steps)<br> // freqStep may need some adjustment depending on your power the formula <br> // you need to us is (500000/AC_freq)/NumSteps = freqStep<br> <br>int pot_value1, pot_value2, pot_value3;<br>const int pot_pin1 = A0, pot_pin2 = A1;<br><br>int ldr_value;<br><br>const int SWITCH_LDR_DIMMER = 8;<br><br>void setup() {<br> for(int a=0; a &lt; PINS; a++) { //set the pins to output<br> pinMode(AC_pins[a],OUTPUT);<br> pinCount[a] = 0; // keeps track of the time in the cycle <br> AC_dim[a] = 0; // dimming level set to zero<br> }<br> attachInterrupt(0, zero_cross_detect, FALLING); // Attach Interrupt to Pin 2 (interrupt 0) for Zero Cross Detection<br> Serial.begin(9600);<br> Timer1.initialize(freqStep); // Initialize TimerOne library for the freq we need<br> Timer1.attachInterrupt(dim_check, freqStep); // Use the TimerOne Library to attach an interrupt<br> // to the function we use to check to see if it is <br> // the right time to fire the triac. This function <br> // will now run every freqStep in microseconds. <br> <br>}<br><br>void zero_cross_detect() { // function to be fired at the zero crossing <br> zero_cross = 1; // set flag to tell dimming function zero cross has occured<br>} // End zero_cross_detect<br><br>void dim_check() { // Function will fire the triac at the proper time<br> if(zero_cross == 1) { // First check to make sure the zero-cross has happened else do nothing<br> for(int a=0; a &lt; PINS; a++) {<br> if(pinCount[a] &gt;= AC_dim[a]) { // Check and see if i has reached the dimming value we want<br> digitalWrite(AC_pins[a], HIGH); // Fire the Triac <br> delayMicroseconds(5); // Pause briefly to ensure the triac turned on<br> digitalWrite(AC_pins[a], LOW); // Turn off the Triac gate (Triac will turn off at the next zero cross)<br> pinCount[a] = 0; // Reset the accumulator<br> zero_cross = 0; // Reset the zero_cross so it may be turned on again at the next zero_cross_detect <br> } else {<br> pinCount[a]++; // If the dimming value has not been reached, incriment the counter<br> } <br> }<br> }<br>}<br><br>void Test_Dim(){<br>// Test dim<br>// This is simply making all outputs cycle through bright-dark, out of time with each other.<br> for(int i=0; i&lt;127; i ++) {<br> for(int a=0; a &lt; PINS; a++) {<br> int ii = i+42; //this is the bit that puts the blinking lights out of sync with one another<br> if(ii &gt; 127) ii -= 127;<br> AC_dim[a] = ii;<br> }<br> delay(50);<br> } <br>}<br><br>void loop(){ <br> pot_value1 = analogRead(pot_pin1);<br> pot_value1 = map(pot_value1, 0, 1023, 0, 128);<br><br> pot_value2 = analogRead(pot_pin2);<br> pot_value2 = map(pot_value2, 0, 1023, 0, 128);<br> <br> AC_dim[0] = pot_value1;<br> AC_dim[1] = pot_value2;<br><br>/*<br> ldr_value = analogRead(2);<br> ldr_value = map(ldr_value, 0, 1023, 0, 128);<br> AC_dim[0] = ldr_value;<br> AC_dim[1] = ldr_value;<br> delay(50);<br>*/<br><br>}<br>
<p>please first try to limit the range a bit by mapping to 5, 125</p>
<p>well it is hard to say as I dont know how you dim your lamps in software. I presume you wait for the zerocross and then fire either of two TRIACS depending on the value of the corresponding pot.<br>Now there could be two causes that come to mind:<br>either the two dim values influence eachother, but I woulkd have to see your full code for that, or your timing is a bit off.<br>Now if you say &quot;turn the pot low&quot; do you mean that you try to turn the lamp low? (pot value 1023, dim value 128)?<br>The point with zerocross detection is that the circuit doesn't provide an ideal signal: depending on the value of the drop down resistors the zeroross signal can start a shorter or longer period before the real; zerocross and can last a bit til after the real zerocross. That means that the 50/60Hz period may not be a full 10 or 8.33 mS, so you may still be waiting to fire the triac, or just have fired it, when the period already passed.<br>What you need to do for now is first let me know if there is aproblem if you just regulate one lamp and secondly, you may try to limit your range a bit by e.g. <br>map(pot_valueX, 0,1023, 5,125) and experiment a bit with those two values.<br>Also if you like to send me your full code I will have a look at it.</p>

About This Instructable

12,487views

63favorites

License:

Bio: 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 ... More »
More by diy_bloke:Working With the Funduino W5100 Ethernet Module. Uploading Data to ThingSpeak With MQTT Adding an MCP23017 I/O Extender to Arduino or ESP8266 
Add instructable to: