Measuring power from a phase fired controller

Hi. I am doing a project that requires measuring the power output of a phase powered controller (Kemo Controller). By looking at the current output using a oscilloscope, I can confirm that this controller works on the principle of a SCR, i.e this is what it does to the output current:

image

For measuring the power, I am using a ESP32 coupled with a voltage transformer and a current transformer. I also tried on of the hall current sensors. For the software part I am using the EmonLib from OpenEnergyMonitor modified for the ESP32 ADC (GitHub of said library). I followed the calibration procedure, using an external power meter for reference and managed to calibrate the current sensor for a maximum usage current of 7A. Here is the code with the instructions on how to calibrate the current sensor:

/* Calibrates the current sensor (SCT013)*/
/* ----------------------------------------------------*/
/* Instructions:
1. Set up the hardware, the potentiometer of the voltage sensor should be already adjusted.
2. Find an "almost pure" resistive load (no motors, no reactors, no electromagnets, no LEDs). Examples: heater, boiler, electric shower, electric oven, kettle...
3. Install a voltmeter and ammeter to use as reference.
4. Connect the voltage measurement and current measurement sensors.
5. Edit the sketch *calibrate-vi.ino* and set the correct GPIO pins for the sensors. 
6. Set the calibration coefficients CV1, CV2, CV3, CI1, CI2 and CI3 to 1000 in the same file.
7. Compile and update the code from Arduino IDE.
8. Watch the values in the serial terminal and wait for them to stabilize. Use 115200bps as baud rate.
9. Take a note of the measured current (I) and voltage (V) from the ESP32 and the current and voltage from the reference voltmeter (Vr) and ammeter (Ir).
10. Calculate the calibration factors: CVnew = Vr*CVold/V, CInew = Ir*CIold/I where CVold and CIold are the previous calibrations from the sketch (initially 1000).
11. Change the values under the "Calibration" section of the code to the calculated ones (CInew and CVnew).
12. Compile and upload the code again, watch the serial monitor until the data stabilizes and then check if the measurements are correct.
13. Repeat steps 8 to 12 if necessary.
*/

#include "EmonLib.h"  // Include Emon Library
#define ESP32

// Pin configuration
// #define V1 34
// #define I1 35
#define V2 34
#define I2 35

// Calibration settings (allways start with 1000)
#define CV1 872
#define CI1 10.00
#define CV2 872
#define CI2 10.01

EnergyMonitor emon1;
EnergyMonitor emon2;

void setup() {
  Serial.begin(115200);

  /*
  Analog attenuation:

  ADC_0db: sets no attenuation. ADC can measure up to approximately 800 mV (1V input = ADC reading of 1088).
  ADC_2_5db: The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 1100 mV. (1V input = ADC reading of 3722).
  ADC_6db: The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 1350 mV. (1V input = ADC reading of 3033).
  ADC_11db (default): The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 2600 mV. (1V input = ADC reading of 1575).
  
  */
  // analogSetPinAttenuation(V1, ADC_11db);
  // analogSetPinAttenuation(I1, ADC_11db);

  // Phase 1
  // emon1.voltage(V1, CV1, 1.7);  // Voltage: input pin, calibration, phase_shift
  // emon1.current(I1, CI1);       // Current: input pin, calibration.

  // Phase 2
  emon2.voltage(V2, CV2, 1.732);  // Voltage: input pin, calibration, phase_shift
  emon2.current(I2, CI2);         // Current: input pin, calibration.

  // Phase 3
  // emon3.voltage(V3, CV3, 1.732);  // Voltage: input pin, calibration, phase_shift
  // emon3.current(I3, CI3);       // Current: input pin, calibration.
}

