Zero-Cross Detection, AC Motors, and Bluetooth issue

I have a broad question about how the Arduino, loops, and how timing work. Im a newbie, so don't assume Im doing this right.

The concept:
I have an AC fan motor that is powered by an opto-isolated TRIAC circuit that is driven by a Zero-Cross detection circuit. I want to control the FAN via Bluetooth... sounds simple?!

The Problem:
If I run the fan by itself and use a 3k pot to dim/slow the fan it works perfect... As soon as I add serial data, i.e. the Bluetooth it starts to loose timing and the fan flickers/stutters.

What I believe is happening is that the serial data, and any other functions, cause a delay in the AC zero-cross timing. Im not sure if there is a way to get around this since there is only one loop... Any ideas, I feel that the most stable way to do this is to have to arduinos, one dedicated to the AC motor and the other could control all other functions.

Take a look at the code below, The first works great, second has all the added functions and BT serial comm... this is the problem code.

Basic Zero-Cross and dimming code, works perfect:

/*
Purpose: to detect zero crossing pulse at 
INT0 digital pin 2, which after delay determined by POT on analog A0
switches on  a triac. 

Power output to triac activated by external switch.
*/

#define triacPulse 5
#define SW 4
#define aconLed 13 

int val;

void setup()  {
   pinMode(2, INPUT);
   digitalWrite(2, HIGH); // pull up
   pinMode(triacPulse, OUTPUT);
   pinMode(SW, INPUT);
   digitalWrite(SW, HIGH);
   pinMode(aconLed, OUTPUT);
   digitalWrite(aconLed, LOW);
   
  }

void loop() {
  // check for SW closed
  if (!digitalRead(SW))   {
    // enable power
    attachInterrupt(0, acon, FALLING); 
    // HV indicator on
    digitalWrite(aconLed, HIGH);
  }  // end if
     else if (digitalRead(SW)) { 
       detachInterrupt(0); // disable power
       // HV indicator off
       digitalWrite(aconLed, LOW);
     }  // else
     
  } // end loop
   
   

// begin ac int routine
// delay() will not work!
void acon()  
   {
    if (analogRead(0) > 50)
    {
    delayMicroseconds((analogRead(0) * 6) + 1000); // read AD1 
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200);  
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
    }
    else 
    {
    delayMicroseconds(1300); // read AD1 
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200);  
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
    }
   }

This is code w/Bluetooth and some other routines added, this is the problem code (removed variables for Blog size limits):

void setup()
{
// LCD control
lcd.begin(16, 2);                 // LCD setup
lcd.clear();                      // LCD, initial clear
// End LCD Control
  
// Initialize set Target temp
globalAnalogReadVariable = (analogRead(getTempInputPin));      // gets pot reading and sets it into an integer
globalTargetTemp = (globalAnalogReadVariable / 4.052) + 150;   //sets temp threshold integer
globalTargetTemp = ((globalTargetTemp+4)/5)*5;                 // round to nearest 5
//analogTargetTemp = globalTargetTemp;
// End Initialize set Target temp

// Set some variables
serialB = 0;  // used in BT comm
//btTempChange = false;   // sets the BT boolean to FALSE.

// End 


   /* Disabled
   // AC Speed control section
   pinMode(2, INPUT);
   digitalWrite(2, HIGH); // pull up
   pinMode(triacPulse, OUTPUT);
   // End AC speed control section
   */
   
   pinMode(8, OUTPUT);
   pinMode(9, OUTPUT);
   pinMode(6, OUTPUT);                // Used for the lamp output to mimic heat
   pinMode(13, OUTPUT);
   Serial.begin(115200);              // serial print window initialize default 9600

   
   
}

void loop()  // Main Loop
{
  processTemps(); // Call funtion to process ALL temp info
  
  processHeatingElement(); // Call to function to turn on Lamp based on temperature
  
  blueToothComm1(); // Call to BT function 1

  processSerialWindoData(); // Call to function to show data in Serial window
  
  // speedControl(); // Call the function to control motor speed  ***** TEMPORARY DISABLED, issue w/ timing *****
  
  LCDdisplayTemps(); // displays current temp on LCD  
    
  delay(1000); // repeat once per second (change as you wish!)  

} // End Loop


/*
void speedControl() // Speed & Motor control function
{
    attachInterrupt(0, acon, FALLING); 
}
*/


void processSerialWindoData()
{
  Serial.print(grillTemp);
  Serial.print(":");
  Serial.println(globalTargetTemp);
}


void processTemps()
{
  int localAnalogReadVariable;                                // Local temp variable to check if state of pot has changed
  int LA;
  int GV;
  int GVup;
  int GVdn;
  localAnalogReadVariable = (analogRead(getTempInputPin));    // assign temp var the POT analog pin value
  
  voltage = getVoltage(temperaturePin);

  degreesC = (voltage - 0.5) * 100.0;
  
  degreesF = degreesC * (9.0/5.0) + 32.0;
  
  // grillTemp = ((degreesF + grillTemp) / 2);                    // replace with if statement below 
  
  
  if (grillTemp >= MinGrillTemp)
  {
    grillTemp = ((degreesF + grillTemp) / 2);                     // used to average the output
    //grillTemp = degreesF + 200;                                 // Test to LED output based on temp *** REMOVE +200 only for testing
  }
  else
  {
  grillTemp = 150;                                            // Used for error handling, in the future we want to add text "Heating" until it reaches 150 deg
  }
 

  localAnalogReadVariable = (analogRead(getTempInputPin));    // assign temp var the POT analog pin value
   LA = localAnalogReadVariable;
   GV = globalAnalogReadVariable;
   GVup = globalAnalogReadVariable +50;
   GVdn = globalAnalogReadVariable -50;

 
 if (LA >= GVup || LA <= GVdn)
 { 
    globalAnalogReadVariable = (analogRead(getTempInputPin));                     // gets pot reading and sets it into an integer
    globalTargetTemp = (globalAnalogReadVariable / 4.052) + 150;                  //sets temp threshold integer 
   
    globalTargetTemp = ((globalTargetTemp+4)/5)*5;                                // round up to nearest multiple of 5 
  }
  
}


void LCDdisplayTemps()
{
  lcd.clear();
  lcd.print("Grill Temp   "); 
  lcd.print(grillTemp); 
  lcd.setCursor(0,1);
  lcd.print("Target Temp  "); 
  lcd.print(globalTargetTemp); 
   
}

/*
// begin ac int routine
// delay() will not work!
void acon()  
   {
    delayMicroseconds((analogRead(0) * 6) + 1000); // read AD1 
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200);  
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
   } 
*/   
   
  
float getVoltage(int pin)  // gets voltage from analog pin and converts it
{
   return (analogRead(pin) * 0.004882814);
}



void blueToothComm1()
{
if (Serial.available() > 0)
  {
  serialRead = Serial.read();
  if (serialRead < 50)
    {
     serialA = serialRead;
     //Serial.print("Serial A = ");
     //Serial.println(serialRead);
    }
  else if (serialRead >= 50)
    {
     globalTargetTemp = (serialRead * 5) - 100;
    }
  }

  switch (serialA) {
    case 1:
      digitalWrite(BTledPin, HIGH);
      break;
    case 2:
      digitalWrite(BTledPin, LOW);
      break;
    case 3:digitalWrite(BTledPin, HIGH);
      delay(100);
      digitalWrite(BTledPin, LOW);
      delay(100);
      default:
      break;

  }
}



void processHeatingElement()
{
  
  GtHi = globalTargetTemp; 
  GtLo = globalTargetTemp;
  
  if (grillTemp < GtLo + GrillTempRange_Lo) // Red & Yellow LED 
    { 
      digitalWrite(6, HIGH);   // Turn on Lamp, Used for the lamp output to mimic heat.
    
    } 
  if (grillTemp > GtHi + GrillTempRange_Hi) // Yellow LED
    {
      digitalWrite(6, LOW);   // Turn off Lamp, Used for the lamp output to mimic heat.
    } 
}

Post a schematic.
Better yet. Trash the triac circuit and get a Solid state relay. It has that built in. It's idiot proof. All it needs is a dc voltage you can generate with a $5 DAC

FYI, you can't use software for a zero-crossing detector. You need to buy an opto isolator that has one built in.
It has to be done with hardware.

I've built a similar system that uses 433Mhz radio control using the RCSwitch library and it works OK

Looking at your code, the one thing that springs to mind is that you are using delay inside your acon() ISR, this seems a bad idea. Also you have a lot of code in your ISR

I can understand small delays inside the ISR to toggle the input to the triac for long enough for it to trigger, but you seem to be delaying by quite possibly very long periods of time.
i.e

delayMicroseconds((analogRead(0) * 6) + 1000)

is potentially 6 * 1023 + 1000 uS i.e just over 7mS

is the acon() ISR function is called when you get a zero crossing, I recommend that rather than delaying inside the ISR that you use a timer which then triggers another ISR which then just toggles the input to the triac.

One other thing you may encounter (I did), is that the zero crossing detection doesnt occur precisely at the zero crossing, mine happened a small time before the real zero point.

So I ended up needing another timer to delay from the zero crossing detection to the real zero point, and then run the timer again to do the delay.
I can't quite remember why (as I built my system over a year ago), but I couldn't simply add some compensation to the timer to factor in the zero crossing inaccuracy.

BTW.
I ended up using Arduino Pro Micro as the ATMega32U4 has an additional hardware timer that the ATMega328 doesn't have.

@raschemmel

DAC for zero crossing detection sounds problematic. You'd need to constantly sample it, at quite a high rate to get a decent resolution on when the zero crossing threshold was. (or am I mistaken)

Assuming that you are zero detecting mains, you'd need a step down transformer, unless the DAC can directly read mains voltage and has isolation to the Arduino

A lot of people just use a simple opto isolator for zero detection, as you can feed the i/P side via some high value resistors e.g. 4 x 100k Ohm, and you can use the internal pullup on the D2, i.e ISR 0

I used a triac as I didnt have a SSR, but I agree that an SSR is probably the best way to go if you have one to hand, just make sure it behaves like a triac and not a FET i.e stays latched, or if not, make sure the code holds the input to the SSR as long as needed (ie until the next zero crossing)

Trash the triac circuit and get a Solid state relay. It has that built in. It's idiot proof. All it needs is a dc voltage you can generate with a $5 DAC

This is what I said. What it means is that if you have a solid state relay you just turn it on period. You don't have to think about zero crossing because it is all built in .That's why it's called a solid state relay. You turn it on with a dc analog voltage period.
The arduino does not have the capability to do that because it does not have a dac , period. Buy a dac, and use that to turn on/off the fan or if you need speed control then you can send a PWM signal to the SSR and the duty cycle will control the speed. I never suggested using a dac for zero crossing . I said buy an ssr that has it built in and turn it on with the dac.

Your comments lead me to believe you have not used ssrs much because you don't have to think about whether they stay latched or not. You just put a dc voltage on the input and it turns on. That's about as much thinking as you need to spend on it.
http://www.ebay.com/itm/1pcs-SSR-25DA-Solid-state-relays-FOTEK-20A-minitype-DC-AC-one-phase-Relay-/261018742414

with safety cover

raschemmel:

Trash the triac circuit and get a Solid state relay. It has that built in. It's idiot proof. All it needs is a dc voltage you can generate with a $5 DAC

This is what I said. What it means is that if you have a solid state relay you just turn it on period. You don't have to think about zero crossing because it is all built in .That's why it's called a solid state relay. You turn it on with a dc analog voltage period.
The arduino does not have the capability to do that because it does not have a dac , period. Buy a dac, and use that to turn on/off the fan or if you need speed control then you can send a PWM signal to the SSR and the duty cycle will control the speed. I never suggested using a dac for zero crossing . I said buy an ssr that has it built in and turn it on with the dac.

Your comments lead me to believe you have not used ssrs much because you don't have to think about whether they stay latched or not. You just put a dc voltage on the input and it turns on. That's about as much thinking as you need to spend on it.
http://www.ebay.com/itm/1pcs-SSR-25DA-Solid-state-relays-FOTEK-20A-minitype-DC-AC-one-phase-Relay-/261018742414

Im very new to this so let me understand.

