Arduino AC Power Shield!

Is there a danger? Yes.

I'm I worried? No. ...

Ryamjmclaughlan, in those few words you have crystallised my concerns perfectly.

PS - People who write
...
do not inspire me with confidence in their research!
I'd strongly advise such people NOT to "build something like this".
Especially when an SSR should be ideal for dimming lighting.
This thing is basically a homebrew quad SSR, but 100% exposed and bringing mains electricity intimately close to the Arduino (and anyone 'interacting' with the Arduino).

Thanks for mentioning me. Actually I do quite a lot of research. As you imply I mustn't be very good at it since I've not found anything remotely close to an example of using a solid state relay to do Arduino controlled dimming.

Hads, its not difficult. Or Arduino specific.
Its actually pretty 'standard'.
You just need to switch the mains-powered light on and off "fast" (many times a second) so that it seems to be 'partly on'.
How much on? That depends on how the 'on' timeslice compares to the 'off' timeslice. And that is down to your software.

Regarding the actual switching - what is 'fast' for us is desperately slow for the Arduino.

UK mains is 50 Hz.
So each second there are 100 half cycles, making each 10ms.
Now, if you switch exactly on the end of the half cycle (when the voltage is zero, whether its going positive or negative) its all round better for the components (and the disruption to the mains), and that's what a zero-crossing SSR (or the Darwin shield) allows you to to.
So there are 100 time slices per second, in each of which you can have the lamp on or off.
An SSR should have no problem switching at that rate.
10ms allows ages for Arduino thinking.

Incidentally, the reason that one can't just use the LED-dimming PWM outputs to drive the SSR is because the pulses are way too fast for the mains-frequency-locked SSR to react to.

But the Arduino digital outputs are a perfect ("logic level") match for what most SSRs need as a control signal. Almost as though they are both designed to the same 'interface' spec...

Don't run away with the idea that a whole second (or 100 half cycles) is automatically a sensible base for your on/off scale. On for 1, off for 99 is going to seem very flickery.
Exactly how you program the different 'duty cycles' (on/off intervals) is where you can have some fun. Try different ideas and tinker!
But tinkering with exposed mains voltage conductors is likely to, as Darwin would say, "die out".

