Short version: here's some sample PowerSSR Tail code in case anyone finds it useful.
Long version:
My wife is interested in a wake-up light (sunrise light). My original version used LEDs. But it wasn't bright enough.
Version 2 will use a PowerSSR Tail + ZeroCross Tail to manage the dimming of store-bought lights. Trying to save power, I'm using store-bought A19 LED lights. With the sample PowerSSR Tail + ZeroCross Tail code, I was getting some flickering when getting close to full power. The light is driven remotely via an XBee, and I was also losing data coming from the XBee sender.
So I created the following test code to:
- Reduce the number of interrupts used to manage the PowerSSR tail. The following code uses one interrupt callback per zero-cross to drive the PowerSSR instead of multiple callbacks.
- Remove the flicker when close to full power.
- Use a dimming scale that was more logical to me.
I also changed the timer library, to make it easier to switch over to a new interrupt timer if needed. As for the loss of serial data, that was finally solved by moving the XBee handling from SoftwareSerial to AltSoftSerial.
Anyway, here's some sample code in the hopes that the next newbie that gets a PowerSSR Tail gets it running faster than I did. Now that's it's running, I'm pretty happy with the PowerSSR. Now I just need to solder up a final solution and enclose it.
I'm new to the hardware side of things, so any suggestions are appreciated.
/*
Dim_PSSR_ZC_Tail_2
This sketch is a sample sketch using the ZeroCross Tail(ZCT)to generate a sync
pulse to drive a PowerSSR Tail(PSSRT) for dimming ac lights.
Connections to an Arduino Duemilanove:
1. Connect the C terminal of the ZeroCross Tail to digital pin 2 with a 10K ohm pull up to Arduino 5V.
(or whatever pin is used for interrupt 0 on your Arduino. For the MICRO it's pin 3.)
2. Connect the E terminal of the ZeroCross Tail to Arduino Gnd.
3. Connect the PowerSSR Tail +in terminal to digital pin 4 and the -in terminal to Gnd.
This sketch differs from the original Dim_PSSR_ZC_Tail by:
- Setting the timer interrupt to fire when the PowerSSR should be fired, instead of looping repeatedly.
The main goal was to reduce interactions with other code that may disable interrupts.
- Uses a different scale, with 0 = off and 100 = full power.
- Avoids firing the PowerSSR too close to the zero-cross. Firing the PowerSSR at the zero-cross gives smooth light.
But flickering occurred with LED lights when the PowerSSR was set to fire between ~600us from the zero-cross events.
So dim values of 1 through 99 fire between 600us and 7600us from the zero cross.
- Uses Timer1.h instead of TimerOne.h
*/
#include <Timer1.h>
const int PSSR1 = 4; // PowerSSR Tail connected to digital pin 4
int dim = 0; // Default dimming level (0-100) 0 = off, 100 = on
// For 60 Hz mains, zero-crosses occur twice per cycle. Or at 120Hz.
// In microseconds (best Arduino timing), that equates to a zero-cross every 8333 microseconds (1,000,000 / 120).
// Empirically, if the PowerSSR is fired too close to the zero-cross it doens't always take, causing the lights to flicker.
// The sweet spot seems to be between 500us and 7700us. To be safe, we'll fire the PowerSSR between 600us and 7600us.
// That gives us a range of 7000us, or 70us for each step of dimming.
const int minTimerDelay = 600;
const int timerDelayStep = 70;
void setup()
{
Serial.begin(9600);
pinMode(PSSR1, OUTPUT); // Set SSR1 pin as output
attachInterrupt(0, zero_cross_detect, RISING); // Attach an Interupt to digital pin 2 (interupt 0),
}
void printDimLevel() {
Serial.print("dim: ");
Serial.print(dim);
Serial.println();
}
void loop() // Main loop
{
for (dim = 0; dim <= 100; dim++) {
printDimLevel();
delay(100);
}
}
// Functions
void zero_cross_detect()
{
pauseTimer1(); // In case the timer did not fire yet (other interrupts), skip it.
if (dim == 100) {
// Full power: no delay
//Fire the PSSR Tail.
delayMicroseconds(100);
digitalWrite(PSSR1, HIGH);
delayMicroseconds(50);
digitalWrite(PSSR1, LOW);
}
else if (dim > 0) {
// Calls timer1Event when it's time to fire the PowerSSR.
// The longer we wait, the less power will be delivered to the light until the PowerSSR turns off at the next zero-cross,
// and the dimmer the light will be.
startTimer1(minTimerDelay + (timerDelayStep * (100 - dim)));
}
// Dim level 0 (off): don't fire PowerSSR
}
ISR(timer1Event)
{
// Don't need to pauseTimer1(). Timer is stopped after the interrupt fires until explicitly reset.
// Fire the PSSR Tail.
// Initial delay taken into account by the timer's min delay.
digitalWrite(PSSR1, HIGH);
delayMicroseconds(50);
digitalWrite(PSSR1, LOW);
}