Calculation of power factor using FFT on an ESP32

Hello, I am working on a project that calculates the power factor of a 120Vac - 60Hz network. I have had problems with the fast Fourier transform and the calculation of the phase shift of the voltage and current signals using the atan2 function. I obtain the voltage signal with a voltage divider along with a DC voltage so that the ESP32 can read the negative values of the wave. I obtain the current signal with a current transformer along with a voltage divider and an operational amplifier in voltage follower mode. Help me with the program.

#include <arduinoFFT.h>

const uint16_t samples = 128;         // Este valor DEBE ser siempre una potencia de 2
const double samplingFrequency = 15360; // Hz, se puede aumentar en ESP32. 7680
unsigned int sampling_period_us;
unsigned long microseconds; 



void setup() {
  Serial.begin(115200);
  analogReadResolution(12); 
  sampling_period_us = round(1000000 * (1.0 / samplingFrequency));

}

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

double vRealA[samples];
double vImagA[samples];
double cRealA[samples];
double cImagA[samples];

ArduinoFFT<double> FFTvoltajeA = ArduinoFFT<double>(vRealA, vImagA, samples, samplingFrequency);
ArduinoFFT<double> FFTcorrienteA = ArduinoFFT<double>(cRealA, cImagA, samples, samplingFrequency);

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

void loop() {

  //bool señalI_1_cruzó = false;

  float I_1 = senal_C_1();
  float Irms_1 = get_C_1();
  Serial.print("Irms_1: ");
  Serial.println(Irms_1);


  float V_1 = senal_V_1();
  float Vrms_1 = get_V_1();
  Serial.print("Vrms_1: ");
  Serial.println(Vrms_1);



  microseconds = micros();
  for (int i = 0; i < samples; i++)
  {
    vRealA[i] = senal_V_1();
    vImagA[i] = 0;

    cRealA[i] = senal_C_1();
    cImagA[i] = 0;
  
  while (micros() - microseconds < sampling_period_us) {
      // bucle vacío
    }
    microseconds += sampling_period_us;
  }
  FFTvoltajeA.windowing(FFTWindow::Hamming, FFTDirection::Forward);
  FFTvoltajeA.compute(FFTDirection::Forward);
  //FFTvoltajeA.complexToMagnitude();
  double peakvoltajeA = FFTvoltajeA.majorPeak(vRealA, samples, samplingFrequency);
  //Serial.print ("peakvoltajeA: ");
  //Serial.println (peakvoltajeA);
  FFTcorrienteA.windowing(FFTWindow::Hamming, FFTDirection::Forward);
  FFTcorrienteA.compute(FFTDirection::Forward);
  //FFTcorrienteA.complexToMagnitude();
  double peakcorrienteA = FFTcorrienteA.majorPeak(cRealA, samples, samplingFrequency);

// Encontrar el índice de la frecuencia fundamental
    float f_fundamental = 60.0; // Ajusta a tu frecuencia
    int indiceFundamental = round(peakvoltajeA * samples / samplingFrequency);
  float fasevoltajeA = atan2(vImagA[indiceFundamental], vRealA[indiceFundamental]);
  float fasecorrienteA = atan2(cImagA[indiceFundamental], cRealA[indiceFundamental]);
  float desfaseA = fasevoltajeA - fasecorrienteA;
  float FP_A = cos(desfaseA);
  Serial.println(FP_A);
  //delay(sampling_period_us);

}

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

int C_prom_1 (int n1){
   long sumaC_1 = 0;
  for(int i1 = 0; i1<n1; i1++){
    sumaC_1 = sumaC_1 + analogRead(34);
  }
  return (sumaC_1/n1);
}

float senal_C_1(){
  float senalC_1 = C_prom_1(10)*(3.3/4095.0)-1.46; //1.46
  float senal_scale_C_1 = senalC_1 *22.5; //22.5
  return (senal_scale_C_1);
}

float get_C_1(){
  float corriente_1 = 0;
  float sumatoriaC_1 = 0;
  float Irms_1 = 0;
  long tiempoC_1 = millis();
  int N1 = 0;

    while(millis() - tiempoC_1 < 16.6){ 
    corriente_1 = senal_C_1();
    sumatoriaC_1 = sumatoriaC_1 + sq(corriente_1);
    N1 = N1 + 1;
    delay (1);
  }
  Irms_1 = sqrt(sumatoriaC_1/N1);
  return (Irms_1);
}

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

int V_prom_1 (int m1){
  long sumaV_1 = 0;
  for(int l1 = 0; l1<m1; l1++){
    sumaV_1 = sumaV_1 + analogRead(32);
  }
  return (sumaV_1/m1);
}

float senal_V_1(){
  float senalV_1 = V_prom_1(10)*(3.3/4095.0)-1.48; //10
  float senal_scale_V_1 = senalV_1 * 127; //160 - 127
  return (senal_scale_V_1);
}

float get_V_1(){
  float voltaje_1 = 0;
  float sumatoriaV_1 = 0;
  float Vrms_1 = 0;
  long tiempoV_1 = millis();
  int M1 = 0;

  while(millis() - tiempoV_1 < 16.6){ 
    voltaje_1 = senal_V_1();
    sumatoriaV_1 = sumatoriaV_1 + sq(voltaje_1);
    M1 = M1 + 1;
    delay (1);
  }
    Vrms_1 = sqrt(sumatoriaV_1/M1);
    return (Vrms_1);
}

I've never done this but I assume the phase difference is usually determined by the time difference between the voltage zero-crossing and the current zero-crossing.

Take a look at Open Energy Monitor to see how they are doing it.

1 Like

You could use fft to determine the phase of the volt and current waveforms and the compare the difference in phase, but as suggested, you could just determine the difference in the timing of the peaks of both waveforms and from their times and the periods of the waveforms their phase difference

Start with

  • capturing the waveforms
  • detecting their peaks
  • determining their periods (should be same)
  • time difference between peaks
  • ratio of time difference to period
1 Like

have you had a look at the pzem-004t-v3 module
I have used it to measure voltage, current and power in resistive applications
never had an application requiring power factor

What sort of problems? Please post useful details.

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