Dimming effect by zero crossing detector and a triac

It actually should do, since it is bi-directional, in that case, the cause may be that the resistor values are to big, and the zero-crossing is not always detected.
I have only used DC opto-couplers in combination a working code, i can post the code if you like. There must be something wrong somewhere. What are your resistor values again ? (and what AC voltage ? )

1 Like

Maybe because the Triac does not trigger on negative voltages at the gate? That's just a guess but i'd look into that.

1 Like

@marcoch , why would there be a negative voltage?

1 Like

@Juraj during the negative part of the sine wave there is also negative voltage fed trough the resistor and the opto triac. A bridge rectifier could solve that issue (and lamps don't mind it). We just need to maxe sure to keep capacities at pay, otherwise the Triac won't shut off anymore if we're not going to zero.

But looking into it I think the negative Gate-Voltage should not be an issue... It should work!
It would be good practive to add a discharge resistor between the gate and the load, but this should just help with switching off and have no influence on switching on...

The zero-crossing thing looks normal, yeah?

Input is 220v , 50Hz

Any help would be appreciated, but I am working on the following sketch and it's kind of more clear for me than any other codes I've looked on so far.

#include <TimerOne.h>

const byte INTERRUPT_PIN = 2;
const byte TRIAC_PIN = 4;
const byte TRIAC_PULSE_MICROS = 30;

const int FADE_MAX = 9800;
const int FADE_MIN = 2000;
const int looptime = 100;
int fadeAmount = 10;

volatile bool triacOn;
volatile int period = FADE_MIN; // microseconds cut out from AC pulse



void zeroCrossing() {
  triacOn = false; // triac tuns off self at zero crossing
  Timer1.setPeriod(period); // to call triacPulse() after off period
}

void triacPulse() {
  if (triacOn) { // stop pulse
    digitalWrite(TRIAC_PIN, LOW);
    Timer1.stop();
  } else { // start pulse
    digitalWrite(TRIAC_PIN, HIGH);
    triacOn = true;
    Timer1.setPeriod(TRIAC_PULSE_MICROS);
  }
}

void setup() {
  pinMode(TRIAC_PIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), zeroCrossing, RISING);
  Timer1.initialize();
  Timer1.attachInterrupt(triacPulse);
}

void loop() {
  period = period + fadeAmount;
  if (period <= FADE_MIN || period >= FADE_MAX) {
    fadeAmount = -fadeAmount;
  }
  delay(looptime);
}



Yeah, but sometimes.

It is quite similar to the one i am using, just a slight difference in philosophy, not setting and stopping timer1, but rather using a counter.

#include <TimerOne.h>   

#define ZERO_X 2
#define AC_LOAD 3

volatile uint16_t dimming = 80;   

volatile int i=0;          
volatile bool zerocross;
uint8_t freqstep=9;

void setup(){
  Serial.begin(250000);
  
  pinMode(AC_LOAD, OUTPUT);
  pinMode(ZERO_X, INPUT);
  
  attachInterrupt(digitalPinToInterrupt(ZERO_X), ZeroCrossed, RISING); 
  Timer1.initialize(freqstep);                      
  Timer1.attachInterrupt(DimCheck, freqstep); 
  Serial.println("Started");

}
void loop()  {
  if (Serial.available()) dimming=(ExtractValue()*4);
}

uint16_t ExtractValue() {
  delay(1);
  byte msg;
  unsigned int command = 0; 
  msg = (byte) Serial.read();
  while (msg != 10) {
    if ((msg>47) && (msg<58)) {
      command = command * 10;
      command = command + (msg - 48);
    }
    msg =(byte) Serial.read();
  }
  Serial.print("Dimming Level ");
  Serial.println(command,DEC);
  if ((command>20) && (command<255)) return command;
  return dimming/4;
}

void ZeroCrossed() {
  zerocross = true;               
  i=0;
  digitalWrite(AC_LOAD, LOW);       
}

void DimCheck() {                   
  if(zerocross == true) {              
    if(i>=dimming) {                     
      digitalWrite(AC_LOAD, HIGH); // turn on light       
      i=0;  // reset time step counter                         
      zerocross = false; //reset zero cross detection
    } 
    else {
      i++; // increment time step counter                     
    }                                
  }                                      
}                                   