If you want to get into dangerous mains stuff, you could make a mains zero-crossing detector (something like Ryan's -watching out for your higher UK voltage) mount it within your mains electricity switching enclosure) and take the safe (physically and) opto-isolated 5v logic-level output signal as an interrupt input to the Arduino.
Then you can pick up most of Ryan's code - but use it to drive SSR's in a box rather than bare triacs almost touching the Arduino.
The alternative to using the zero-crossing-interrupt is just to count out a delay of about 9ms or so. The zero-crossing SSR won't switch until the next end of a half cycle. With counting, you're going to get the odd extra half cycle as you drift out of sync, but that really shouldn't matter except to the perfectionist and at extreme dimness... and tweaking the actual time delayed should minimise that effect.

The point that I and so many other posters here are trying to flag up is that handling mains on a close-mounted Arduino shield is wilfully courting disaster.
The potential is not just killing the Arduino (and any attached computer - I wouldn't back the little polyfuse to protect against mains) but also the risk really does go as far as potentially killing the user.
There are simpler, MUCH safer and barely more expensive ways of getting an Arduino to control a small mains (incandescent) lamp, or four.

I'm not saying that people shouldn't ever switch mains with an Arduino.
I'm not criticising the prototype.
I am criticising the basic concept of putting mains electricity on an Arduino shield. That seems lethally daft.

I do wonder who provides "McLaughlin Engineering" with their product liability insurance?

that_chap,

Thank you for your explanation, quite useful. I had assumed that basing the switching on timing without zero crossing detection that it would drift. I guess I should just pick up an SSR, have a play with it and see what happens.

Thanks that_chap for the pointers.

I will post again when I get another design going.

Wow, Ryan! You really shook people up with your project. :slight_smile:

I'm still working on the code to get the relationship between the zero-cross timing and the triac pulse right. It's working well on one channel, but the way I'm doing it right now I think the arithmetic overhead of four channels may be introducing just a little too much of a delay to be useful. So I've been trying to figure out how to slim down the math a bit and lighten the processor load.

Cheers!

That_Chap earlier posted a cautionary note about controlling mains power on a shield. That's hard to argue with. About the idea of dimming by cycle switching? It really might be flickery, that way.

The lighting dimmers that were the source of this idea can switch at some point into each cycle using thyristors. which leaves a smooth dimming effect. That's what the original shield designer has in mind, I expect. But I really like that idea of keeping the line power switching elements in a secure box with a ground current sensing power socket. It would be so safe and GCIs are cheap.

Meanwhile, here I am with an LCD hooked up to a duemilanove and a button that steps up a displayed time duration per push, or faster if held, and when I hook up just one switch to command this pulse and one output to drive an SSR with it, I will be in business. Bang bang control is so much easier in my application!

Brian W

Betwys-

About the idea of dimming by cycle switching? It really might be flickery, that way.
The lighting dimmers that were the source of this idea can switch at some point into each cycle using thyristors. which leaves a smooth dimming effect.

You may have heard the saying "To a man with a hammer, every problem looks like a nail". Well, I've got this zero-crossing SSR, and Ryan was using zero-crossing detection ... ::slight_smile:

For power switching, like the heater I want to use my SSR with, zero-crossing seems to be the best the time to switch, on and off, not least because it minimises electrical noise ('interference'), and a zero-crossing ssr is the seriously easy way to achieve that.
However, for the smoothest possible lighting dimming, yes indeed Betwys is quite right that you should switch during the cycle. I believe the ideal is to switch on at a zero-crossing and then off a controlled (ie variable) time through the cycle, going off at the same time through each half cycle for steady (non-flickering) brightness.
Now, for that you can still use an off-the-shelf ssr BUT you should select one that does NOT boast of zero-crossing switching!
One of the descriptions these go by is 'random switching' ssrs. (But "not-zero-crossing-switching" should explain to a vendor what you are after.) These can switch (on or off) whenever you tell them to, at any point in the cycle. Switching time being about 0.1 ms. (10ms for the half cycle on European (etc) 50Hz mains remember.) So plenty fast enough switching!
Betwys is absolutely right that using a zero-cross detector and a 'random' SSR would be much better than using the integral number of half-cycles that MY ssr lead my thinking towards!

At least some of the $8 (inc worldwide delivery) eBay ssr's make no mention of zero-crossing in their description, so could well be the 'random' type. But if 'random' is what you are after, check with the vendor before buying!

Hads -

I had assumed that basing the switching on timing without zero crossing detection that it would drift. I guess I should just pick up an SSR, have a play with it and see what happens.

With a zero-crossing ssr like mine, it doesn't matter so much if you slip in an extra half cycle on or off very occasionally. That's all that would happen.
BUT if you are using a 'random' ssr (or a triac board), and trying to do the switching-on at the start of the half cycle, then off at a set time into the cycle (for the smoothest dimming) then you really DO want a zero-crossing interrupt signal to tell the Arduino exactly where you are in the cycle.

I have seen alot of heater, fan, etc dimming doing exactly what you are describing. SSR with a specific duty cycle, however I have not seen much lighting this way.

When I was doing some testing, I tried this without doing the zero crossing, but with lighting I could never the the flickering (which was major flickering) to go away. But, this could just because I could not find the right duty cycle.

There two main types of light dimming. These are the two found in your house. Forward-phase control and Reverse-phase control. Phase-fired controller - Wikipedia

Forward phase control is waiting a specific amount of time after a zero cross, then turning on the device to let the remaining part of the wave power the load. This is exactly what this project is doing. Wait a specific amount of time after a zero cross and momentarily fire the triac to power the load. Using zero-crossing triacs the triac will stay latched until the next zero cross. This makes the device easy to control because you only have to control the firing once, and not ensure you turn off again at zero, which can be hard to time right.

Reverse phase control is the opposite where you turn on the triac at the zero cross and cut the power after a specified interval, cutting the trailing end of the wave. This type of control is not as common, but the technology used to dim low voltage transformers for LV lighting in houses. It is not as common because it is more expensive and only used where needed. Go on Lutron or another lighting switch company and you will see that most of the high-end dimmers have an electronic low voltage style dimmer that uses this type of control.

Zero crossing SSR's are more popular because of this reason and they are cheaper. I am not an EE, but if you look at the theroy behind a triac, it is setup to latch until a zero cross. You need to find a special random-phase SSR to be able to turn off the power mid wave.

Switching time being about 0.1 ms. (10ms for the half cycle on European (etc) 50Hz mains remember.) So plenty fast enough switching!

Remember that that is only the 1/2 cycle, you still need to have the dimming resolution within that half cycle. If you want smooth light dimming you are talking about > 50 divisions = 156 microseconds. But, if you are doing element, fan dimming, this resolution can be far less as that_chap suggested

Some countries run at 400Hz, which is the high end that I've been testing against. That's a 1.25ms (1250us) half-period - not very much time to work with. For one channel of dimming control, I've gotten the minimum delay down to about 50us. That's referenced to the falling edge of the previous half-period, so it gives a little wiggle room.

So I guess my research isn't that bad after all.

I just connected a couple dots.... :smiley:

that_chap kept talking about SSRs (Solid State Relays) and I kept reading it as SCRs (Silicon Controlled Rectifiers).

To clear things up a little: SSRs are made from a back-to-back pair of opto-controlled triacs, which means they are functionally the same as the triacs on Ryan's board. So yes, you could use SSRs to do the load switching. However, when I look up the cost of SSRs vs. the cost of triacs with similar specs, the SSRs are an order of magnitude more expensive and considerably larger.

SCRs are sort of similar to triacs, but only conduct on one half of the wave, so you need two of them in inverse parallel to work like a triac. But that would be pointless since you can just use a triac.

EDIT: NEW CODE IS FINISHED!!

OK, it works great at 50/60Hz but it's range isn't all that good at 400Hz. The problem is that it takes about 500us to do all the timing calculations on four channels, which is about 2/5 of the half-period at 400Hz. If you wanted just one channel, you could scale down the code and 400Hz would work fine with about 125us minimum dim. But the dimming is smooth and more-or-less frequency agnostic. The lower the frequency, the better.

I intentionally did not use loops to cycle through the channels. Loops added processing overhead and with four channels, it really wasn't that hard to keep track of. Another weird-but-intentional thing is the placement of the OKToFire=0; lines. I did this because it really didn't matter where these lines went in each section and it adds a little time delay between the digitalWrite lines without using delayMicroseconds() :wink:

You can see the code in action if you have a dual trace oscilloscope. Attach one input to the interrupt pin and another to an output pin. Sync to the interrupt pin and you will see how all the timing works together.

I fought with this code for days until I re-read the attachInterrupt() doc. It states that millis() and delay() don't work in the ISR function. That was key to success since I was trying to calculate all the timing in the ISR function, but was getting weird and inconsistent results. Once I took all that out and put it in void loop(), all was better.

/*
  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
 */
 
/*
  Modified by Mark Chester <mark@chesterfamily.org>
  
  to use the AC line frequency (half-period) as a reference point 
  and fire the triacs based on that plus a dimmer delay value.
  I removed the second timer-based interrupt and replaced it with a
  means to reference the zero-crossing point as per interrupt 0.
*/

// General
unsigned long int ZeroXTime1 = 0;                      // Timestamp in micros() of the latest zero crossing interrupt
unsigned long int ZeroXTime2 = 0;                      // Timestamp in micros() of the previous zero crossing interrupt
unsigned long int NextTriacFire[4];                    // Timestamp in micros() when it's OK to fire the triacs again.
unsigned long int DimStep;                             // How many micros() in each step of dimming
int Dimmer[4];                                         // The dimmer input variable. One for each channel
byte TriacPin[4] = {4,5,6,7};                          // Which digital IO pins to use
boolean OKToFire[4];                                   // Bit to say it's OK for the triacs to fire
volatile boolean zero_cross = 0;                       // Boolean to store a "switch" to tell us if we have crossed zero

void setup() {                                         // Begin setup
  pinMode(TriacPin[0], OUTPUT);                        // Set the Triac pin as output
  pinMode(TriacPin[1], OUTPUT);                        // Set the Triac pin as output
  pinMode(TriacPin[2], OUTPUT);                        // Set the Triac pin as output
  pinMode(TriacPin[3], OUTPUT);                        // Set the Triac pin as output
  attachInterrupt(0, zero_cross_detect, FALLING);      // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  delay(50);                                           // Give the interrupt time to capture a few AC cycles
}                                                      // End setup
  
void zero_cross_detect() {                             // function to be fired at the zero crossing
  zero_cross = 1;                                      // All we do is set a variable that's picked up later in the code
}

void loop() {                                              // Main Loop
  if ( zero_cross ) {                                      // Did we detect a zero cross?
    ZeroXTime2 = ZeroXTime1;                               // shift the current zero cross value to the previous
    ZeroXTime1 = micros();                                 // set the new current zero cross time in micros()
    DimStep = (ZeroXTime1 - ZeroXTime2)/1024;              // Calc the duration of each dimming step
    Dimmer[0] = analogRead(0);                             // Read in a dimmer value (change to suit needs)
    Dimmer[1] = analogRead(1);
    Dimmer[2] = analogRead(2);
    Dimmer[3] = analogRead(3);
    NextTriacFire[0] = ZeroXTime1 + (Dimmer[0] * DimStep); // Calc the next triac fire time
    NextTriacFire[1] = ZeroXTime1 + (Dimmer[1] * DimStep);
    NextTriacFire[2] = ZeroXTime1 + (Dimmer[2] * DimStep);
    NextTriacFire[3] = ZeroXTime1 + (Dimmer[3] * DimStep);
    OKToFire[0] = 1;                                       // Tell us it's OK to fire the triacs
    OKToFire[1] = 1;
    OKToFire[2] = 1;
    OKToFire[3] = 1;
    zero_cross = 0;                                        // Done.  Don't try again until we cross zero again
  }
  if ( OKToFire[0] && micros() >= NextTriacFire[0] ) { // Are we OK and past the delay time?
    digitalWrite(TriacPin[0], HIGH);                   // Fire the Triac mid-phase
    OKToFire[0] = 0;                                   // We fired - no longer OK to fire
    digitalWrite(TriacPin[0], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  if ( OKToFire[1] && micros() >= NextTriacFire[1] ) { // Are we OK and past the delay time?
    digitalWrite(TriacPin[1], HIGH);                   // Fire the Triac mid-phase
    OKToFire[1] = 0;                                   // We fired - no longer OK to fire
    digitalWrite(TriacPin[1], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  if ( OKToFire[2] && micros() >= NextTriacFire[2] ) { // Are we OK and past the delay time?
    digitalWrite(TriacPin[2], HIGH);                   // Fire the Triac mid-phase
    OKToFire[2] = 0;                                   // We fired - no longer OK to fire
    digitalWrite(TriacPin[2], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  if ( OKToFire[3] && micros() >= NextTriacFire[3] ) { // Are we OK and past the delay time?
    digitalWrite(TriacPin[3], HIGH);                   // Fire the Triac mid-phase
    OKToFire[3] = 0;                                   // We fired - no longer OK to fire
    digitalWrite(TriacPin[3], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
}

Thanks koyaanisqatsi that looks great, the code is so short! I will try this on my own board soon.

My only question is what do you do about if you need to run other code? If you are using the Arduino for anything else (LCD, I/O, etc) won't it mess with the timing? I just ask because this was why I started to use Timer1 so I ensured the dimming function worked as a software interrupt and my regular code would not interfere.

Let me know your thoughts. Great work! I guess I should get working on a final design now. In your testing were you getting much buzz from the Triac? I am trying to figure out how to silence it a bit, maybe an inductor but I need to pick a few up to try it.

koyaanisquatsi -

Some countries run at 400Hz ...

Domestic mains electricty at 400 Hz? Really? Where about would that be??
I think that all large populations get their mains notionally at either 50 or 60 Hz (though it always varies slightly with the supply/demand balance varying).

Regarding SSRs (Solid State Relays), my thinking of their usefulness is that :

  • they are a sealed module, even the terminals are usually well-shrouded
  • they provide excellent opto-isolation of the controller from the mains
  • they can be immediately usable (driven absolutely direct from an Arduino digital-out pin)
  • their 'pinout' encourages the physical separation of mains and logic-level control voltages

They aren't tiny. The "25 amp" units I mention are about the same footprint as a standard Arduino.
But with mains, size and space are good for you. Cramming mains things tightly together is not a great idea. Space allows heat dissipation and physical isolation.

I don't think SSRs need be very expensive. (Even though I admit to being 'cost-conscious'!) Searching eBay.com turned up a few vendors seemingly competing at the US$8 price point including delivery worldwide - for units able to switch a nominal 25 amps (with a suitably big heatsink). They should switch 5 amps (or run 3 amps pretty continuously) of resistive load, happily without any heatsink.
You can find (smaller) lower power rated devices, but probably not much cheaper.

IMHO, SSRs are a great way for "Arduino tinkerers" to explore mains control with a massively greater degree of safety than with Ryan's shield. Even so, I'd still advise great care be taken.
But, as I said to Ryan, his quad-triac with sensing of mains timing could be a neat unit to have, as long as it was really well isolated from the Arduino (or other tinkerers' platform) and from the fingers (etc) of the 'tinkerers' themselves.
Absent that, an SSR seems to me to tick the right boxes.

Domestic mains electricty at 400 Hz? Really? Where about would that be??

Well, you got me on that one. It seems my memory has misled me. 400Hz is used in certain systems, but not as a municipal/utility power grid due to efficiency issues. So with 400Hz support, this project can be used in certain aerospace applications. :smiley:

It's just as well. The performance of the code at 400Hz wasn't very good, and now I don't have to be annoyed about it.

Ryan, I am concerned about the performance of the code with other things going on too - and it will suffer if much more than a few very simple tasks are being done. I hadn't thought of a good way to handle that as of last night. But since you mentioned the software interrupt, I'm rolling that around in my head now. We'll see what I can come up with over the weekend. I should know better than to say "NEW CODE IS FINISHED!!" Is software every really finished? :wink:

OK, I am really stoked with this version of code! Oddly enough, some of it boiled back down to something similar to what you were doing originally. But it has the following features now (again):

o Four independent output channels with their own inputs.
o Interrupt driven triggering for timing accuracy, even with other code in loop()
o Tracks changes in the line frequency and adjusts accordingly
o Tested with up to 512 steps of dimming resolution (could go higher? max is dependent on line freq, YMMV)

Here's a couple thoughts that may save a few clock cycles:

  1. The triac pulse is 6us in the current code. Removing the 2us delay reduces it to a 4us pulse. How long does the pulse need to persist to properly fire the triac?
  2. If we can leave the triac pins high until the next zero cross, we can save even more clocks.
/*
  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
 */
 
/*
  Modified by Mark Chester <mark@chesterfamily.org>
  
  to use the AC line frequency (half-period) as a reference point 
  and fire the triacs based on that plus a count of dimming steps.
  Tracks the line frequency and adjusts accordingly.  Can set up to
  an estimated 512 steps of dimmer resolution.
  
*/

#include <TimerOne.h>                                  // http://www.arduino.cc/playground/Code/Timer1

// General
unsigned long int ZeroXTime[4] = {0,0,0,0};            // Timestamp in micros() of the zero crossing interrupts
unsigned long int DimStep;                             // How many micros() in each step of dimming
unsigned long int AvgPeriod;                           // The average line voltage period in micros()
unsigned long int PeriodResync = 3000;                 // Number of milliseconds between line freq measurements
unsigned long int ResetPeriod = PeriodResync;          // The timestamp in millis() when we will measure the period again
unsigned long int DimRes = 256;                        // How many steps of dimmer resolution
volatile unsigned long int DimStepCounter;             // For counting Timer1 interrupts
volatile unsigned long int FireTriac[4] = {0,0,0,0};   // When it's OK to fire the triacs, in counts of DimRes
volatile boolean zero_cross = 0;                       // Tels us we've crossed the zero line
byte TriacPin[4] = {4,5,6,7};                          // Which digital IO pins to use

void setup() {                                         // Begin setup
  Timer1.initialize(DimStep);                          // Start up the Timer1 timer
  attachInterrupt(0, zero_cross_detect, FALLING);      // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  pinMode(TriacPin[0], OUTPUT);                        // Set the Triac pin as output
  pinMode(TriacPin[1], OUTPUT);                        // Set the Triac pin as output
  pinMode(TriacPin[2], OUTPUT);                        // Set the Triac pin as output
  pinMode(TriacPin[3], OUTPUT);                        // Set the Triac pin as output
  measure_half_period();                               // Initially measure the half period
}                                                      // End setup

void measure_half_period() {
  zero_cross = 0;                                      // Clearing this here increases the accuracy of the measurement
  byte F = 0;                                          // Frequency counter counter  ;)
  while ( F < 4 ) {                                    // This loop takes 4 zero cross samples
    if ( zero_cross ) {                                // Only run if a zero cross is detected
      ZeroXTime[F] = micros();                         // Set the new current zero cross time in micros()
      zero_cross = 0;                                  // Reset zero_cross
      F++;                                             // Bump the counter for the next sample
    }
  }                                                    // Now we calc the length of each DimStep
  DimStep = (((ZeroXTime[1]-ZeroXTime[0]) + (ZeroXTime[2]-ZeroXTime[1]) + (ZeroXTime[3]-ZeroXTime[2])) / 3) / DimRes;
  Timer1.attachInterrupt(fire_triacs, DimStep);        // (Re)Associate fire_triacs() with the Timer1 interrupt and the latest DimStep period
  ResetPeriod = ResetPeriod + PeriodResync;            // Set the next time when we'll measure the half period again
}
  
void zero_cross_detect() {                             // function to be fired at the zero crossing
  zero_cross = 1;                                      // set a variable that's picked up later
  DimStepCounter = 0;                                  // Reset the step counter for the next round of triac firings
}

void fire_triacs() {                                   // Called every DimStep (Timer1 interrupt, checks FireTriac[n] and fires if it's time
  if ( FireTriac[0] == DimStepCounter ) {              // Is it time to fire?
    digitalWrite(TriacPin[0], HIGH);                   // Fire the Triac mid-phase
    delayMicroseconds(2);
    digitalWrite(TriacPin[0], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  if ( FireTriac[1] == DimStepCounter ) {              // Is it time to fire?
    digitalWrite(TriacPin[1], HIGH);                   // Fire the Triac mid-phase
    delayMicroseconds(2);
    digitalWrite(TriacPin[1], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  if ( FireTriac[2] == DimStepCounter ) {              // Is it time to fire?
    digitalWrite(TriacPin[2], HIGH);                   // Fire the Triac mid-phase
    delayMicroseconds(2);
    digitalWrite(TriacPin[2], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  if ( FireTriac[3] == DimStepCounter ) {              // Is it time to fire?
    digitalWrite(TriacPin[3], HIGH);                   // Fire the Triac mid-phase
    delayMicroseconds(2);
    digitalWrite(TriacPin[3], LOW);                    // Turn off the Triac gate (Triac will not turn off until next zero cross)
  }
  DimStepCounter++;                                    // This counter increments every time fire_triacs runs
}

void loop() {                                          // Main Loop
  if ( millis() >= ResetPeriod ) {                     // Measure the half period every PeriodResync milliseconds to prevent drift
    measure_half_period();
  }
    FireTriac[0] = (DimRes * analogRead(0)) / 1024;    // Read input and calc the next triac fire time
    FireTriac[1] = (DimRes * analogRead(1)) / 1024;    // Read input and calc the next triac fire time
    FireTriac[2] = (DimRes * analogRead(2)) / 1024;    // Read input and calc the next triac fire time
    FireTriac[3] = (DimRes * analogRead(3)) / 1024;    // Read input and calc the next triac fire time
}

Could I join in you guys?

I am trying to build the similar circuit for my power control project with the arduino. But I use high current triac (15A MAC15SD) for my power monster. For safe propose, I use two opto-isolators (M11A1 and MOC3063), which one is for zero crossing detector and the other one is for triac, to separate AC and DC sides, although AC and DC circuits are on the same small solder less breadboard. But AC circuit is AC, and DC is only DC. My reference circuit comes from here. (Keyword: fairchild AN-3006.pdf)(Sorry, I can't post web address when my first post is. I will add it later)

I adjusted several pars of this circuit to suit my need. One 555 timer is used in my circuit. I think a outside timer is better than arduino. According to my previous tests, arduino works at high speed (less 10 microseconds. Min limitation is 3 or 4 usec). I often found a lot noise from circuits. But I am not sure it comes from arduino or not.

Reminder: to use isolator transformer (120v to 120v) before oscilloscope's probes contact your AC circuit, if you are new. Otherwise, the circuit will burn out!!! Due to AC circuit and the oscilloscope don't share the same ground. I burned my circuit three times. :cry:

To ryanjmclaughlin: for buzz from the triac, you can probably use a small capacitor to filter it, like the upper reference circuit. An inductor will let ac phase changed. Or noise comes from arduino?

To Koyaanisqatsi: I tried to compile your latest code(Version: April 8). I got a error from this line.
"ZeroXTime[F] = micros(); "
Could you please let me know what value of micros() is?

To that_chap
Your advice is a good ideal for several applications which don't need phase control. Some projects like mine, I have to get continuous power output, not discrete power output. They are totally different. Fast on and off switch doesn't suit for phase control applications, like lamp dimming. I have done this switch circuit with my arduino. I captured zero crossing signal, synchronized the signal, and use PWM function on the arduino to generate a fast on-off switch. I did it. I can even control output power very accurate. Unfortunately, I get a flickering light. We need a phase control , not a high speed switch.

The reference circuit website is:
www.fairchildsemi.com/an/AN/AN-3006.pdf
:slight_smile:

Hi bluefish,

Here's the description of micros(). You may need a minimum version of the IDE for it to work. I'm running 0013.
http://arduino.cc/en/Reference/Micros

Please let us know if you are able to use the code. That was my first real project and I'm very curious to know how it works for people.

Cheers!

To koyaanisqatsi:
For DelayMicroseconds function
http://arduino.cc/en/Reference/DelayMicroseconds

"This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times."

2usec is too small for arduino. I will try 2usec first and let you know what I get. But I suggest we use 6 usec or up. (Twice of the mini level of arduino)

Although I used 5usec for my previous tests, the strength of pulses is very difficult to trigger triac. Due to weak pulse signal, a lot of noise is observed in my circuit.