Pages: 1 [2] 3 4 ... 8   Go Down
Author Topic: Arduino AC Power Shield!  (Read 27431 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you have the time, I'm curious what would need to be changed for this to work with 240V/50Hz. Are you sill using the same schematic from your other post?

I've been looking at doing some lighting control myself and have been researching building something like this.

Cheers,

hads
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I know you are not directing this query to me, but using ONE Arduino digital output to drive a SEPERATE bolt down SSR means you buy just one thing - a 120 volt or a 240 volt SSR at whatever rating you need: 10A, 25A  or 40A

This would be a safe, no brainer approach except for one small detail:
opto-isolated zero crossing SSRs don't come cheap.
Sadly.

Brian Whatcott
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I agree an SSR would be ideal, except in this particular instance I'd like the ability to dim lighting.
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 74
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hads,

240V/50Hz...  is that 120V on 2 legs?  I am not that familiar with that power.

Check out the data sheet for the opto-triac drivers.  http://www.fairchildsemi.com/ds/MO%2FMOC3020M.pdf it has some 240V examples.  Basically the same setup with some different resistor values between the opto-coupler and triac.  The zero-cross can stay the same, just ensure you adjust the resistor value for the rating of the H11AA1 maybe 50k?

Remember, if you want to switch high current loads with this, your PCB needs to have large traces and a heavy copper layer to handle the current, even though the triac may be rated for 12A make sure the interconnects are as well!
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 74
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

betwys,  I agree I use alot of SCR's at work.  Like you said, the big downside is cost, and sometimes space.  I really built this for someone that needs something low cost and simple to hook up.  AC is a strange world.

This circuit is just about the same thing that goes inside a SCR, it is really just making the cost less by breaking it down and giving some more control.

I am working on the final version and am leaning towards a separate board rather than working with a shield.  I personally love the shield idea, but I know it can strick some high voltage fears in people.
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 74
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the current UNTESTED code...  let me know if you have any better ideas of doing this...  I hope to test this tonight or tomorrow.

Code:
/*
  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[4], j;               // Variable to use as a counter
volatile boolean zero_cross[4] = {0,0,0,0};  // Boolean to store a "switch" to tell us if we have crossed zero
int AC[4] = {4,5,6,7};          // Setup the AC output pins
int ACenable[4] = {1,1,1,1};    // Enable dimming for this output
int output[4] = {64,64,64,64};  // Create output vars for Dimming level (0-128)  0 = on, 128 = 0ff
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[0], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[1], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[2], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[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
  Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
  Timer1.attachInterrupt(output_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.  
  Serial.begin(9600);                                        
}                                                   // End setup
  
void zero_cross_detect() {                 // function to be fired at the zero crossing                          
    zero_cross[0] = 1;                     // set the boolean to true to tell our dimming function that a zero cross has occured
    zero_cross[1] = 1;
    zero_cross[2] = 1;
    zero_cross[3] = 1;
}                                          // End zero_cross_detect

void output_check() {                      // Function will fire the triac at the proper time

for( j=0; j<4; j++ ) {                     // Loop this function for each one of the outputs
  if(zero_cross[j]) {                      // First check to make sure the zero-cross has happened else do nothing
    if(i[j] >= output[j] && ACenable[j]) { // Check and see if i has accumilated to the dimming value we want
      digitalWrite(AC[j], HIGH);             // Fire the Triac mid-phase
      delayMicroseconds(2);                // Pause briefly to ensure the triac turned on
      digitalWrite(AC[j], LOW);              // Turn off the Triac gate (Triac will not turn off until next zero cross)  
      i[j]=0;                              // If we fired the triac, reset the counters
      zero_cross[j] = 0;                   // Reset the zero cross detection
    } else {
      i[j]++;                              // if nothing is done incriment th counter
    }
  }                                       // End zero_cross check
}                                         // End each loop
}                                         // End output_check function

void loop() {                        // Main Loop
 
  output[0] = 75;                    // Set output values for dimming
  output[1] = 25;
  output[2] = 50;
  output[3] = 100;

  Serial.print("Output Values:   ");
  Serial.print(output[0]);
  Serial.print("\t");
  Serial.print(output[1]);
  Serial.print("\t");
  Serial.print(output[2]);
  Serial.print("\t");
  Serial.print(output[3]);
  Serial.print("\n");
}

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ryanjmclaughlin,

Thanks for the pointer, I should have read the data sheet more carefully - you're right the info is there.

To clarify, it's 230-240V/50Hz, used in New Zealand, Australia and the Pacific.

I've done a little work with HV before but not a huge amount, I'd be installing on a separate board in a box isolated from the Arduino. More than happy to work together if you're interested.

Cheers,

hads
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 74
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hads,

Let me know if you can do any testing.  there may be a good resistor combination for both power systems.  e-mail me an I can show you some of the designs for the next board.  I am used to doing HV, but new at micros.

what is the voltage on the line for you?  I know in US what we call 220V is actually 4-conductors...  120V, 120V, Neutral, GND.  Is your power the same way? or 240V, Neutral, Ground?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ryanjmclaughlin,

Our 240V is 3 conductor, 240V, Neutral, Ground.

I'll drop you an email regarding testing.

Cheers,

hads
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Another red herring (probably) for the person that wants to dim lighting with no drama.....
All the stores carry light dimmers - around 700 watts for around $10.
This is relatively attractive pricing. The potentiometer dimmer control would not be hard to automate with a smoothed analog output, I suspect!

Brian W
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 74
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The components on this board are exactly what is in the hardware store dimmers...  My current prototype...  800W * 4 channel ~$30
Logged

Hillsboro, Oregon, USA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
Doing strange things with electricity since 1980!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I would suggest trying to design the shield so that another of the same type could be stacked on top. That would give the user the ability to control 8 lights, in a 4 x 2 configuration (with 2 AC inputs).

Otherwise, I'll be definitely putting this on my "shields to buy" list!
Logged

Join the fun! - DorkbotPDX & Make:PDX (Portland, Oregon)

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 74
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the modified and tested code, it works good!  I just need to add an inductor to the board to get rid of some of the triac buzz.

Code:
/*
  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[4], j;                        // Variable to use as a counter
volatile boolean zero_cross[4] = {0,0,0,0};  // Boolean to store a "switch" to tell us if we have crossed zero
int AC[4] = {4,5,6,7};                       // Setup the AC output pins
int ACenable[4] = {1,1,1,1};                 // Enable dimming for this output
int output[4] = {0,0,0,0};                 // Create output vars for Dimming level (0-128)  0 = on, 128 = 0ff
int dim = 0;                                 // Dimming level (0-128)  0 = on, 128 = 0ff
int freqStep = 59;                           // 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[0], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[1], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[2], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[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
  Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
  Timer1.attachInterrupt(output_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.  
  Serial.begin(9600);                                        
}                                                   // End setup
  
void zero_cross_detect() {                 // function to be fired at the zero crossing                          
    zero_cross[0] = 1;                     // set the boolean to true to tell our dimming function that a zero cross has occured
    zero_cross[1] = 1;
    zero_cross[2] = 1;
    zero_cross[3] = 1;
}                                          // End zero_cross_detect

void output_check() {                        // Function will fire the triac at the proper time

  for( j=0; j<4; j++ ) {                     // Loop this function for each one of the outputs
    if(zero_cross[j]) {                      // First check to make sure the zero-cross has happened else do nothing
      if(i[j] >= output[j] && ACenable[j]) { // Check and see if i has accumilated to the dimming value we want
        digitalWrite(AC[j], HIGH);           // Fire the Triac mid-phase
        delayMicroseconds(2);                // Pause briefly to ensure the triac turned on
        digitalWrite(AC[j], LOW);            // Turn off the Triac gate (Triac will not turn off until next zero cross)  
        i[j]=0;                              // If we fired the triac, reset the counters
        zero_cross[j] = 0;                   // Reset the zero cross detection
      } else {
        i[j]++;                              // if nothing is done incriment th counter
      }
    }                                        // End zero_cross check
  }                                          // End each loop
}                                            // End output_check function

void loop() {                        // Main Loop
  for( int k=0; k < 128; k++ ) {     // Slowly fade out all channels at the same time
    output[0] = k;
    output[1] = k;
    output[2] = k;
    output[3] = k;
    
    Serial.print("Output Level:\t"); // Print the current output level over serial
    Serial.println(output[0]);
    
    delay(200);                      // Delay after each step to slow down the fade
  }
}

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
I Love YaBB 2!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

How about boxing it so that only the arduino pins are accessible?  You could possibly even pot the whole board so that the header pins stick out the bottom and the next-layer header sockets stick slightly out the top.  Then one ac power cord sticks out the 'in' side and four ac power cords stick out the 'out side'.  Nothing says shields have to be bare circuit boards.
Logged

Atascadero, CA
Offline Offline
Sr. Member
****
Karma: 2
Posts: 468
Arduino addict
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ryan,

OK, this is my first time ever offering a big code change to someone so I hope this works out.    smiley

I took your latest posted code and modified it to fire the triacs based on the zero crossing moment plus some delay as calculated by the dimmer level and a dimmer resolution value (all in microseconds).  This removed the software timer-based interrupt.  I felt that statically dividing the line frequency would drift over time and the dimmer would slowly get lower or brighter, depending on the slight variance in the Arduino's clock and possible line frequency fluctuations. This change also allows support for almost any line frequency, even if it's changing radically and you don't have to think about it.

I'd _really_ like it if one of the more seasoned programmers would take a look at this and make sure I didn't do anything obviously wrong (which I'm pretty sure I did).  I know there is some issue with variables used in interrupt-triggered functions as needing to be declared as volatile or something.  But I'm not sure when that need to be done.

Enjoy!

UNTESTED CODE!!  (but it does compile)
Code:
/*
  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.
  It also tracks the rollover of the internal microseconds counter
  to avoid the glitch that would arise about every 70 minutes otherwise.
  
  THIS CODE IS UNTESTED!!!
  Use at your own risk.

  But I would love to hear whether/how well this works!  :)
*/

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

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
int uRollover;                               // A value that can go negative when micros() rolls over
unsigned long int XTimePeriod;               // The calculated micros() between the last two zero crossings
int DimResolution = 128;                     // How many levels of dimming
unsigned int DimStep;                        // The number of micros() in each step of DimResolution
unsigned long int LastTriacFire;             // Timestamp in micros() of the last time the triacs were fired
unsigned long int NextTriacFire;             // Timestamp in micros() when it's OK to fire the triacs again.
boolean WaitForRollover = 0;                 // Used for when the triac fire delay calculates out to a number beyond the rollover value of micros()

volatile int i[4], j;                        // 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[4] = {4,5,6,7};                       // Setup the AC output pins
int ACenable[4] = {1,1,1,1};                 // Enable dimming for this output
int output[4] = {0,0,0,0};                   // Create output vars for Dimming level (0-128)  0 = on, 128 = 0ff
int DimLevel = 0;                            // Dimming level (high value = more dim, low value = more light)

void setup() {                                      // Begin setup
  pinMode(AC[0], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[1], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[2], OUTPUT);                           // Set the Triac pin as output
  pinMode(AC[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
  Serial.begin(9600);                              
  delay(50);                                        // Give interrupt 0 some time to catch a few AC cycles
}                                                   // End setup
  
void zero_cross_detect() {                                   // function to be fired at the zero crossing
  ZeroXTime2 = ZeroXTime1;                                   // shift the current zero cross value to the previous
  ZeroXTime1 = micros();                                     // set the new current zero cross time in micros()
  if (ZeroXTime2 > ZeroXTime1){                              // Trap when micros() rolls over
    uRollover = ZeroXTime2 - 4294967295;                     // Subtract the rollover value to get a negative number
    XTimePeriod = ZeroXTime1 - uRollover;                    // And calc XTimePeriod from that
  }
  else {                                                     // Otherwise just calc as usual
    XTimePeriod = ZeroXTime1 - ZeroXTime2;                   // Calculate the time since the last zero crossing
  }
  DimStep = XTimePeriod / DimResolution;                     // Determine how many micros() are in each step of dimming level
  if ( (ZeroXTime1 + (DimLevel * DimStep)) > 4294967295 ) {  // If this pushes the value of NextTriacFire past the micros() rollover
    WaitForRollover = 1;                                     // Then set a flag to watch for.
  }
  NextTriacFire = ZeroXTime1 + (DimLevel * DimStep);         // Calc the next triac fire time
  zero_cross = 1;                                            // set the boolean to true to tell our dimming function that a zero cross has occured
}                                                            // End zero_cross_detect

void loop() {                                                // Main Loop

// Set DimLevel in some fashion. This just sets it to random values.
  randomSeed(analogRead(0));                                 // Initialize the randomizer EVERY TIME (this is not a good thing to do)
  DimLevel = random(DimResolution);                          // Pull a number out of the hat that is <= DimResolution
  Serial.print("Output Level:\t");                           // Print the current output level over serial
  Serial.println(DimLevel);

// DimLevel is used to set NextTriacFire in zero_cross_detect

// NextTriacFire is used here
  if ( WaitForRollover && ( micros() < NextTriacFire ) ) {
    WaitForRollover = 0;
  }
  if ( zero_cross && !WaitForRollover ) {                    // Did we cross zero?
    if ( micros() >= NextTriacFire ) {                       // Check to see if it's time to fire the triac
      for( byte j=0; j<4; j++ ) {                            // For each one of the outputs
        digitalWrite(AC[j], HIGH);                           // Fire the Triac mid-phase
        delayMicroseconds(2);                                // Pause briefly to ensure the triac turned on
        digitalWrite(AC[j], LOW);                            // Turn off the Triac gate (Triac will not turn off until next zero cross)
        zero_cross = 0;                                      // Reset the zero cross detection
      }
    }
  }
}
Logged

What about elevensies? Luncheon? Afternoon tea? Dinner? Supper?

Pages: 1 [2] 3 4 ... 8   Go Up
Jump to: