How to turn this single Channel AC Dimmer code to 4 Channel AC Dimmer code

I am working on a project where I have to Control 4 Channel AC Dimmer…For Single Channel the code given is working flawlessly…How can I turn this into 4 Channels AC Dimmer ?. I want to Control each channel independently.

#include "hw_timer.h"          
const byte zcPin = 12;
const byte pwmPin = 13;  

byte fade = 1;
byte state = 1;
byte tarBrightness = 255;
byte curBrightness = 0;
byte zcState = 0; // 0 = ready; 1 = processing;
void setup() {
  Serial.begin(115200);   
  pinMode(zcPin, INPUT_PULLUP);
  pinMode(pwmPin, OUTPUT);
  attachInterrupt(zcPin, zcDetectISR, RISING);    // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  hw_timer_init(NMI_SOURCE, 0);
  hw_timer_set_func(dimTimerISR);
}

void loop() {
  // put your main code here, to run repeatedly:
    if (Serial.available()){
        int val = Serial.parseInt();
        if (val>0){
          tarBrightness =val;
          Serial.println(tarBrightness);
        }
        
    }
}


void dimTimerISR() {
    if (fade == 1) {
      if (curBrightness > tarBrightness || (state == 0 && curBrightness > 0)) {
        --curBrightness;
      }
      else if (curBrightness < tarBrightness && state == 1 && curBrightness < 255) {
        ++curBrightness;
      }
    }
    else {
      if (state == 1) {
        curBrightness = tarBrightness;
      }
      else {
        curBrightness = 0;
      }
    }
    
    if (curBrightness == 0) {
      state = 0;
      digitalWrite(pwmPin, 0);
    }
    else if (curBrightness == 255) {
      state = 1;
      digitalWrite(pwmPin, 1);
    }
    else {
      digitalWrite(pwmPin, 1);
    }
    
    zcState = 0;
}

void zcDetectISR() {
  if (zcState == 0) 
  {
    zcState = 1;
  
    if (curBrightness < 255 && curBrightness > 0) {
      digitalWrite(pwmPin, 0);
      
      int dimDelay = 30 * (255 - curBrightness) + 400;//400
      hw_timer_arm(dimDelay);
    }
  }
}

Ask somebody familiar with the hw_timer library. If the library doesn't offer multiple triggers off the shelf, the required changes require deep insight into the library code.

I guess that a separate timer or timer compare register is required for each channel.

You may have to take control of a hardware timer directly and use an Output Compare Register to trigger an interrupts for each PWM value. In the ISR you would turn off the output pin and update the OCR for the next brighter output pin.

At 16 MHz the clock will tick 133,333.333 times for each half cycle of AC. Since Timer1 can only count to 65536 you need a pre-scale of at least 2.03 and the first step above 1 is 8. That gives you a 2 MHz clock or 16666.6 clocks per half cycle. Timer 2 can only count to 256 so you need a pre-scale of at least 520.8. The next available value is 1024 so 15625 Hz or 130.2 clocks per half cycle.

A good approach, except that the next interrupt is for the next dimmer channel. Also prevent missed triggers, because the interrupt occurs only at exact equality.

When would the pins be reset to off state?

DrDiettrich:
When would the pins be reset to off state?

My understanding is that the (non-zero brightness) output pins would be set ON in the zero-crossing interrupt and OFF in the appropriate timer interrupt. Repeat for every zero crossing: 120 Hz on a 60 Hz system. The hard part is keeping track of what order the outputs turn off so the OCR can be set for the next pin(s). I guess for each OCR interrupt you could scan the table, turn off the pin(s) that match the current OCR setting and find the lowest OCR setting higher than the current one.
I think it might be sufficient to keep PWM value, PORT address and PIN mask for each channel. A PWM value of 0 would be sufficient to mark a channel as unused. Any value over the maximum PWM value would work for keeping the output pin on all the time. The timer gets set to 0 in the zero crossing interrupt. so those OCR values will never be reached.

All outputs will be off at zero crossing. Then the brightest output is served first, turning the output on so that the triac will fire for the remainder of the half cycle. Once the triac has fired, the output can be turned off again.

A timer has multiple OCR and PWM pins, so that 2 (Uno) or 3 (Mega) channels can be served from a single timer.

DrDiettrich:
I guess that a separate timer or timer compare register is required for each channel.

will you suggest some other timer for NodeMCU....Please guide me to used that as I am new to coding stuff.

You should learn more about coding and controllers before you attack such an ambitious project.

DrDiettrich:
You should learn more about coding and controllers before you attack such an ambitious project.

upto now I have learned about OS_Timer. By using it I can control 7 Channels. But I am confused about dimTime ISR as used in this code.

It adjusts curBrightness if fading, and sets the pwmPin.