I have an SSR relay to control an on/off function for an AC bulb, thats simple. But do you send a PWM signal to the SSR and depending on the DC voltage applied to the SSR that regulates the AC voltage or waveform?

The best and most efficient way to regulate AC motor speed is by chopping the waveform, not sure how that is achieved w/out a zero-cross detect circuit.

As I said the the SSR has the zero crossing circuitry already in it. That means that whenever there is a dc voltage greater than 3V on the dc input the ssr turns on. If the control voltage is turned off, the ssr does not turn off the output until the zero crossing. Any change in the input is updated at the zero crossing so it NEVER switches UNDER POWER. Now , what if you put a PWM signal on the dc input ?
What is the result ? The PWM is 0V to 5V with a variable duty cycle . If you look at the input and output of an ssr controlled by a PWM
what do you suppose you are going to see on the output during those brief moments when the pwm signal is HIGH (5V) for milliseconds ?
Conversely, what will you see on the output for those moments when the input voltage is zero ? Now consider that the output is not switching dc, it is switching AC, so when the output turns on during a brief moment of the positive phase of the ac sinewave, you will see that portion of the sinewave. When it turns off during that portion of the sinewave you will see 0V. If you compare a "chopping " signal like the one you are talking about with the output signal of a pwm controlled ssr. If I show you the two scope screenshots with the labels on the backside of the paper so you can't see it, will you be able to tell the one from the pwm controlled ssr from the one with the chopping signal you are talking about if I don't tell you which is which ?

raschemmel:
As I said the the SSR has the zero crossing circuitry already in it. That means that whenever there is a dc voltage greater than 3V on the dc input the ssr turns on. If the control voltage is turned off, the ssr does not turn off the output until the zero crossing. Any change in the input is updated at the zero crossing so it NEVER switches UNDER POWER. Now , what if you put a PWM signal on the dc input ?
What is the result ? The PWM is 0V to 5V with a variable duty cycle . If you look at the input and output of an ssr controlled by a PWM
what do you suppose you are going to see on the output during those brief moments when the pwm signal is HIGH (5V) for milliseconds ?
Conversely, what will you see on the output for those moments when the input voltage is zero ? Now consider that the output is not switching dc, it is switching AC, so when the output turns on during a brief moment of the positive phase of the ac sinewave, you will see that portion of the sinewave. When it turns off during that portion of the sinewave you will see 0V. If you compare a "chopping " signal like the one you are talking about with the output signal of a pwm controlled ssr. If I show you the two scope screenshots with the labels on the backside of the paper so you can't see it, will you be able to tell the one from the pwm controlled ssr from the one with the chopping signal you are talking about if I don't tell you which is which ?

Very interesting, so i will give this a go. worth a try in my opinion. So I should control the SSR as if dimming an LED from the Arduino. On the AC side run the HOT leg through the Mains and It should dim?

Can you take a look at the SSRs I have in my stock and tell me if they may work?

Thanks raschemmel!

The two screws on the AC side of the ssr are for Interrupting (switching) the AC HOT (Line) . Neutral is NEVER swiched !

YES. JUST LIKE DIMMING LED

YES.
Those will work but may need heatsink

You're right, I've never used an SSR, but I'm still a bit confused about their use.

It sounds like the SSR you are describing is a triac in a box with an opto couple inside the box on the input side.

I cant quite see how you can "dim" an ac load without zero crossing detection

i.e unless the SSR you are describing can be modulated using a voltage between 0 to 5V which represents between 0 and 180 deg delay for switchon of the SSR

They are not normally "modulated" . As I said, they are plug and play. You put 3+Vdc on the input , and the output works like a zero crossing controlled SPST switch where all changes on the input are updated at the zero crossing. They're idiot proof . Your grandmother could use one. Just cut the hot wire and connect it to the output pins and connect dc to the input . done.

Hi raschemmel

I'm very sorry but I still don't understand why you are recommending an SSR for this use.
Perhaps we are talking at cross purposes.

I took a quick look for SSR schematics, and found the one for a cheap SSR on eBay

http://www.fotek.com.hk/solid/SSR-1.htm

And I can see that this type has zero crossing detection on the input and a triac as the power control / output.

Hence for AC it will only turn on at zero crossing and the triac will only conduct for 1/2 a cycle until the HT side goes back to zero.