void loop() {

  Serial.println("------------");

  // emon1.calcVI(120, 2000);  // Calculate all. No.of half wavelengths (crossings), time-out
  // emon1.serialprint();           // Print out all variables (realpower, apparent power, Vrms, Irms, power factor)
  // Serial.printf("V1: %.2f, I1: %.2f \n", emon1.Vrms, emon1.Irms);
  // Serial.printf("realPower: %.2f, apparentPower: %.2f, powerFactor: %.2f \n", emon1.realPower, emon1.apparentPower, emon1.powerFactor);

  emon2.calcVI(60, 1000); // Calculate all. No.of half wavelengths (crossings), time-out
  Serial.printf("V2: %.2f, I2: %.2f \n", emon2.Vrms, emon2.Irms);
  Serial.printf("realPower: %.2f, apparentPower: %.2f, powerFactor: %.2f \n", emon2.realPower, emon2.apparentPower, emon2.powerFactor);
}

With this I managed to get a steady and accurate reading of 7A. However when I adjust the output power (which is connected to a heating element), so as to change the load current, I notice that the accuracy is severely compromised when I move from the calibrated value. For example at 1A output current on the power meter, the ESP32 reads 2A.

Could the SCR output waveform have any effect on the accuracy of using the EmonLib with a ESP32? I have tried with different current sensors and the end result is the same... Also, when comparing commercial power meters I find that while the power measurement is basically the same, the current measurement is very different. I used a Voltcraft power meter ($75) and a Wago power meter ($250) and the current measured at 6A on the Voltcraft was 4A on the Wago....
Any input or ideas on how to tackle this project would be highly appreciated!

Best,
Pablo

how does the EmonLib determine RMS current

  1. from the peak minimum and maximum and hence calculating RMS value (0.707 of peak value) which is fine for a sine wave but not for the waveform you have
  2. samping the current over time to calculate RMS, e.g. squaring the value of samples over several cycles, calculating the mean and taking the square root? I think this should work OK even for your wavefrom

From what I understand, it samples over time. This is the relevant function from the .cpp file:

//--------------------------------------------------------------------------------------
// emon_calc procedure
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kWh increment
// From a sample window of the mains AC voltage and current.
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
//--------------------------------------------------------------------------------------
void EnergyMonitor::calcVI(unsigned int crossings, unsigned int timeout)
{
  #if defined emonTxV3
  int SupplyVoltage=3300;
  #else
  int SupplyVoltage = readVcc();
  #endif

  unsigned int crossCount = 0;                             //Used to measure number of times threshold is crossed.
  unsigned int numberOfSamples = 0;                        //This is now incremented

  //-------------------------------------------------------------------------------------------------------------------------
  // 1) Waits for the waveform to be close to 'zero' (mid-scale adc) part in sin curve.
  //-------------------------------------------------------------------------------------------------------------------------
  unsigned long start = millis();    //millis()-start makes sure it doesnt get stuck in the loop if there is an error.

  while(1)                                   //the while loop...
  {
    startV = analogRead(inPinV);                    //using the voltage waveform
    if ((startV < (ADC_COUNTS*0.55)) && (startV > (ADC_COUNTS*0.45))) break;  //check its within range
    if ((millis()-start)>timeout) break;
  }

  //-------------------------------------------------------------------------------------------------------------------------
  // 2) Main measurement loop
  //-------------------------------------------------------------------------------------------------------------------------
  start = millis();

  while ((crossCount < crossings) && ((millis()-start)<timeout))
  {
    numberOfSamples++;                       //Count number of times looped.
    lastFilteredV = filteredV;               //Used for delay/phase compensation

    //-----------------------------------------------------------------------------
    // A) Read in raw voltage and current samples
    //-----------------------------------------------------------------------------
    sampleV = analogRead(inPinV);                 //Read in raw voltage signal
    sampleI = analogRead(inPinI);                 //Read in raw current signal

    //-----------------------------------------------------------------------------
    // B) Apply digital low pass filters to extract the 2.5 V or 1.65 V dc offset,
    //     then subtract this - signal is now centred on 0 counts.
    //-----------------------------------------------------------------------------
    offsetV = offsetV + ((sampleV-offsetV)/ADC_COUNTS);
    filteredV = sampleV - offsetV;
    offsetI = offsetI + ((sampleI-offsetI)/ADC_COUNTS);
    filteredI = sampleI - offsetI;

    //-----------------------------------------------------------------------------
    // C) Root-mean-square method voltage
    //-----------------------------------------------------------------------------
    sqV= filteredV * filteredV;                 //1) square voltage values
    sumV += sqV;                                //2) sum

    //-----------------------------------------------------------------------------
    // D) Root-mean-square method current
    //-----------------------------------------------------------------------------
    sqI = filteredI * filteredI;                //1) square current values
    sumI += sqI;                                //2) sum

    //-----------------------------------------------------------------------------
    // E) Phase calibration
    //-----------------------------------------------------------------------------
    phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);

    //-----------------------------------------------------------------------------
    // F) Instantaneous power calc
    //-----------------------------------------------------------------------------
    instP = phaseShiftedV * filteredI;          //Instantaneous Power
    sumP +=instP;                               //Sum

    //-----------------------------------------------------------------------------
    // G) Find the number of times the voltage has crossed the initial voltage
    //    - every 2 crosses we will have sampled 1 wavelength
    //    - so this method allows us to sample an integer number of half wavelengths which increases accuracy
    //-----------------------------------------------------------------------------
    lastVCross = checkVCross;
    if (sampleV > startV) checkVCross = true;
                     else checkVCross = false;
    if (numberOfSamples==1) lastVCross = checkVCross;

    if (lastVCross != checkVCross) crossCount++;
  }

  //-------------------------------------------------------------------------------------------------------------------------
  // 3) Post loop calculations
  //-------------------------------------------------------------------------------------------------------------------------
  //Calculation of the root of the mean of the voltage and current squared (rms)
  //Calibration coefficients applied.

  double V_RATIO = VCAL *((SupplyVoltage/1000.0) / (ADC_COUNTS));
  Vrms = V_RATIO * sqrt(sumV / numberOfSamples);

  double I_RATIO = ICAL *((SupplyVoltage/1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / numberOfSamples);

  //Calculation power values
  realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
  apparentPower = Vrms * Irms;
  powerFactor=realPower / apparentPower;

  //Reset accumulators
  sumV = 0;
  sumI = 0;
  sumP = 0;
//--------------------------------------------------------------------------------------
}

