AC Light Dimming

attached a slightly updated schematic. The block with the AC_Out is
your load (a light for example). On my board I have this as a hot and
neutral to connect the load.

I am trying to remember what the capacitor is used for. It will make
a difference in the performance of the circuit when dimming. Check
out the MOC3020 data sheet for more info and some other schematics.
It also should be a cap rated for 250V. Part of that circuit is the
feedback to OK2 to ensure the triac fires properly.

Hope this helps.

Glad to see there is so much progress on this topic! This is getting far more detailed than I ever expected. Awesome!

Sorry I have not been around too much. My "real" job has been keeping me away from fun projects like this. However, I have been having some more time lately to get back into things.

I should be posting in the next few days some more info on the final version of the "Darwin Shield" :smiley: 4 AC I/O for the Arduino. I am trying to work out the best way of enclosing the whole thing and connectors for each output. Let me know if you have any input.

I did a project very similar to this a few years ago. I skipped putting in protections for inductive loads while I was developing the circuit. Worked great on lights and such. Then I forgot I had skipped the inductive load protection and plugged in a dremel. Worked great for about 20 seconds. ::slight_smile:

Thanks for posting details.

I actually ended up getting it to work with the capacitor. Mind explaining what inductive load protection is? Is that what the capacitor is for?

attached a slightly updated schematic. The block with the AC_Out is
your load (a light for example). On my board I have this as a hot and
neutral to connect the load.

Sorry, but shouldn't you use a bridge rectifier inbetween the AC input and your opto-isolator? Or is it unnecessary?

The H11AA1 actually has 2 diodes between pins 1 and 2, one facing each direction, so you don't need a rectifier if you use that IC. The 4N25M only has one diode, so you need to use a rectifier with that chip.

Has anyone actually tried using this example to dim multiple lights? I am wondering how to implement the timing issues with switching more than one pin on the arduino where each one has a different value for 'dim'. Anyone have any success or sample code they'd like to share?

Has anyone actually tried using this example to dim multiple lights? I am wondering how to implement the timing issues with switching more than one pin on the arduino where each one has a different value for 'dim'. Anyone have any success or sample code they'd like to share?

Hi,

I did it here:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1251810521

Sorry, but it's in French...

With this code, you can drive 8 lights, probably more. Each light is independent: you can change the 'dim' value via the IDE serial monitor by sending a frame like

" D/0/45/F"
=> a space
=> "D" = start of frame flag
=> "/" first separator
=> n° of dim channel (0 to 7)
=> "/" second separator
=> dim level (0% to 100%)
=> "/" last separator
=> "F" = end of frame flag

In the last version of code, I have added a transition delay when you change 'dim' value. This delay should be modify.

If you are interessing by the last version of the code, perhaps I can translate the comments and publish here ?

Gromain

Thanks Gromain,
I think I can manage the translation, however I think you have only uploaded the initialization part of the code in that thread. The second half of the code appears to be missing.

Chicken,

Sorry, I've just upload the second part of code now !!
After the first post, I was unabled to post again ("internal server error" ?!).
This morning it's ok.
If you have any question/improvement/feed-back, please contact me.

I've been reading this post for awhile now. I made a single output dimmer.
Recently, I translated the french code Gromain put up.
I then made a multi (3) output triac circuit.
My lights are flickering.
I assume it has something to do with my 60Hz 120v
and you have 50Hz 220v?
What do i have to change to make the flicker go away?

What do i have to change to make the flicker go away?

50hz gives a zero cross detection every 10ms
So, 60hz gives a zero cross detection every 8ms

I think the last timer interruption (49) appears after a new zero cross.

You have to recalculate all the values retard[] (reduce the value)