However to "dim" a light with this device, (which is what I think the posted of this tread was doing)

If I run the fan by itself and use a 3k pot to dim/slow the fan it works perfect

and

Basic Zero-Cross and dimming code, works perfect:

using an SSR, you'd need to control its power in the time domain (i.e PWM) , but as the SSR only turns on at zero crossing and only turns off at the HT zero crossing point and then it turns off again at the next HT zero crossing. The minimum on time is 180 deg of the HT AC.

so for 50% power you could turn it on for half cycle and off for the next, for 25% power, you'd turn it on for half a cycle and off for the next 3 etc

But I'm not sure how smooth this would look in practice, or whether the fan motor would start to "buzz" or of you were feeding a filament lamp you'd see it flicker.

I also suspect that you'd need to somehow synchronize the "turn on" signal from the Arduino with the AC HT rather than relying on the 16Mhz clock in the Arduino, otherwise you could possibly end up with some slow "interference" pattern type effect where the Arduino slowly went in and out of phase with the mains.

I'm probably going to buy some SSR's from eBay to play with, and I'll see if I can get some without input Zero crossing detection as I'd like to be able to turn the SSR on in the middle of a half cycle

Does anyone make FET type SSR's which only stay on for the duration of the input pulse, as I have some projects where I need to turn on some resistive load devices for perhaps 1mS or less at set periods during the AC waveform

Did you ever post any schematic for your triac circuit ? I couldn't find one .
I don't think you'll be able to find SSRs without zero crossing because when you turn OFF ANY AC switching device when it is NOT at zero crossing there is a possibliity or arcing. I am not saying there are no devices that can do that I just don't happen to know which ones.
I'll get a hold of an ac fan and an SSR and play around with it. One complete ac 60hz cycle is 16.6 ms so one half cycle is 8.33 mS which
is still a very brief period of time so I don't think you are going to see an incandescent light that is on for 16 mS . Consequently, any PWM
from an arduino is going to be able to turn on /off the ssr within 8.33mS so the duty cycle will always be whatever you command +/- 8.33mS. I don't see why that is not adequate for a fan. I would like to see your triac circuit if you can post a photo of the schematic.

raschemmel:
Did you ever post any schematic for your triac circuit ? I couldn't find one .
I don't think you'll be able to find SSRs without zero crossing because when you turn OFF ANY AC switching device when it is NOT at zero crossing there is a possibliity or arcing. I am not saying there are no devices that can do that I just don't happen to know which ones.
I'll get a hold of an ac fan and an SSR and play around with it. One complete ac 60hz cycle is 16.6 ms so one half cycle is 8.33 mS which
is still a very brief period of time so I don't think you are going to see an incandescent light that is on for 16 mS . Consequently, any PWM
from an arduino is going to be able to turn on /off the ssr within 8.33mS so the duty cycle will always be whatever you command +/- 8.33mS. I don't see why that is not adequate for a fan. I would like to see your triac circuit if you can post a photo of the schematic.

Here is a schematic of my current circuit.

I breadboarded the zero-crossing detector and it works fine. The total width of the positive pulse at the zero crossing is about 1 ms but it is shaped like the top of a sinewave. when you reduce the Time/Div enough to see most of the ac sinewave then the zero crossing looks like a narrow peak in comparison to the width of the sinewave positive phase since the positive phase is 8.33 mS and the zero crossing is less than a mS (or looks like it).
Where did you get the code. Did you write that ? I was just wondering about the timing for the Triac.

 /*
Purpose: to detect zero crossing pulse at 
INT0 digital pin 2, which after delay determined by POT on analog A0
switches on  a triac. 

Power output to triac activated by external switch.
*/

#define triacPulse 5
#define SW 4
#define aconLed 13 

int val;

void setup()  {
   pinMode(2, INPUT);
   digitalWrite(2, HIGH); // pull up
   pinMode(triacPulse, OUTPUT);
   pinMode(SW, INPUT);
   digitalWrite(SW, HIGH);
   pinMode(aconLed, OUTPUT);
   digitalWrite(aconLed, LOW);
   
  }