(I did use a bias resistor so that the output voltage was centered on VCC/2). With the last tests I did, which where with the current transfomer, I used this circuit:

The current transformer is from Wago and is 35:1. I choose a 4ohm burden resistor because the max current I would measure in the primary would be 10A rms. R1 = R2 = 10kOhms, VCC = 3.3V and C1 = 10uF.

looks like the library calculates RMS by squaring samples, taking mean and then square root

I have used three SCT-013 current clamps to measure currect on my 3-phase system
used 100amp clamps and implemented a program to sample over several cycles and calculated rms (square samples, take mean and square root) - comparing results with a commercial current clamp meter the current readings were within a couple of amps

recently been experimenting with a 4-Channel-Mains-Current-Sensor-module which has an onboard ESP8266 WiFi device. Transmiting UDP datagrams to a server for analysis and display

I think the problem might be the accuracy of the ESP32 ADC... when doing a Serial plot of the analog pin connected to the current transformer, the waveform looks very distorted. However when probing this signal with the oscilloscope, there is a very accurate and crisp phase controlled AC signal. Of course the ESP32 ADC was never going to be as fast and precise as a 100MHz oscilloscope, but I though that since the signal is 50Hz the ADC would be accurate enough to make a power meter.

AC voltmeter and tong would get you there.

I have seen SCR fired power electronics such as this used with battery chargers and for Hydro Generator DC excitation . With both of these systems they were 3 phase AC to the power bridge with "free wheeling diodes" on the Neutral leads and both have big filter capacitors on the output . These measure DC current and voltage so calculating power is simply V x I . Try to calculate the power of the "Quasi-AC values would be quite a chore since the AC current and voltage output wave forms will be varying and will be rarely at unity . Seems like the DC equivalent would be the simplest

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.