50Hz phase cut (triac dimmer) detection

Hi, I'm trying to detect the triac dimmer on a 220V pump. The dimming is made by the Solar Control Unit through a phase-cut.
Based on the phase-cut (https://i.stack.imgur.com/HZjE7.jpg) I take the 220V 50Hz signal through a transformer to 9V, a diode bridge to rectify, and then a voltage diver with 4.7 kOhm and 10 kOhm to read more or less 3V into A1 analog pin with Arduino UNO.
I was trying to read the number of samples ON vs samples OFF to compute the powering ratio (and then compute the phase shift due to the Triac dimmer).
The following code should compute the ratio for 40 cycles and then print the value in the serial monitor. I can more or less print into the serial the ratioAV with values comparable to the Control Unit ones.

However, if I try to add a delay on the serial.print() I read a completely different value.
For instance, if I use millis to plot every 2 seconds I read completely different values, but if I add an empty line outside the if condition then the ratio is printed with the correct value.

It doesn't work:

  if (currentMillis - previousMillis >= 2000) {
    Serial.println(100*ratioAV)
    previousMillis = currentMillis;
  }

It works:

  Serial.print("\r")
  if (currentMillis - previousMillis >= 2000) {
    Serial.println(100*ratioAV)
    previousMillis = currentMillis;
  }

I also tried to put all the code inside the if condition of the millis to sample the signal for 2 seconds and then print the results (and repeat it every 2 seconds) but it doesn't work.

I'm not sure if I can sample this signal (but Arduino should be able to sample a sufficient number of samples for each wave cycle) or if there exists a better way to do it.

Thanks in advance.

The complete code:

int ch1=0;
float oldTime=0;
bool state=0;
bool oldState=0;
bool changed=0;
bool toPrint=0;
float timeOff=0;
float timeOn=0;
float ratio=0;
float ratioAV=0;
float ratioAVold=0;
int ratioPrint=0;
int ratioIndex=0;
const int threeshold=100;
unsigned long timeZero=0;

void setup() {
  Serial.begin(9600);
}
void loop() {
  ch1=analogRead(A1);
  oldState=state;
 ratioAVold=ratioAV;
  if(ch1<threeshold){
    state=0;
     timeOff+=1;
     timeZero+=1;
  }
  if(ch1>threeshold){
    state=1;
    timeOn+=1;
  }
  if(state!=oldState){
    changed=1;
    timeZero=0;
    ratio=ratio+(timeOn/(timeOn+timeOff));
   if(state==1){
     timeOn=1;
    }
    if(state==0){
    timeOff=0;
    timeZero+=1;
    }
    ratioIndex+=1;
      }
   if(ratioIndex>38){
      ratioAV=ratio/40.00;
      ratioIndex=0;
      ratio=0;  
      }
 Serial.println(100*ratioAV);
  if(timeZero>200){
    ratioAV=0;
  }
}

to visualize the cut wave, I read the current with ACS720.

void sampleAC(unsigned long start) {
  const short size = 400;
  unsigned long ts[size];
  unsigned short data[size];
  for (int i = 0; i < size; i++) {
      data[i] = analogRead(A1);
      ts[i] = micros() - start;
  }
  for (int i = 0; i < size; i++) {
    client.print(ts[i]);
    client.print('\t');
    client.println(data[i]);
  }
}

then I import the output into Calc and make a chart

to get the consumed power I read RMS of samples in 200 millis

int readElSens() {

  const int ELSENS_ANALOG_MIDDLE_VALUE = 512;

  // sample AC
  unsigned long long sum = 0;
  int n = 0;
  unsigned long start_time = millis();
  while (millis() - start_time < 200) { // in 200 ms measures 10 50Hz AC oscillations
    long v = analogRead(A0) - ELSENS_ANALOG_MIDDLE_VALUE;
    sum += v * v;
    n++;
  }
  return sqrt((double) sum / n) * 10;
}

then I use a function with some magic values to calculate the power

float ratio = 1.0 - ((float) elsens / ELSENS_MAX_VALUE); // to 'guess' the 'power factor'
elsensPower = (int) (elsens * ELSENS_VALUE_COEF * cos(PF_ANGLE_SHIFT + ratio * PF_ANGLE_INTERVAL)) + ELSENS_VALUE_SHIFT;

The usual calculation of consumed power from value returned by analogRead consist of calculating the voltage on pin, then to calculate the AC current based on the characteristic of the sensor, then calculate RMS current from measurements over one or two AC waves and then multiply the RMS current with measured or fixed RMS voltage (230 V).

What I do: I sample the analogRead values 200 milliseconds (10 waves at 50 Hz) and calculate the RMS of this values. Then I multiply the RMS value with a coefficient determined to match the power measured by some external consumption meter. The coefficient covers the calculation of current from analogRead value and the multiplication with fixed RMS voltage.

The phase cutting technique changes the power factor. For full power the power factor is 1. The more is cut away from the AC wave, the power factor is smaller then 1 (0.92 for example). The effective power is calculated as apparent power (V x I) multiplied by the power factor, which is a value between 0 and 1. Power factor is calculated as cosine of an angle.

The power factor changes with the amount of the current so I based the calculation of the power factor on the ratio of the RMS of sampled values to maximum RMS of sampled values. I determined the maximum angle for my system by matching in a Excel/Calc chart the lines of my function and of consumption measured by external meter. So the power factor is cosine of the part of the angle calculated with ratio of sensor values RMS.

1 Like

Thank you very much. I solved using readElSens() and computing the Vrms like that:

int readElSens() {
  // sample AC
  unsigned long long sum = 0;
  int n = 0;
  const int prop=800; // proportional value to scale the 0-1023 input
  unsigned long start_time = millis();
  while (millis() - start_time < 200) { // in 200 ms measures 10 50Hz AC oscillations
    long v = 320*analogRead(A1)/prop;
    sum += v * v;
    n++;
  }
  return sqrt((double) sum / n) * 10;
}

Then I can simply convert the Vrms in current power or phase shift angle.

Can you post an annotated schematic, I do not do well with word problems or frizzes. Also post links to technical information on each of the hardware items in the design.

That's the schematic:

The AC Triac Dimmer Source came from a Thermal Solar System Control Unit to modulate a pump between 30% and 100% of power (in steps of 5%). I can read the signal parallel to the pump transforming it in the 0-10V range. I don't have specs for the Control Unit but I'm sure it uses a triac dimmer by following tech supports info.

Looks nice on paper but it tends to cause problems with the Arduino. the Arduino is not a constant current load so the resistor divider will give a varying output voltage. The ripple from the bride will cause to many problems and the Amega will not operate, it requires a steady state DC not a pulsating DC. For a power supply, try this link you can get a decent unit for less then $5 US. https://www.aliexpress.us/item/3256804596662304.html?spm=a2g0o.productlist.main.27.35ae4ecc6otEzT&algo_pvid=7f1e8eba-fc10-4fb6-9fcf-8f582ced268a&algo_exp_id=7f1e8eba-fc10-4fb6-9fcf-8f582ced268a-13&pdp_npi=3%40dis!USD!2.35!1.64!!!!!%40211bd7d616830555360463248d0833!12000030468788422!sea!US!100026910&curPageLogUid=rsk7m3apaHF7 You can search they come in many different confiturations and prices. They also make zero cross opto couplers which were designed for this type of application.

A simple circuit with the opto couplet will convert the line voltage to a 5V signal for the Arduino. This link should help.

Yes, but the Arduino is powered with constant 5V via usb and I have to read the pulsatile signal on pin A1

Do you want to read amplitude or detect the zero cross?

I want to read the signal amplitude and compute the Vrms to understand how much power the triac dimmer is cutting.

Hi, to get the RMS you need only the zero crossing:

We assume that we know the amplitude. Amplitude means peak voltage and for 220V is 220V*sqrt(2).
BR

Ok, but to use this formula I don't need the zero crossing but the triac angle, i.e. the time in which the signal is zero as a fraction of 180°.
Can I detect both what happens in zero and alpha (real zero crossing and conduction angle) with the zero crossing? And how to do that?
Isn't it easier to calculate the RMS numerically by adding the samples squared and then dividing by the number of samples, over multiple periods? (As described in a previous comment).

Forgive me, there is an error in the formula, missing square root:

The benefits here are less samples, you don't have to pool the ADC with relatively (10/50Hz) high speed. You can measure only the duty cycle of the PWM by zero crossings. Also the HW could be just opto-isolator although a transformer is a good idea from safety prospective. Also, the transformer will give error in the voltage because they are not precise and usually not designed to work with other than sine waves (and specified load) but the zero cross will be the same.

Here, time to angle: alpha = pi * ta / Ts = pi * duty

2 Likes
void loop() {
  // put your main code here, to run repeatedly:
   ontime = pulseIn(pulse_inp,HIGH);
   offtime = pulseIn(pulse_inp,LOW);
   period = ontime + offtime;
   dutyc =  ontime/period;
   angle = PI*dutyc;
   rms_percent = sqrt((PI-angle + sin(2*angle)/2)/PI);
   rms = Vrail * rms_percent;

   Serial.println(rms_percent);
}
1 Like

Ok, so you suggest I add an optoisolator between the transformer and Arduino? It will give me a squarewave-like signal depending on the triac AC dimmer. Then, I read this signal with:

   ontime = pulseIn(pulse_inp,HIGH);
   offtime = pulseIn(pulse_inp,LOW);

Is that correct?
Any suggestion on the optoisolator? (the transformer works with 230V -->10V)

You can google and find thousands results for zero crossing detector:

https://erg.abdn.ac.uk/users/gorry/eg3576/resources/DMX-Simple_Methods_ZCD.pdf

Some of them are not isolated and DANGEROUS! Some of them will give you inverted signal or will detect only half of the wave. Few examples:


Optoisolator with 10V transformer is not a good idea, the LED in the coupler has 2-3V forward voltage and will loose ?% of the time.

Also, I don't like pulsein(), delay() and etc. (loop) blocking functions, they are good for proof of a concept, fast check. It is much better to use interrupt and mils or capture interrupt.
What is your real triac application and why you want to measure the rms? If you need only % of the (avg) power or the rms voltage, the formula is the better way.

Have you checked with scope?

I have done it now but this is not what I'm expecting from a triac dimmer. It seems that the control unit completely cut some cycles with respect to the desired power:

(the voltage is 10x scaled)

The following is when the control unit reads 30% of power:

image

At 100% of power, the signal is a sinusoidal wave 230V 50 Hz:

image

I had the feeling that the control is not triac-like phase control that's why I asked. It is uncommon for water pumps. Alright, you have to count packets or use the code from @Juraj but filtering the input.

Of course. Thank you very much for the help.

Although, it may not work. You need long time averaging filter after the ADC and maybe a second to get the right value. Anyway, you can figure it out. Cheers.

1 Like