void loop() {
  // check for SW closed
  if (!digitalRead(SW))   {
    // enable power
    attachInterrupt(0, acon, FALLING); 
    // HV indicator on
    digitalWrite(aconLed, HIGH);
  }  // end if
     else if (digitalRead(SW)) { 
       detachInterrupt(0); // disable power
       // HV indicator off
       digitalWrite(aconLed, LOW);
     }  // else
     
  } // end loop
   
   

// begin ac int routine
// delay() will not work!
void acon()  
   {
    if (analogRead(0) > 50)
    {
    delayMicroseconds((analogRead(0) * 6) + 1000); // read AD1 
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200);  
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
    }
    else 
    {
    delayMicroseconds(1300); // read AD1 
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200);  
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
    }
   }

The code explains about the switch triggering the ISR to read the zero crossing detector and then turn on the Triac after a delay determined by reading the pot connected to A0 but the schematic doesn't show any of that, just the power detect & control circuitry.
Why is that ?
Also, what is this "else" code for ?

delayMicroseconds(1300); // read AD1 
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200);  
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);

In case you want a version that uses a timer to delay the firing of the triac

Here is the code I wrote ( and it works)

#include <TimerOne.h>	// Avaiable from http://playground.arduino.cc/Code/Timer1
#include <RCSwitch.h>
#include <FlexiTimer2.h>
#include "EmonLib.h"             // Include Emon Library
EnergyMonitor emon1;             // Create an instance

#define FREQ 50 	// 50Hz power in these parts
#define AC_PIN 9	// Output to Opto Triac
#define LED 13		// builtin LED for testing
#define VERBOSE 1	// can has talk back?
#define PERCENTAGE_STEP 2

#define DEBUG_PIN 5	//scope this pin to measure the total time for the intrupt to run

#define SLOW_TRIAC 1

volatile byte state = 255;	// controls what interrupt should be 

long lastTime=0;

long  period = 1000000  / (2 * FREQ);//The Timerone PWM period in uS, 60Hz = 8333 uS
long  onTime = 0;	// the calculated time the triac is conducting

boolean percentageChanged=false;
int onPercentage;

int zeroCount=0;
int timerCount=0;
volatile boolean isResttingTimer=false;

RCSwitch mySwitch = RCSwitch();



void updateOnTime(int percentage)
{
  onTime = ((100-percentage) * period)/100  ;	// re scale the value from hex to uSec . pulse occurs 500uS after zero crossing

  percentageChanged=true;
      #ifdef VERBOSE
      Serial.print("percentage:");
      Serial.print(onPercentage);
      Serial.print("\tonTime:");
      Serial.println(onTime);
    #endif

}

void setup()
{
    Serial.begin(115200);	//start the serial port at 115200 baud we want
    Serial.println("AC Motor Control v1");	//the max speed here so any
    #ifdef VERBOSE		//debugging output wont slow down our time sensitive interrupt
    pinMode(DEBUG_PIN, OUTPUT);
    digitalWrite(DEBUG_PIN, LOW);
    Serial.println("----- VERBOSE -----");	// feeling talkative?
    #endif
    
    pinMode(AC_PIN, OUTPUT);		// Set the Triac pin as output
    pinMode(LED, OUTPUT);
    digitalWrite(LED, LOW);// LED off
    digitalWrite(AC_PIN, LOW);// Triac off
    
     //emon1.voltage(1, 190, 1.7);  // Voltage: input pin, calibration, phase_shift

    emon1.current(0, 15.93);       // Current: input pin, calibration.
   
   
   
   
    mySwitch.enableReceive(1);

    onPercentage = 50;
    updateOnTime(onPercentage);// 0% on time


 
    Timer1.initialize(onTime);
    Timer1.attachInterrupt(nowIsTheTime);
    Timer1.stop();// Dont need to start yet

    delay(1000);// Wait for zero crossing detector to stabilize
    FlexiTimer2::set(1, 1.0/2200, ZeroCrossingDelay) ;
    attachInterrupt(0, zero_cross_detect, FALLING); 	// Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
    lastTime=millis();

} 