Timer1 is running all the time,. The triac pin can either be turned off at the next zero-X (as here) or as soon as possible, ideally during the next call to the timer1 function, it will turn off by itself at the crossing.
This sketch is actually the modfication i used when controlling a DC load with mosfets. For an AC-load it should be :

void ZeroCrossed() {
  zerocross = true;               
  i=0;   
}

void DimCheck() {    
  static bool triacOn = false;               
  if(zerocross == true) { 
    if (triacOn) {
      triacOn = false;    
      i=0;  // reset time step counter                         
      zerocross = false; //reset zero cross detection  
      digitalWrite(AC_LOAD, LOW);             
    }
    else if ( i >= dimming) {                     
      digitalWrite(AC_LOAD, HIGH); // turn on light   
      triacOn = true;    
    } 
    else {
      i++; // increment time step counter                     
    }                                
  }                                      
}    

9us as a frequency is rather low and may not leave much processing time for the CPU, 17us works really well for me.
I suspect that the stopping and setting of timer1 like you do may cause an issue.

1 Like

Apparently the half-wave distortion thing is related to the zero-crossing section, I changed it with another one suggested by Deva_Rishi

It seems this gives sharper z-c and that's the matter?

zcc

Since the triac naturally stops conducting near zero crossing, it must be reliably triggered for it to conduct on both half cycles. It needs two triggers per AC cycle.

Is it possible to reuse the zero-crossing section of this same circuit along with another Triac as an ac switch? (not dimmer)

You can use a triac as an AC switch because that is what it is. Then you do not need to have a zero-crossing detector.

1 Like

