AC phase controller and interrupts

I have worked up the attached test code. I haven’t tested it in the flesh yet because mains voltage with breadboards needs approaching with circumspection I think.

I was wondering about the interrupt functions intakeFan() and circFan() which are fired from separate inputs, unless there’s a way of saving one of those pins on a Nano. That’s the question - is it at all possible to combine the two into a single function fired by a zero-crossing signal?

int offPeriods;
int fanOffPeriods;
int ventOffPeriods;
int fanOffTime;
int ventOffTime;
byte ventTriggerPin = 10; 
byte fanTriggerPin = 11; 
float currentTemp;

void setup() {

  Serial.begin(9600);
  attachInterrupt(0, intakeFan, RISING);
  attachInterrupt(1, circFan, RISING);
}

void loop() {
  for (currentTemp = 12; currentTemp < 36; currentTemp++) {
    //currentTemp=(currentTemp+0.5);
    fanOffPeriods = map(currentTemp, 15, 30, 15, 0);
    ventOffPeriods = map(currentTemp, 20, 35, 15, 0);
    fanOffPeriods = constrain(fanOffPeriods, 0, 16);
    ventOffPeriods = constrain(ventOffPeriods, 0, 16);
    // this returns 0 for max value and 21 for min value
    // which means maximum 'off periods' at lowest temperatures.
    fanOffTime = (640 * fanOffPeriods);
    ventOffTime = (640 * ventOffPeriods);
    Serial.print (currentTemp);
    Serial.print("  ");
    Serial.print("Fan off ");
    Serial.print(fanOffTime);
    Serial.print("mS    ");
    Serial.print("Vent off ");
    Serial.print(ventOffTime);
    Serial.println("mS");
    delay(1000);
  }

}

//-----------------------------------------------------------
void intakeFan() { //Function fired by zero crossing interrupt
  
  delayMicroseconds(ventOffTime);
  digitalWrite(ventTriggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(ventTriggerPin, LOW);
}

//-------------------------------------------------------------
void circFan() { //Function fired by zero crossing interrupt
  
  delayMicroseconds(fanOffTime);
  digitalWrite(fanTriggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(fanTriggerPin, LOW);
}
//------------------------------------------------------------

That's the question - is it at all possible to combine the two into a single function fired by a zero-crossing signal?

There is only one ac line with a single zero crossing time, and you need to time (delay) the two triac triggers from that common time.

Indeed, because the interrupt service routines can only work sequentially, you will have to do that. If you have two interrupts from the same signal, one isr would execute completely before the other, and the timing of the second one would not be correct.

You will need to have sequential triggers for the different firing times within a single isr. Something like this might work if the two delay periods are different by more than 10 microseconds and the several microseconds it takes to execute the digitalWrite().

void triacTriggers() { //Function fired by zero crossing interrupt  
  delayMicroseconds(shorter off time);
  digitalWrite(shorterTimeTriggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(shorterTimeTriggerPin, LOW);
  delayMicroseconds(longer off time - (shorter offtime+10));
  digitalWrite(longerTimeTriggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(longerTimeTriggerPin, LOW);
}

I was imagining that ISRs on seperate pins would complete at the same time so I have learned something new. I'll try that out, thanks.

This is as far as I have progressed, I am being stymied by the odd results from the calculation of ‘ventOffDelay’ towards the end of the loop. The calculation of ‘fanOffDelay’ is perfectly predictable but the other variable is crazy.

I can’t see where my error lies. The product of a single vent period and the number of periods should be around 9600 at the start, but the result doesn’t give that.

byte fanControl;
byte fanOffPeriods;
int fanOffDelay;
byte ventControl;
byte ventOffPeriods;
byte ventOffDelay;
int oneFanPeriod = 600;  //uS
int oneVentPeriod = 873;  //uS
byte ventPin = 10;
byte fanPin = 11;
float currentTemp;
byte difference;

void setup() {

  Serial.begin(9600);
 
  attachInterrupt(0, triacTriggers, RISING);
}

void loop() {

  for (currentTemp = 10; currentTemp < 40; currentTemp++) { // testing only

    fanControl = constrain(currentTemp, 14, 30);
    fanOffPeriods = map(fanControl, 15, 30, 15, 0);

    ventControl = constrain(currentTemp, 24, 35);
    ventOffPeriods = map(ventControl, 25, 35, 10, 0);

    fanOffDelay = (oneFanPeriod * fanOffPeriods);
    ventOffDelay = (oneVentPeriod * ventOffPeriods); //intend to subtract fanOffDelay+10uS here


Serial.print(currentTemp);
Serial.print("   Fan  ");
Serial.print(fanOffDelay);
Serial.print("  One Period  ");
Serial.print(oneFanPeriod);
Serial.print("  Number of Periods  ");
Serial.println(fanOffPeriods);
Serial.print("        Vent  ");
Serial.print(ventOffDelay);
Serial.print("  One Period  ");
Serial.print(oneVentPeriod);
Serial.print("  Number of Periods  ");
Serial.println(ventOffPeriods);
    delay(1000);
  }
}

//------------------------------------------------------------


void triacTriggers() { //Function fired by zero crossing interrupt

  delayMicroseconds(fanOffDelay);
  digitalWrite(fanPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(fanPin, LOW);
  delayMicroseconds(ventOffDelay);
  digitalWrite(ventPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(ventPin, LOW);

}

/*There is only one ac line with a single zero crossing time, and
  you need to time (delay) the two triac triggers from that common
  time.
  Indeed, because the interrupt service routines can only work
  sequentially, you will have to do that. If you have two interrupts
  from the same signal, one isr would execute completely before the
  other, and the timing of the second one would not be correct.
  You will need to have sequential triggers for the different firing
  times within a single isr. Something like this might work if the
  two delay periods are different by more than 10 microseconds and
  the several microseconds it takes to execute the digitalWrite().

  A snubber is not necessary for the motor operation, but it reduces
  the RF which can affect devices in the neighbourhood. I cannot suggest
  values, look for commonly available mains RF/noise filters that
  support (at least) the motor current.
*/

ventOffDelay is overflowing when typed as byte.

//byte ventOffDelay;
int ventOffDelay;