void ZeroCrossingDelay()
{
  digitalWrite(DEBUG_PIN,LOW);
  
  FlexiTimer2::stop();
        // Can't do anything between 95 and 100% because the zero crossing interrupt arrives 500uS after zero.
   if (onPercentage >=100)
   {
     digitalWrite(AC_PIN,HIGH);// turn triac on (all the time)
     digitalWrite(LED, HIGH);
   }
   else
   {
       digitalWrite(LED, LOW);
   }
   
   if (percentageChanged)
   {
     percentageChanged=false;
     Timer1.setPeriod(onTime);
   }

   isResttingTimer=true;
   TCNT1=0;// Causes phantom interrupt. hence the line above
   
   if (onPercentage>0 && onPercentage<100)
   {
     Timer1.resume();
   }
   else
   {
       Timer1.stop();
   }
}
void zero_cross_detect()	
{		
  digitalWrite(DEBUG_PIN,HIGH);
    FlexiTimer2::start();
}
void nowIsTheTime ()
{
    if (isResttingTimer)
    {
      cli();
      isResttingTimer=false; 
      sei();
      return;
    }
    else
    {
      Timer1.stop();// stop running and wait for next Zero crossing
 
      digitalWrite(AC_PIN,HIGH);
      digitalWrite(LED,HIGH);
      #ifdef SLOW_TRIAC
        __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");  
      #endif
      digitalWrite(AC_PIN,LOW);// triac only needs to be pulsed on. Set its gate to low so that it will turn off at the next zero crossing.

    }
}
void loop() 
{			
// 5510485 ON
// 5510484 OFF
  if (mySwitch.available()) 
  {
    switch(mySwitch.getReceivedValue())
    {
      case 5510485:
      if (onPercentage<=100-PERCENTAGE_STEP)
      {
        onPercentage+=PERCENTAGE_STEP;
        updateOnTime(onPercentage);// 5% on time
      }
      break;
      case 5510484:
        if (onPercentage>=PERCENTAGE_STEP)
        {
          onPercentage-=PERCENTAGE_STEP;
          updateOnTime(onPercentage);// 5% on time
        }
      break;
    }
    mySwitch.resetAvailable();
  }
   if (millis() - lastTime > 1000)
  {
    emon1.calcVI(20,2000);         // Calculate all. No.of half wavelengths (crossings), time-out
    double Irms = emon1.calcIrms(1480);  // Calculate Irms only
  
    Serial.println(Irms*240.0);	       // Apparent power
    lastTime=millis();
  }
}

Edit. Just realized I better say what the code does.

It controls a triac in the same way, i.e has a zero crossing and then delays before triggering the triac
It receives commands via remote control on 433MHz using the RCSwitch library which interprets a couple of buttons on a wireless remote control (hence the strange numbers in the main loop)

The other thing the code does at the same time is use the eMonLib to monitor the amount of power in another AC circuit, as the main purpose of my code is to use up excess solar power by dissipating it into a large resistive load (a water heater).

My zero crossing detector is more complex than the one in the schematic, and it gives a nice square pulse, but the pulse occurs several hundred micro seconds before the real zero crossing, so the code waits for the zero crossing, then uses a timer to delay for a hard coded period (the amount the zero crossing detector is early), and then triggers a second timer to delay the triac before eventually triggering the triac.