So your issue is solved ? Well that is great, I will keep it in mind that the H11AA1 does not yield good results.
As a small note on your final schematic. Having a current limiting resistor on either side of the rectifier bridge (or opto-coupler is preferred in High voltage circuits like that, like you had in the original schematic. Depending a little on your country of residence, which of the AC inputs is the 'live wire' is undefined. By having a resistor on either end the voltage is significantly reduced from that point onwards. Mains power circuits are dangerous and should always be handled with more care. ( i trust you do, but others may find this topic in a search, )

1 Like

So just like a relay? I ask this because it is said the dimmer with Triac isn't appropriate for ac LEDs (non-dimmable)...

Yeah I guess so. But still I hope the previous z-c works fine too because it doesn't add the bridge rectifier to the circuit , tho not a big deal.

Yeah thanks. BTW what's the role of this diode? necessary?
thediode

As an on/off switch, of course. I actually found that with the method i can even dim non-dimmable AC-LED bulbs, though they have a very narrow band in which the dimming is working, and quickly go from fully off to fully on.
Still it can be made to work, better than with an analog dimmer. Also Dimmable AC-LED bulb (like fillament) usually have a narrower band, and it really depends on the specific type of bulb (and it's internal specifications)

No it looks like overkill, no current can flow in that direction, even with the diode there.

Bridge rectifiers can also be found in small packages, rather than in 4 individual diodes.

1 Like

Now back to the sketch I want to make this happen:
A switch S1 and another ac light added, this time it is a non-dimmable LED.

I want the switch works like this:
if (S1 == HIGH) {
1-the incandescent light gradually goes from off to on mode
2-and after that the LED turns on
3-and the incandescent bulb turns off}

if (S1 ==LOW) {
1-the incandescent bulb turns on (to maximum brightness)
2-the LED turns off
3-the incandescent bulb gradually turns off}

For approaching this I added this to triacPulse function

if(period <= FADE_MIN){   // yeah, so where is the switch you are talking about??
    noInterrupts();
    digitalWrite(LED_LAMP,HIGH);
  }

and changed period = period - fadeAmount; in the loop and set volatile int period = FADE_MAX;

This works partly but certainty doesn't satisfy all the conditions I mentioned earlier in the post, I guess I need to use interrupt for the S1, but how?

noInterrupts();

does not belong within an ISR, interrupts are always disabled during the ISR.
I would simply do

void loop() {
  period = period + fadeAmount;
  if (period <= FADE_MIN ) {
    digitalWrite(LED_LAMP,HIGH);
    fadeAmount = -fadeAmount;
  }
  else if (period >= FADE_MAX) {
    digitalWrite(LED_LAMP,LOW);
    fadeAmount = -fadeAmount;
  }
  delay(looptime);
}

There is no need to switch the LED_LAMP on or off within an ISR. You can simply switch it within the main loop when the conditions for switching are met.

1 Like

Dimmable LED bulb's AC/DC adapter senses the change on AC side and PWMs the DC side. A not dimmable LED with switching AC/DC adapter can cause damage if dimmed (in my 'experiment' it destroyed the commercial dimmer). There are new 'AC' LEDs which use some other trick to convert 240/120 V AC to DC for the LED.

The filament dimmable leds i have, do not use much more than a diode (or 2) and a capacitor (and an inductor to reduce radio noise i think) Non dimmable Bulbs normally lack the capacitor. I have not seen a bulb with an AC input and PWM output you are talking about (not that they don't exist of course).
The dimmable filament bulb work with analog and digital dimmers, but because the wattage is very low, usually special (electronic / digital) dimmers are used. (though low wattage analog dimmer work ok)
As i said, with the digital triac dimmer sketch & circuit i can dim non-dimmable LED bulbs, but the dimming band is quite narrow.
When creating a multi channel dimmer, a different issue came up which cause the channels to interfere with each other when connected. I then modified it to DC dimming (the filament bulb doesn't care if it's AC or DC) but not using PWM, because the Bulb was not made for that frequency. 50Hz PWM would probably have worked, but i just used the zero-x detection to create that and that worked fine (turning the mosfet off at zero-x)
In the end i suspect that the power to the triac was interrupted sufficiently by the capacitor in the one bulb to influence the triac in the other. By then i had a working system though and didn't bother another full investigation. Maybe just waiting with turning the triac 'off' until zero-x (or minimum dimming level) would have worked also.

Are triacs like relays? I say, no, because a relay gives isolation between the driving circuit and the switched circuit. A triac gives no such isolation. Triacs are strange components! They are like two parallel but opposed polarity SCRs. We drive them like a transistor but, they are good for AC in that they can conduct in both directions but, they latch on when triggered, so the gate can't turn them off! They naturally turn off when the current flowing through them falls below the minimum holding value, so we don't use them for DC circuits (SCRs are used there).

1 Like

I don't want it to turns on and off over and over again, that's why I thought the interrupt is needed.
So my ISR is like this:

void switching() {

  int sValue = digitalRead(SWITCH);
  switch (sValue){
    case HIGH:
      period = FADE_MAX;        // incandescent bulb gradually turns on
      fadeAmount = -100;
      break;
    case LOW:
      period = FADE_MIN;        // incandescent bulb gradually turns off
      fadeAmount = +100;
      break;
  }

}

and I defined the following variables like the below to set the default mode of the switch in high position:

volatile int period = FADE_MAX; 
int fadeAmount = -100;

and the loop is:

void loop() {
  
  period = period + fadeAmount;
  if (period <= FADE_MIN ) {
    detachInterrupt(INTERRUPT_PIN);
    digitalWrite(LED_LAMP,HIGH);
   
  }
  else if (period >= FADE_MAX) {
    
    digitalWrite(LED_LAMP,LOW);
    detachInterrupt(INTERRUPT_PIN);
    
  }
  delay(looptime);
}

The first part works as expected, meaning when the switch is in HIGH level the bulb gradually turns on then the led turns on and then the bulb goes off.
but when the switch is in LOW mode , the bulb gradually goes off and on again and again...seems the Triac still fires even after I detached the interrupt.

I suspect 'period' rolls over. How about :

void loop() {
  if (period <= FADE_MIN ) {
    detachInterrupt(INTERRUPT_PIN);
    digitalWrite(LED_LAMP,HIGH);   
  }
  else if (period >= FADE_MAX) {    
    digitalWrite(LED_LAMP,LOW);
    detachInterrupt(INTERRUPT_PIN);    
  }
  else {      
    period = period + fadeAmount;
  }
  delay(looptime);
}

I find it anyway not such good practice to disable or enable the interrupt as you please, and would rather use a 'flag' tested on entry to the ISR

The interrupt for a 'switch' is not really needed, also you have not allowed for debounce.

1 Like