but first, you can test by limiting the number of timer interruption by change value 49 in if (c2>=49){ to 45, or maybe less

Don't forget to feedback your test.

Gromain

Thanks big time.
I changed the value to 41 and the flicker is nearly gone.

Here is the code I translated:


/*
DIMMERS 120V 60Hz (8-channel Possibly more)
     
by Gromain59
Translated By Mike Deuschle
mike.dausch@gmail.com

Material part:
- Triac driven by a digital output via an optocoupler
- AC opto-coupler for detecting the zero crossing of phase

Software part:
- A hardware interrupt input 2 at the zero crossing of phase
- A software interrupt that occurs between 100us and 1400us.
      => Interrupt interval is variable to obtain a light curve by linear orders, because of the shape of the sinusoidal signal.
we have:
1. Detection of the transition to zero on input 2
2. execution of detection_zero (): processing channel with a setpoint of 0% and 100%
3. deactivating hardware interrupt, enabling the software interrupt on the basis of delay [0]
4. interrupt after delay [c2] ?s (c2 = 0)
5. execution of controle_canaux ()
      => Index increment c2
      and if c2 is greater than 49, then this is the last cycle
            => Turn OFF of all channels
            => Activate the hardware interrupt
      otherwise:
            => Activation of output channels with 98% to record (either a 469?s delay) or if
            => Interrupt reconfiguration of time with another delay, delay [c2]
           
To change the setpoint of a channel, you must send via the serial monitor a frame of the form:
            " D/0/45/F"
      => Space
      => "D" to indicate the start of the frame
      => "/" As separator
      => The affected channel (0 to 7 here)
      => "/" As separator
      => The desired level (from 0% to 100%)
      => "/" As separator
      => "F" to indicate the end of the frame
     
      Once the frame received, the function sscanf is responsible for retrieving data.
      It converts the received record levels (0 to 50 levels)
     
Resources:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230333861/30 for first test
http://www.hoelscher-hi.de/hendrik/english/dimmer.htm for electronics
http://www.abcelectronique.com/bigonoff/domocan.php?par=3efd4 for electronics and soft (PIC)
arduino forum

*/

#include <TimerOne.h>  // for the interruption time http://www.arduino.cc/playground/Code/Timer1
#include <stdio.h>  // for the treatment of the frame containing the change orders

                      // timeout value for the reception of the frame
int tps_max_lecture = 200; // reading code, counter max between all the characters of a code
int tps_max_carte = 1000; // max meter between reception of a character


long curve[] = {
1469 , // 98 % 1 225,3V retard / zéro = 1469 ms
287 , // 96 % 2 222,7V retard / zéro = 1867 ms
234 , // 94 % 3 220,6V retard / zéro = 2154 ms
201 , // 92 % 4 218,2V retard / zéro = 2388 ms
180 , // 90 % 5 215,4V retard / zéro = 2589 ms
164 , // 88 % 6 213,3V retard / zéro = 2769 ms
152 , // 86 % 7 210,8V retard / zéro = 2933 ms
143 , // 84 % 8 208V retard / zéro = 3085 ms
135 , // 82 % 9 205,7V retard / zéro = 3228 ms
129 , // 80 % 10 202,8V retard / zéro = 3363 ms
124 , // 78 % 11 200,5V retard / zéro = 3492 ms
120 , // 76 % 12 197,6V retard / zéro = 3616 ms
116 , // 74 % 13 195,2V retard / zéro = 3736 ms
112 , // 72 % 14 192,4V retard / zéro = 3852 ms
110 , // 70 % 15 189,6V retard / zéro = 3964 ms
108 , // 68 % 16 186,8V retard / zéro = 4074 ms
106 , // 66 % 17 184V retard / zéro = 4182 ms
105 , // 64 % 18 180,9V retard / zéro = 4288 ms
103 , // 62 % 19 178,1V retard / zéro = 4393 ms
102 , // 60 % 20 175,1V retard / zéro = 4496 ms
101 , // 58 % 21 172,1V retard / zéro = 4598 ms
101 , // 56 % 22 168,9V retard / zéro = 4699 ms
100 , // 54 % 23 166,2V retard / zéro = 4800 ms
100 , // 52 % 24 162,6V retard / zéro = 4900 ms
100 , // 50 % 25 159,3V retard / zéro = 5000 ms
101 , // 48 % 26 155,8V retard / zéro = 5100 ms
100 , // 46 % 27 152,6V retard / zéro = 5201 ms
101 , // 44 % 28 149,1V retard / zéro = 5301 ms
102 , // 42 % 29 145,3V retard / zéro = 5402 ms
103 , // 40 % 30 141,8V retard / zéro = 5504 ms
105 , // 38 % 31 138V retard / zéro = 5607 ms
106 , // 36 % 32 133,8V retard / zéro = 5712 ms
108 , // 34 % 33 130V retard / zéro = 5818 ms
110 , // 32 % 34 126V retard / zéro = 5926 ms
112 , // 30 % 35 121,7V retard / zéro = 6036 ms
116 , // 28 % 36 117,1V retard / zéro = 6148 ms
120 , // 26 % 37 112,6V retard / zéro = 6264 ms
124 , // 24 % 38 107,7V retard / zéro = 6384 ms
129 , // 22 % 39 102,4V retard / zéro = 6508 ms
135 , // 20 % 40 97,2V retard / zéro = 6637 ms
143 , // 18 % 41 92V retard / zéro = 6772 ms
152 , // 16 % 42 85,7V retard / zéro = 6915 ms
164 , // 14 % 43 79,4V retard / zéro = 7067 ms
180 , // 12 % 44 72,8V retard / zéro = 7231 ms
201 , // 10 % 45 64,8V retard / zéro = 7411 ms
234 , // 8 % 46 56,4V retard / zéro = 7612 ms
286 , // 6 % 47 46V retard / zéro = 7846 ms
399 , // 4 % 48 32,4V retard / zéro = 8132 ms
500 , //
1469  // 2 % 49 0V retard / zéro = 8531 ms
};

int set[] = {    // set channel level (0 = 100%, 50 = 0%)
0, // Output 0
0, // output 1
0, // output 2
0, // output 3
0, // output 4
0, // output 5
0, // output 6
0, // output 7
};

int output [] = { // assign a pin for each channel.
4, // Output 0
3, // output 1
5, // output 2
0, // output 3
0, // output 4
0, // output 5
0, // output 6
0, // output 7
};

volatile int c1 = 0; // index c1 for reading data from each channel (No pin, luggage)
volatile int c2 = 0; // c2 index number passing through the loop control phase delay (49 passages)

// Definition of macros to drive the output
#define lightON(index) (digitalWrite(output[index], HIGH))
#define lightOFF(index) (digitalWrite(output[index], LOW))


void setup () {// Start of setup

// Initialize the serial
Serial.begin (9600);

// Initialize the channel outputs (triacs)
      for (c1 = 0; c1 <= 7; c1++) {// we traverse the 8 channels to configure
                  pinMode(output[c1], OUTPUT); // we associate each channel has a pin, which sets the output digital
                  lightOFF(output[c1]); // and we switch off the output
                                  }

      Serial.println( "Gromain 8-CHANNEL DIMMER v0.2");
      Serial.println( "FRAME EXPECTED: <space> 'D' / 'Output Port' / 'Value of DIM' / 'F'");


// Initialize the interruption time Timer1
      Timer1.initialize(); // Initialize TimerOne library for the freq we need


// Attach the interrupt 0 to pin 2 for the detection of zero crossing (Zero Cross Detection)
      attachInterrupt(0, detection_zero, FALLING); // Attach an Interrupt to Pin 2 (Interrupt 0) for Zero Cross Detection

} // End of setup

void detection_zero() {// function associated with the interrupt 0

      detachInterrupt(0); // disables the interrupt on zero crossing

      c2 = 0;
     
      for (c1 = 0; c1 <= 7; c1++) {// we scan the 8 outputs to check their orders
              if (set[c1] >= 49 ) {// if set 0%
                  lightOFF(c1); // then we switch off
              }
           
            if (set[c1]<= 0){// if set 100%
                  lightON(c1); // then we light
            }
           
      }

      Timer1.attachInterrupt(controle_canaux, curve[c2]); // we attach the interruption time
     
} // End of detection_zero

void controle_canaux() {// here we verified whether the triac must be initiated

     c2=c2++;
     
      attachInterrupt(0, detection_zero, FALLING); // we attach an interrupt on pin 2 (interrupt 0)
      Timer1.detachInterrupt(); // we detach the interruption time

      if (c2 >= 41) {// If last cycle then (best at 41 for 60Hz)

      for (c1 = 0; c1 <= 7; c1++) {// we scan the 8 outputs
            lightOFF(c1);  // and we put out the channel for the next cycle
      }

     
      }
else { // else

      Timer1.attachInterrupt(controle_canaux, curve[c2]); // we attach a break time

      for (c1 = 0; c1 <= 7; c1++) { // we scan the 8 outputs to check their orders
              if (set[c1] == c2) // if is set equal to the processed (no change in the loop)
            {lightON(c1);} // then we light the channel
      }
                 
} // End function controle_canaux

}
void loop() {// Main Loop
 
      int n = 0;
     
      if (Serial.available ()> 0) {
            n = lecture();
      }

}

int lecture() {// read a frame type: "D / aaa / bbb / F
                              // Or "D" starting character frame
                              // Or "yyyy" No output which is set to modify
                              // Or "bbbb" new set of output (between 0 and 100%)



      char buf[15] = "              ";
      int timeout = 0;
      int i = 0;
      int n1 = 0;
      int n2 = 0;
      char c1, c2;
     
      while (Serial.available() > 0) {
            if(i!=14){
            buf[i] = Serial.read ();
            i++;
            }

      timeout++;
     
      if (timeout>tps_max_lecture)
            {Serial.println("T1");
            return -1;
            }
      if (timeout> tps_max_lecture)
            {Serial.println("T2");
            return -2;
            }
        }
      sscanf(buf, "%c/%d/%d/%c", &c1, &n1, &n2, &c2); // decoding frame
     
      if (c1 == 'D' && c2 == 'F') {// Check if the plot starts out by D and ending in F
           
            int nouv_cons = n2;  // we store the new value for the work then
           
            nouv_cons = constrain(nouv_cons, 0, 100); // on the new terminal value between 0 and 100%

... Continue (i ran out of characters)

Serial.print("Output ");
Serial.print(n1);
Serial.print(" , new value of: ");
Serial.print(nouv_cons);
Serial.print(" % index, delay: ");

set[n1] = (50 - (nouv_cons / 2)); // it converts the value 0-100% in no phase delay

Serial.println (set[n1]);
}
else // if character from the beginning or end of frame not recognized
{Serial.println("Code Unknown");}

return i;

}

Can a BTA20 be dropped in place of the BTA10 to provide more amperage.

I need to control several 2kw heating elements

You should be able to use a BTA20, as long as you make the proper adjustments to the circuit to safely carry the additional current. Larger traces, wider trace spacing, etc. As always, give yourself extra space around the components and traces, and once it's all working, I would suggest coating the solder side of your board with a protectant, such as silicone or epoxy. This makes repair difficult to impossible, but also makes the thing much safer in the work environment.

--Tesla

Very cool, thank-you very much.

I will reply back with the results if I don't turn myself into a cinder.

Just keep your hands and feet clear of the system while it's energized, and be aware of stray bits of wire and such. Make sure whatever you have it set on is clean and non-conductive. I usually use a chunk of cardboard if I'm testing it outside a proper mount or enclosure.
Good luck with it!

--Tesla

Still working on this, out of curiosity what would be required to run on 220V AC?