The reason 2 timers are used is to try to achieve dimming in the area between 0% and 5% where the zero crossing detector has already triggered, (that is, the zero crossing detector triggers somewhere around 450uS before the actual zero crossing point which at 50Hz is around 5% before the zero crossing.

Hence to be able to control between 0 and 5% I needed to use time timers, as it was impossible to use one timer, as the triac trigger timer may need to expire after the zero crossing input has occurred for the next half cycle

The code came from a a website and youtube video from an electronics guru. His circuit design was fine but was a little clunky.

The schematic came from someone i worked with on fiverr. He did a quick simple design for me. The only thing missing in the POT but that will not be in the final design i just use it for manual dimming.

I know there is many ways to skin this cat, and I'm not an electrical engineer, so thats why I'm reaching out to you guys. I appreciate your help. I assume that you breadboarded the SSR circuit and had success in dimming that? I don't have my equipment with me, out of the office. I will be able to get back to it on wednesday.

raschemmel:
I breadboarded the zero-crossing detector and it works fine. The total width of the positive pulse at the zero crossing is about 1 ms but it is shaped like the top of a sinewave. when you reduce the Time/Div enough to see most of the ac sinewave then the zero crossing looks like a narrow peak in comparison to the width of the sinewave positive phase since the positive phase is 8.33 mS and the zero crossing is less than a mS (or looks like it).
Where did you get the code. Did you write that ? I was just wondering about the timing for the Triac.

 /*

Purpose: to detect zero crossing pulse at
INT0 digital pin 2, which after delay determined by POT on analog A0
switches on  a triac.

Power output to triac activated by external switch.
*/

#define triacPulse 5
#define SW 4
#define aconLed 13

int val;

void setup()  {
   pinMode(2, INPUT);
   digitalWrite(2, HIGH); // pull up
   pinMode(triacPulse, OUTPUT);
   pinMode(SW, INPUT);
   digitalWrite(SW, HIGH);
   pinMode(aconLed, OUTPUT);
   digitalWrite(aconLed, LOW);
   
  }

void loop() {
  // check for SW closed
  if (!digitalRead(SW))   {
    // enable power
    attachInterrupt(0, acon, FALLING);
    // HV indicator on
    digitalWrite(aconLed, HIGH);
  }  // end if
     else if (digitalRead(SW)) {
       detachInterrupt(0); // disable power
       // HV indicator off
       digitalWrite(aconLed, LOW);
     }  // else
     
  } // end loop

// begin ac int routine
// delay() will not work!
void acon() 
   {
    if (analogRead(0) > 50)
    {
    delayMicroseconds((analogRead(0) * 6) + 1000); // read AD1
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200); 
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
    }
    else
    {
    delayMicroseconds(1300); // read AD1
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200); 
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);
    }
   }




The code explains about the switch triggering the ISR to read the zero crossing detector and then turn on the Triac after a delay determined by reading the pot connected to A0 but the schematic doesn't show any of that, just the power detect & control circuitry.
Why is that ?
Also, what is this "else" code for ?


delayMicroseconds(1300); // read AD1
    digitalWrite(triacPulse, HIGH);
    delayMicroseconds(200); 
    // delay 200 uSec on output pulse to turn on triac
    digitalWrite(triacPulse, LOW);

Hi Roger,
I will definitely try this code out. What advantages do you think your zero cross circuit has over the one im using. I want a stable circuit so im looking for the best choice possible.

Also, my main concern is the timing issue im having, hopefully your code will help that. If i can pass Bluetooth serial data without interrupting the zero cross circuit then im good! I wont be able to test it until wed, out of the office/home till wed.

rogerClark:
In case you want a version that uses a timer to delay the firing of the Trac.

Edit. Just realized I better say what the code does.

It controls a triac in the same way, i.e has a zero crossing and then delays before triggering the triac
It receives commands via remote control on 433MHz using the RCSwitch library which interprets a couple of buttons on a wireless remote control (hence the strange numbers in the main loop)

The other thing the code does at the same time is use the eMonLib to monitor the amount of power in another AC circuit, as the main purpose of my code is to use up excess solar power by dissipating it into a large resistive load (a water heater).

My zero crossing detector is more complex than the one in the schematic, and it gives a nice square pulse, but the pulse occurs several hundred micro seconds before the real zero crossing, so the code waits for the zero crossing, then uses a timer to delay for a hard coded period (the amount the zero crossing detector is early), and then triggers a second timer to delay the triac before eventually triggering the triac.

The reason 2 timers are used is to try to achieve dimming in the area between 0% and 5% where the zero crossing detector has already triggered, (that is, the zero crossing detector triggers somewhere around 450uS before the actual zero crossing point which at 50Hz is around 5% before the zero crossing.

Hence to be able to control between 0 and 5% I needed to use time timers, as it was impossible to use one timer, as the triac trigger timer may need to expire after the zero crossing input has occurred for the next half cycle

I assume that you breadboarded the SSR circuit and had success in dimming that?

If I can get an SSR today I'll try it today, otherwise I'll have to wait till Monday and use one from work.

Sorry, guys, but you cannot PWM an SSR for AC dimming (or variable fan speed).