En el capítulo anterior……
Si recordais habíamos obtenido un medidor que comprarado con uno comercial nos daba valores bastante parecidos (del orden de un 1%), pero que tenía un problema en el valor devuelto sin carga provocado por el ruido eléctrico del sensor.
Decía que el problema era estético porque por el tipo de producto no se van a medir potencias de 3 ó 4 W, pero cuando no estás utilizando una salida esperas que las lecturas de consumo sean cero y no tengan un comportamiento errático. De hecho creo que es mejor que para unos pocos Watios marque cero que no que por el ruido vaya dando lecturas de potencias negarivas ni cosas por el estilo.
Los sensores de corriente que hemos utilizado tienen dos patillas a las que se les pone un condensador de filtro (FILTER en el esquema), internamente antes del último amplificador operacional del chip se inserta este condensador, eso permite poner el filtro con un condensador más pequeño y sin utilizar la resistencia en serie, porque utilizaremos la resistencia de salida de un operacional. A continuación el esquema interno del sensor:
//**********************************************************************************
//PDU gestionable por carlos Rodríguez
//http://www.zigbe.net
//El Sketch mide voltaje y corriente y calcula los valores de consumo de potencia
//Basado en los algoritmos del la app note de Atmel AVR465 con inspiración en el código de openenergymonitor
//**********************************************************************************
//********************Variables de configuración********************
int NumeroDeMuestras = 3000;
//Pines de entrada de voltaje y corriente
int PinV = 1;
int PinI1 = 2;
int PinI2 = 3;
int PinI3 = 4;
int PinI4 = 5;
int PinI;
//********************Coeficientes de calibración********************
double CalV = 1.62;
double CalI1 = 0.043;
double CalI2 = 0.043;
double CalI3 = 0.043;
double CalI4 = 0.043;
double CalI;
double CalFase1 = 0.7;
double CalFase2 = 0.7;
double CalFase3 = 0.7;
double CalFase4 = 0.7;
double CalFase;
//********************Variables para las muestras********************
int UlMuestraV;
int UlMuestraI;
int MuestraV;
int MuestraI;
int MuestraI0;
int MuestraI1;
int MuestraI2;
int MuestraI3;
//********************Variables filtradas********************
double UlFiltradoV;
double UlFiltradoI;
double FiltradoV;
double FiltradoI;
double filterTemp;
//********************Almacena el voltahe instantaneo con la fase calibrada********************
double calibratedV;
//********************Variables de cálculo de potencia********************
double sqI,sqV,instP,sumI,sumV,sumP;
//********************Variables de salida********************
double PotenciaReal1, PotenciaAparente1, FactorPotencia1, Vrms1, Irms1;
double PotenciaReal2, PotenciaAparente2, FactorPotencia2, Vrms2, Irms2;
double PotenciaReal3, PotenciaAparente3, FactorPotencia3, Vrms3, Irms3;
double PotenciaReal4, PotenciaAparente4, FactorPotencia4, Vrms4, Irms4;
double PotenciaReal, PotenciaAparente, FactorPotencia, Vrms, Irms;
//********************Variables de medida de la energía********************
//Calculation of kwh
//time taken since last measurment timems = tmillis - ltmillis;
unsigned long ltmillis, tmillis, timems;
//time when arduino is switched on... is it 0?
unsigned long startmillis;
//kwhTotal is cumulative kwh today, the other 3 are historCalIX over the last 3 days for comparison.
double kwhTotal =0.0;
//********************Variables de medida de la frecuencia********************
//Tiempo en microsegundos desde el último paso del voltaje por cero
unsigned long vLastZeroMsec;
unsigned long vPeriod;
//Suma de vPeriod para obtener el promedio
unsigned long vPeriodSum;
//Número de periodos
unsigned long vPeriodCount;
//Frequencia
float freq;
//Se utiliza para filtrar lecturas erróneas de vPeriod
//50Hz -> 20000, 60 Hz -> 16666
unsigned long expPeriod = 20000;
unsigned long filterWidth = 2000;
void setup()
{
Serial.begin(115200);
//*******************Setup de la medida de energía*******************
tmillis = millis();
startmillis=tmillis;
pinMode (13, OUTPUT);
//Para aproximar el primer valor
//CalcularEnergia();
}
void loop()
{
//Primer puerto
digitalWrite(13,1); //Enciende el led de la placa
PinI=PinI1; //asigna el pin analógico de I del puerto1
CalI=CalI1; //asigna los valores de calibración del puerto1
CalFase=CalFase1; //Asigna los valores de calibración de fase del puerto1
//CalcularEnergia(); //Calcula la energía
CalcularEnergia(); //Calcula la energía
CalculoRMSPot(); //Calculos de potencia
Vrms1=Vrms; //Asigna resultados a variables
Irms1=Irms;
PotenciaReal1=PotenciaReal;
PotenciaAparente1=PotenciaAparente;
FactorPotencia1=FactorPotencia;
CalculoFrecuencia(); //Calcula la frecuencia
ResetearAcumuladores(); //resetea los acumuladores
//Segundo puerto
digitalWrite(13,0); //Documentado en el puerto1
PinI=PinI2;
CalI=CalI2;
CalFase=CalFase2;
CalcularEnergia();
CalculoRMSPot();
Vrms2=Vrms;
Irms2=Irms;
PotenciaReal2=PotenciaReal;
PotenciaAparente2=PotenciaAparente;
FactorPotencia2=FactorPotencia;
CalculoFrecuencia();
ResetearAcumuladores();
//tercer puerto
digitalWrite(13,1); //Documentado en el puerto1
PinI=PinI3;
CalI=CalI3;
CalFase=CalFase3;
CalcularEnergia();
CalculoRMSPot();
Vrms3=Vrms;
Irms3=Irms;
PotenciaReal3=PotenciaReal;
PotenciaAparente3=PotenciaAparente;
FactorPotencia3=FactorPotencia;
CalculoFrecuencia();
ResetearAcumuladores();
//Cuarto puerto
digitalWrite(13,0); //Documentado en el puerto1
PinI=PinI4;
CalI=CalI4;
CalFase=CalFase1;
CalcularEnergia();
CalculoRMSPot();
Vrms4=Vrms;
Irms4=Irms;
PotenciaReal4=PotenciaReal;
PotenciaAparente4=PotenciaAparente;
FactorPotencia4=FactorPotencia;
CalculoFrecuencia();
ResetearAcumuladores();
//--ENERGY MEASURMENT CALCULATION----------------
//Calculate amount of time since last PotenciaReal measurment.
ltmillis = tmillis;
tmillis = millis();
timems = tmillis - ltmillis;
//Calculate number of kwh consumed.
// REVISAR____>//kwhTotal = kwhTotal + ((PotenciaReal/1000.0) * 1.0/3600.0 * (timems/1000.0));
//Reset accumulators
//Muestra la salida por el puerto serie
Serial.print ("Puerto 1 -> ");
Serial.print(PotenciaReal1);
Serial.print(" W / ");
Serial.print(PotenciaAparente1);
Serial.print(" VA / ");
Serial.print(FactorPotencia1);
Serial.print(" Factor P / ");
Serial.print(Vrms1);
Serial.print(" V rms / ");
Serial.print(Irms1);
Serial.print(" A rms / ");
Serial.print(kwhTotal);
Serial.print(" KWh / ");
Serial.print(freq);
Serial.println(" Hz");
Serial.print ("Puerto 2 -> ");
Serial.print(PotenciaReal2);
Serial.print(" W / ");
Serial.print(PotenciaAparente2);
Serial.print(" VA / ");
Serial.print(FactorPotencia2);
Serial.print(" Factor P / ");
Serial.print(Vrms2);
Serial.print(" V rms / ");
Serial.print(Irms2);
Serial.print(" A rms / ");
Serial.print(kwhTotal);
Serial.print(" KWh / ");
Serial.print(freq);
Serial.println(" Hz");
Serial.print ("Puerto 3 -> ");
Serial.print(PotenciaReal3);
Serial.print(" W / ");
Serial.print(PotenciaAparente3);
Serial.print(" VA / ");
Serial.print(FactorPotencia3);
Serial.print(" Factor P / ");
Serial.print(Vrms3);
Serial.print(" V rms / ");
Serial.print(Irms3);
Serial.print(" A rms / ");
Serial.print(kwhTotal);
Serial.print(" KWh / ");
Serial.print(freq);
Serial.println(" Hz");
Serial.print ("Puerto 4 -> ");
Serial.print(PotenciaReal4);
Serial.print(" W / ");
Serial.print(PotenciaAparente4);
Serial.print(" VA / ");
Serial.print(FactorPotencia4);
Serial.print(" Factor P / ");
Serial.print(Vrms4);
Serial.print(" V rms / ");
Serial.print(Irms4);
Serial.print(" A rms / ");
Serial.print(kwhTotal);
Serial.print(" KWh / ");
Serial.print(freq);
Serial.println(" Hz");
Serial.println("************************************************************************");
}
void CalcularEnergia()
{
UlMuestraV=607;
UlMuestraI=607;
MuestraV=607;
MuestraI=607;
UlFiltradoV=2.5;
UlFiltradoI=2.5;
FiltradoV=2.5;
FiltradoI=2.5;
for (int n=0; n<NumeroDeMuestras; n++)
{
//Variables utilizadas para eliminar el ofset de tensión
// MuestraI0=MuestraI1;
// MuestraI1=MuestraI2;
// MuestraI2=UlMuestraI;
UlMuestraV=MuestraV;
UlMuestraI=MuestraI;
//Lee las muestras de corriente y voltaje
MuestraV = analogRead(PinV);
MuestraI = analogRead(PinI);
if (MuestraI <=610 and MuestraI >=604)
{MuestraI=607;
}
//Serial.println (MuestraI);
// MuestraI3= (MuestraI0+MuestraI1+MuestraI2+UlMuestraI+MuestraI)/5;
//Serial.print (MuestraI);
//Serial.print (";");
//Serial.println (MuestraI3);
// MuestraI=MuestraI3;
//Variables utilizadas para eliminar el ofset de tensión
UlFiltradoV = FiltradoV;
UlFiltradoI = FiltradoI;
//Filtro digital de paso alto para eliminar el Ofset de 2,5V en las entradas
FiltradoV = 0.996*(UlFiltradoV+MuestraV-UlMuestraV);
FiltradoI = 0.996*(UlFiltradoI+MuestraI-UlMuestraI);
//Calibración de fase (para compensar el retraso en el conversor AD)
calibratedV = UlFiltradoV + CalFase * (FiltradoV - UlFiltradoV);
//Calculo del voltaje RMS
sqV= calibratedV * calibratedV; //Eleva al cuadrado
sumV += sqV; //Añade al acumulador
//Calculo de la corriente RMS
sqI = FiltradoI * FiltradoI; //Eleva al cuadrado
//Serial.println (sqI);
sumI += sqI; //Añade al acumulador
//Cálculo de la potencia instantanea
instP = calibratedV * FiltradoI; //Multiplica tensión por corriente
sumP +=instP; //Añade al acumulador
//*******************MEDICIÓN DE FRECUENCIA**************************
if (n==0) vLastZeroMsec = micros();
if (UlFiltradoV < 0 && FiltradoV >= 0 && n>1) //Chequea el paso por cero
{
vPeriod = micros() - vLastZeroMsec; //Periodo de la forma de onda del voltaje
if (vPeriod > (expPeriod-filterWidth) && vPeriod<(expPeriod+filterWidth)) //Filtra las medidas de periodo erróneas
{
vPeriodSum += vPeriod;
vPeriodCount++;
}
vLastZeroMsec = micros();
}
//********************************************************************
}
}
void ResetearAcumuladores()
{
sumV = 0;
sumI = 0;
sumP = 0;
}
void CalculoRMSPot()
{
Vrms = CalV*sqrt(sumV / NumeroDeMuestras); //Cálculo de la raiz cuadrada del promedio del voltaje elevado al cuadrado (RMS) / Aplica los coeficientes de calibración
Vrms= double(int(Vrms*10))/10; //Reduce a uno el número de decimales
Irms = CalI*sqrt(sumI / NumeroDeMuestras); //Cálculo de la raiz cuadrada del promedio de la corriente elevada al cuadrado (RMS) / Aplica los coeficientes de calibración
Irms= double(int(Irms*20))/20; //Dos decimales, pero la segunda cifra solo puede ser 0 ó 5
//calcula los valores de potencia
PotenciaReal = CalV*CalI*sumP / NumeroDeMuestras;
PotenciaReal = double(int(PotenciaReal*10))/10; //Reduce el número de decimales a uno
PotenciaAparente = double(int((Vrms * Irms -4)*10))/10;//El cuatro es una calibración para compensar el algoritmo de reducción de ruido
if (PotenciaAparente<PotenciaReal)
{PotenciaAparente=PotenciaReal;} //En alguna ocasión "rara" Ha dado algún valor inferior que da factor de potencia negativo
if (PotenciaReal<4) // No se detecta una potencia menor de 4W, para evitar los errores que quedan del filtro pasa bajos
{PotenciaReal=0;}
if (PotenciaAparente<4)// No se detecta una potenciao menor de 4VA, para evitar los errores que quedan del filtro pasa bajos
{PotenciaAparente=0;}
FactorPotencia = PotenciaReal / PotenciaAparente;
}
void CalculoFrecuencia()
{
//******************Cáculos de frecuencia*****************
freq = (1000000.0 * vPeriodCount) / vPeriodSum; //divido por 1000 crc
freq= double(int(freq*20))/20; //El segundo decimal es cero o 5
vPeriodSum=0;
vPeriodCount=0;
}

