Cami Can Calders, 8 2º-2ª | 08173 Sant Cugat del Valles info@bmotes.com 932504996

WhatsBee blog

Midiendo la energía: 26 ¿Otro post despues del último?

 

Pues sí….

Tal como comenté en el último artículo nuestro producto ya envía de forma periódica al gateway los valores de corriente rms, tensión rms, potencia aparente, potencia activa, potencia reactiva, frecuencia, etc. Además se identifica, envía el número de serie, el identificador del HW y el del SW.  Como primer prototipo alfa ya nos vale, aunque hay que pulir algunas cosas.

He hecho una nueva versión del firmware (la 10) que incluye un par de novedades, las comento y aprovecho para publicarla.

Para compensar desviaciones de los componentes el sketch tiene una serie de parámetros de calibración, de tensión, de corriente para cada puerto y de fase para cada puerto. Los parámetros de calibración se definen como variables al principio del sketch. Pero ¿cómo los puede cambiar el usuario si necesidad de reescribir el firmware y cargarlo?, tenemos que darle opción.

No tiene ninguna lógica preguntarlos en un interfaz de usuario cada vez que se enchufa la PDU, deberíamos de establecer una calibración por defecto y dar al usuario la posibilidad de cambiarla. Evidentemente no podemos permitirnos que se carguen los parámetros por defecto cada vez que se vaya la corriente, deberíamos de almacenarlos en un área de memoria que no se borre aunque la PDU no esté alimentada, esta memoria es la EEPROM. 

ATMEGA 328, contiene internamente una memoria EEPROM

El ATMEGA328 contiene 4K de memoria EEPROM, los valores que contenía el sketch ahora son los valores por defecto, la primera vez que se arranca el producto se comprueba si están escritos en la EEPROM, y si no lo están se escriben, esto solo sucede la primera vez que se enchufa el producto a la corriente.

Para poder cambiar estos valores he puesto una ventana de comando en el driver, ahora se pueden enviar comandos a la PDU, para encender y apagar relés, para cambiar los valores de calibración y para cambiar los datos de la PDU: número de serie, versión del HW, etc. Simplemente en esta inputbox tenemos que poner XXX:YYY=ZZZZZZ. Para modificar el estado de los relés:

CMD:PX=Y -> dónde X es el puerto e Y es 0 ó 1 en función de si queremos encender o apagar el relé.

Para cambiar un parámetro de configuración: CNF:XXX=YYYYYYYYY, dónde XXX puede tomar los valores CV0 (calibración de V), CIX (Calibración de I para el puertoX), CFX (Calibración de fase para el puertoX), PID (Identificador de producto), SNX(Número de serie), HWV (Versión del HW), SWV (Versión del SW), TLE (Segundos entre envíos de lecturas) o TIN (Segundos entre envíos de la identificación del equipo). Lo que hay despues del signo igual es el valor que queremos asignar. Recuerdo que estos valores se almacenan de forma permanente.

En próximas versiones añadiremos nuevos parámetros para definir el estado por defecto de los relés en el arranque de la PDU, para definir en que momento se quiere cortar la corriente, etc. También utilizaremos la EEPROM para almacenar el consumo acumulado cada hora.

Otra novedad es que ahora la PDU acepta el comando GET del gateway y envía de forma inmediata toda la info, de esta forma podemos alargar los tiempos en los que el gateway envía y controlarlo desde el driver pidiendo la info con un scheduler.

Hay un montón de características que podemos añadir, pero de momento lo dejaremos en este punto, seguiremos trabajando en otros elementos del entorno y al final le daremos un repaso.

Por si a alguien le interesa él fuente de la versión 10 del código es el siguiente (Mis disculpas por el aburrimiento ;-)):

//**********************************************************************************
//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
//**********************************************************************************
//____________________LIBRERIA DE EEPROM PARA ALMACENAR LOS DATOS___________________
#include <EEPROM.h>
//____________________NEWSOFTSERIAL (SOLO PARA DEPURAR)_____________________________
#include <NewSoftSerial.h>
uint8_t ssRX = 8; // Connect Arduino pin 8 to TX of usb-serial device
uint8_t ssTX = 7;// Connect Arduino pin 7 to RX of usb-serial device
NewSoftSerial nss(ssRX, ssTX);
int cuentas =0;
int cuentasZB =0;
//__________________________________XBEE____________________________________________
#include <XBee.h> //Librería Xbeee
#define MAX_PAYLOAD_SIZE 72
XBee xbee = XBee(); //Crea el objeto de Xbee
uint8_t payload[MAX_PAYLOAD_SIZE]; //Crea el puntero para el payload
XBeeAddress64 addr64 = XBeeAddress64(0x00000000, 0x00000000);
ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));
ZBTxStatusResponse txStatus = ZBTxStatusResponse();
// create reusable response objects for responses we expect to handle 
ZBRxResponse rx = ZBRxResponse();
ModemStatusResponse msr = ModemStatusResponse();
String pzb; //Contiene la cadena que se va a enviar en el payload
//*************************Tiempo Entre Lecturas**************************
struct Config {
char OK[3];
char ProductId[10];
char SerialNumber[10];
char HardwareVersion[10];
char SoftwareVersion[10];
double tLecturas; //Tiempo en segundos para leer cada puerto, lo lee de la EEPROM
double tInfo; //Tiempo en segundos para mandar la info del equipo, lo lee de la EEPROM
};
Config defConf = {"OK", "bMotesPDU", "000000001", "   A01.03", "   V07.10", 5, 100};
Config cf;
double mlecturas=0; //Contador del tiempo de lectura
double tUlLectura=0; //Tiempo transcurrido desde la última lectura
double tUlLecInfo=0; //Contador del tiempo de info
//________________________VARIABLES DE CONFIGURACIÓN DEL HARDWARE________________________
const int NumeroDeMuestras = 3000; //Número de muestras para las medidas de energía, 
const int NumPorts =4; //Define el número de puertos de la PDU
int Port=1; //define el puerto incial
int PinV = 1; //Pin de medida de tensión
int PinI[NumPorts+1] = {0, 2, 3, 4, 5}; //Pines de medida de corriente (1 a 4)
int PinRele[NumPorts+1] = {0, 6, 3, 4, 5}; //Pines en los que están conectados los relés
int EstadoRele[NumPorts+1] = {0, 1, 1, 1, 1}; //Por defecto todos los relés están en on TODO: Esto debería de poder ser configurable????
//________________________COEFICIENTES DE CALIBRACIÓN____________________________________
struct Calib {
char OK[3];
double CalV; //Coeficiente de calibración de tensión
double CalI [NumPorts+1]; //Coeficientes de calibración de corriente
double CalFase[NumPorts+1]; //Coeficientes de calibración de fase
double CalOfset;
double DifOfset;
};
Calib defCal = {"OK", 1.62, {0, 0.043, 0.043, 0.043, 0.043}, {0, 0.7, 0.7, 0.7, 0.7}, 607, 3};
Calib cl;
/*double CalV = 1.62; //Coeficiente de calibración de tensión
double CalI [NumPorts+1] = {0, 0.043, 0.043, 0.043, 0.043}; //Coeficientes de calibración de corriente
double CalFase[NumPorts+1] = {0, 0.7, 0.7, 0.7, 0.7}; //Coeficientes de calibración de fase
*/
//________________________VARIABLES PARA LAS MUESTRAS Y ACUMULADORES_____________________
int UlMuestraV, UlMuestraI, MuestraV, MuestraI;
double UlFiltradoV, UlFiltradoI, FiltradoV, FiltradoI, filterTemp, calibratedV;
double sumI,sumV,sumP;
//_________________________VARIABLES PARA ALMACENAR LOS RESULTADOS DE SALIDA_____________
double PotenciaReal[NumPorts+1];
double PotenciaAparente[NumPorts+1];
double FactorPotencia[NumPorts+1];
double Vrms[NumPorts+1];
double Irms[NumPorts+1];
//********************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********************
unsigned long vLastZeroMsec;//Tiempo en microsegundos desde el último paso del voltaje por cero
unsigned long vPeriod;
unsigned long vPeriodSum;//Suma de vPeriod para obtener el promedio
unsigned long vPeriodCount;//Número de periodos
float freq;//Frequencia
unsigned long expPeriod = 20000;//Se utiliza para filtrar lecturas erróneas de vPeriod, 50Hz -> 20000, 60 Hz -> 16666 
unsigned long filterWidth = 2000;
int statusLed = 13;
int errorLed=13;
//___________________________TEMPLATES PARA LA GRABACIÓN EN LA EEPROM_______________________________________
template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
    const byte* p = (const byte*)(const void*)&value;
    int i;
    for (i = 0; i < sizeof(value); i++)
   EEPROM.write(ee++, *p++);
    return i;
}
template <class T> int EEPROM_readAnything(int ee, T& value)
{
    byte* p = (byte*)(void*)&value;
    int i;
    for (i = 0; i < sizeof(value); i++)
   *p++ = EEPROM.read(ee++);
    return i;
}
 
void setup()
{
  xbee.begin(9600);
//*******************Setup de la medida de energía*******************
tmillis = millis();
startmillis=tmillis;
for (int n=1; n<=NumPorts; n++){pinMode (PinRele[n], OUTPUT);} //Inicializa todos los puertos de los relés como salidas 
pinMode (13, OUTPUT); //Inicializa el puerto del LED como salida
EEPROM_readAnything (0, cf); //carga las variables de la EEPROM en cf
  if (defConf.OK == cf.OK){ //suponemos que la EEPROM tiene algo grabado
  //No hace nada
  } 
  else{EEPROM_writeAnything (0 , defConf);
  delay(50); //se espera porque en la EEPROM se tarda en escribir
  EEPROM_readAnything (0, cf); //Carga los datos de la EEPROM en la variable cf
  }
EEPROM_readAnything (100, cl); //carga las variables de la EEPROM en cf
  if (defCal.OK == cl.OK){ //suponemos que la EEPROM tiene algo grabado
  //No hace nada
  } 
  else{EEPROM_writeAnything (100 , defCal);
  delay(50); //se espera porque en la EEPROM se tarda en escribir
  EEPROM_readAnything (100, cl); //Carga los datos de la EEPROM en la variable cf
  }
// Solo depuración
  nss.begin(9600);
  nss.println("Se ha reiniciado el micro!!");
  nss.println (cf.ProductId);
//TODO: Definir el valor de arranque de los puertos por defecto
//TODO: Hacer una función para escribir directamente en la EEPROM desde la red Zigbee
}
//_________________________________BUCLE PRINCIPAL DEL PROGRAMA________________________________________
void loop()
{ 
  if (millis()-tUlLectura >cf.tLecturas*1000){ //Si ha pasado un tiempo superior al de la última lectura
    tUlLectura=millis();//Asigna el tiempo de la última lectura
    if (Port>NumPorts){Port=1;}//si el número de puerto es mayor que el número de puertos vuelve al uno
    flashLed(13, Port, 100); //Activa el Led un número de veces igual al del puerto que está escaneando
    // debug
    nss.println ("  ");
    nss.print ("Calculando energia, Puerto->");
    nss.println (Port);
    
      if (CalcularEnergia(Port)){ //Calcula la energía devuelve true si no se cancela por una entrada serie
        nss.println ("ha vuelto de la funcion de calcular FINALIZADA");
        enviarPaquetePort (Port);
        Port += 1; //Incrementa el número de puerto    
      }
      else {
        nss.println ("ha vuelto de la funcion de calcular CANCELADA");
      }
   }
  if (millis()-tUlLecInfo >cf.tInfo*1000){ //Si ha pasado un tiempo superior al de la última lectura
    nss.println ("Enviando datos del HW");
    tUlLecInfo=millis();//Asigna el tiempo de la última lectura
    enviarPaqueteHW();
  }
 leerZigbee();
} //Fin del loop 
//_______________________________________LEE LOS PAQUETES DE LA RED ZIGBEE________________________________________
 void leerZigbee()
 {
   xbee.readPacket(); //lee el paquete
   cuentasZB += 1;
   if (cuentasZB>200){
     cuentasZB=0;
   nss.print ("#");
  }
  
    if (xbee.getResponse().isAvailable()) {//Si se ha recibido.....
    nss.println ("Se ha recibido un paquete");
      if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) { //Comprueba que se trate de un paquete ZB RX
        xbee.getResponse().getZBRxResponse(rx); //rellena la clase ZB RX
    borrarPayload();
    String comando;
    for (int n=0; n<=rx.getDataLength(); n++){
    payload[n] = rx.getData()[n];
    comando += rx.getData()[n];
    }
     if (comando.substring (0, 4)=="CMD:"){ //Se ha enviado un comando a los relés
        nss.println ("Se ha recibido un comando");
        procesarRele (atoi(&comando[5]), atoi(&comando[7]));
     } else if(comando.substring (0, 4)=="CNF:"){ //Se ha enviado un cambio en la configuración
        nss.println ("Se ha recibido un comando de CONFIGURACION");
        procesarConfig (comando.substring (4, 7), comando.substring (8, 17));
     } else if(comando.substring (0, 3)=="get"){ //El gateway ha pedido que le manden la configuración
        nss.println ("El Gateway ha pedido información de estado");
          for (int i=1; i<=NumPorts; i++){//Manda un paquete de cada puerto
            enviarPaquetePort(i);}
     }    enviarPaqueteHW ();//Manda la info del HW
      } else if (xbee.getResponse().getApiId() == MODEM_STATUS_RESPONSE) {
        xbee.getResponse().getModemStatusResponse(msr); //El Xbee local envía estas respuestas ante ciertos eventos, por ejemplo asociación desasociación
          if (msr.getStatus() == ASSOCIATED) {flashLed(statusLed, 10, 10);} // Enciende el LED 10 veces para indicar que está asociado
          else if (msr.getStatus() == DISASSOCIATED) {flashLed(errorLed, 5, 10);}// Enciende el LED 5 veces para indicar que está desasociado
          else {flashLed(statusLed, 3, 10);}// Otro statos
      } else {flashLed(errorLed, 1, 25);} // Error inesperado
    }
 }
//_______________________PROCESAR COMANDO RELE____________________________________________________
void procesarRele (int Pto, int Est)
{
  digitalWrite (PinRele[Pto], !Est); //Cambia el estado del puerto
  EstadoRele[Pto]=Est; //Cambia la variable que almacena el estado
  enviarPaquetePort (Pto); //Envia un paquete con la información del puerto actualizada
//DEBUG
  nss.print ("Dentro de la función de los reles. Puerto->");
  nss.print (Pto);
  nss.print (" / Estado->");
  nss.println (Est);
//DEBUG  
}
//_______________________PROCESAR CONFIGURACION____________________________________________________
void procesarConfig (String cmd, String val){
if (cmd=="CV0"){
} else if (cmd=="CI1"){
} else if (cmd=="CI2"){
} else if (cmd=="CI3"){
} else if (cmd=="CI4"){
} else if (cmd=="CF1"){
} else if (cmd=="CF2"){
} else if (cmd=="CF3"){
} else if (cmd=="CF4"){
} else if (cmd=="PID"){
  for (int i=0; i<10; i++){cf.ProductId[i]=val[i];}
} else if (cmd=="SNX"){
  for (int i=0; i<10; i++){cf.SerialNumber[i]=val[i];}
} else if (cmd=="HWV"){
  for (int i=0; i<10; i++){cf.HardwareVersion[i]=val[i];}
} else if (cmd=="SWV"){
  for (int i=0; i<10; i++){cf.SoftwareVersion[i]=val[i];}
} else if (cmd=="TLE"){
} else if (cmd=="TIN"){
}
//DEBUG
nss.print ("Se ha recibido el comando ");
nss.print (cmd);
nss.print ("=");
nss.println (val);
//DEBUG
EEPROM_writeAnything (0, cf);
EEPROM_writeAnything (100, cl);
delay(50);
enviarPaqueteHW();
}
//______________________CALCULA LOS VALORES DE LA ENERGIA__________________________________________
boolean CalcularEnergia(int Port)
{
  UlMuestraV=cl.CalOfset; UlMuestraI=cl.CalOfset;
  MuestraV=cl.CalOfset; MuestraI=cl.CalOfset;
  UlFiltradoV=2.5; UlFiltradoI=2.5;
  FiltradoV=2.5; FiltradoI=2.5;
  double MaxOfset=cl.CalOfset + cl.DifOfset;
  double MinOfset=cl.CalOfset - cl.DifOfset;
  
  for (int n=0; n<NumeroDeMuestras; n++)
  {
     if (digitalRead (0)==LOW) {return false;} //Si se ha recobodo algún dato por la UART cancela inmediatamente el bucle
     UlMuestraV=MuestraV;   //Variables utilizadas para eliminar el ofset de tensión
     UlMuestraI=MuestraI;   //Variables utilizadas para eliminar el ofset de corriente
     MuestraV = analogRead(PinV); //Lee las muestras de voltaje
     MuestraI = analogRead(PinI[Port]); //Lee las muestras de corriente
     if (MuestraI <=MaxOfset and MuestraI >=MinOfset){MuestraI=cl.CalOfset;} //Filtro de histéresis para eliminar el ruido en el sensor de corriente
     UlFiltradoV = FiltradoV;   //Variables utilizadas para eliminar el ofset de tensión
     UlFiltradoI = FiltradoI;   //Variables utilizadas para eliminar el ofset de corriente
     FiltradoV = 0.996*(UlFiltradoV+MuestraV-UlMuestraV);   //Filtro digital de paso alto para eliminar el Ofset de 2,5V en la entrada de tensión
     FiltradoI = 0.996*(UlFiltradoI+MuestraI-UlMuestraI);   //Filtro digital de paso alto para eliminar el Ofset de 2,5V en las entradas de corriente
     calibratedV = UlFiltradoV + cl.CalFase[Port] * (FiltradoV - UlFiltradoV);   //Calibración de fase (para compensar el retraso en el conversor AD)
     sumV += calibratedV * calibratedV; //Añade al acumulador el cuadrado del voltaje  
     sumI += FiltradoI * FiltradoI; //Eleva al cuadrado y añade al acumulador
     sumP +=calibratedV * FiltradoI;  //Cálculo de la potencia instantanea Multiplica tensión por corriente y Añade al acumulador
     //Medición de la 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();
     }
  }
Vrms[Port] = cl.CalV*sqrt(sumV / NumeroDeMuestras); //Cálculo de la raiz cuadrada del promedio del voltaje elevado al cuadrado (RMS) / Aplica los coeficientes de calibración
Irms[Port] = cl.CalI[Port]*sqrt(sumI / NumeroDeMuestras); //Cálculo de la raiz cuadrada del promedio de la corriente elevada al cuadrado (RMS) / Aplica los coeficientes de calibración 
PotenciaReal[Port] = cl.CalV*cl.CalI[Port]*sumP / NumeroDeMuestras;//calcula las potencias
PotenciaAparente[Port] = double(int((Vrms[Port] * Irms[Port] -4)*10))/10;//El cuatro es una calibración para compensar el algoritmo de reducción de ruido
if (PotenciaAparente[Port]<PotenciaReal[Port]){PotenciaAparente[Port]=PotenciaReal[Port];} //En alguna ocasión "rara" Ha dado algún valor inferior que da factor de potencia negativo
if (PotenciaReal[Port]<4){PotenciaReal[Port]=0;}// No se detecta una potencia menor de 4W, para evitar los errores que quedan del filtro pasa bajos
if (PotenciaAparente[Port]<4){PotenciaAparente[Port]=0;}// No se detecta una potenciao menor de 4VA, para evitar los errores que quedan del filtro pasa bajos
FactorPotencia[Port] = PotenciaReal[Port] / PotenciaAparente[Port];
freq = (1000000.0 * vPeriodCount) / vPeriodSum; //divido por 1000 crc //Cálculo de la frecuencia
vPeriodSum=0; //resetea los acumuladores
vPeriodCount=0; //resetea los acumuladores
sumV = 0;//resetea los acumuladores
sumI = 0;//resetea los acumuladores
sumP = 0;//resetea los acumuladores
return true;
}
//____________________________ENVIAR PAQUETE DE UN PUERTO_______________________________________
void enviarPaquetePort (int Port)
{
    pzb= "PT=";
    pzb += floatToString(Port, 0);
    pzb += ";P=";
    pzb += floatToString(PotenciaReal[Port], 1);
    pzb += "W;S=";
    pzb += floatToString(PotenciaAparente[Port], 1);
    pzb += "VA;V=";
    pzb += floatToString(Vrms[Port], 1);
    pzb += "V;I=";
    pzb += floatToString(Irms[Port], 1);
    pzb += "A;F=";
    pzb += floatToString(freq, 1);
    pzb += "Hz;ST=";
    pzb += floatToString(EstadoRele[Port], 0);
    pzb += "st;";
    borrarPayload();
    for (int i=0; i<=pzb.length(); i++){payload[i] = pzb[i];} //Mete el valor de la string pzb en el payload 
    xbee.send(zbTx); //Envía el paquete a la red Zigbee
    //DEBUG
    nss.println ("enviarPaquetePort->ha enviado el paquete");
    nss.println(pzb);
    //
}
//____________________________ENVIAR PAQUETE DEL HARDWARE_______________________________________
void enviarPaqueteHW ()
{
    pzb= "PID=";
    pzb += cf.ProductId;
    pzb += ";HW=";
    pzb += cf.HardwareVersion;
    pzb += ";SW=";
    pzb += cf.SoftwareVersion;
    pzb += ";SN=";
    pzb += cf.SerialNumber;
    pzb += ";";
    borrarPayload();
    for (int i=0; i<=pzb.length(); i++){payload[i] = pzb[i];} //Copia la String en el Payload
    xbee.send(zbTx);
    //DEBUG
    nss.println ("enviarPaqueteHW->ha enviado el paquete");
    nss.println(pzb);
    //
}
//____________________________BORRAR PAYLOAD_______________________________________
void borrarPayload()
{
      for ( int n=0; n<=MAX_PAYLOAD_SIZE; n++){payload[n]= ' ';} //borra el contenido de la valiable del payload
}
//_____________CONVERTIR LAS VARIABLES DOUBLE EN TEXTO______________________________
String floatToString(double number, uint8_t digits) 
{ 
  String resultString = "";
  if (number < 0.0){  // Handle negative numbers
     resultString += "-";
     number = -number;
  }
  double rounding = 0.5;// Redondea correctamente (1.999, 2) se imprimem como "2.00"
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
    number += rounding;
  unsigned long int_part = (unsigned long)number;// Extrae la parte entera del número y la imprime
  double remainder = number - (double)int_part;
  resultString += int_part;
  if (digits > 0) {resultString += ".";}   // Imprime el punto digital si hay decimales
  while (digits-- > 0){  // Estrae los dígitos que quedan de uno en uno
    remainder *= 10.0;
    int toPrint = int(remainder);
    resultString += toPrint;
    remainder -= toPrint; 
  } 
  return resultString;
}
//_________________MUESTRA LOS CÓDIGOS DE ERROR EN EL LED____________________________
void flashLed(int pin, int times, int wait) {
    for (int i = 0; i < times; i++) {
      digitalWrite(pin, HIGH);
      delay(wait);
      digitalWrite(pin, LOW);
      if (i + 1 < times) {delay(wait);}
    }
}

2 Comentarios

  1. riscking

    Hola Carlos, acabo de descubrir vuestra pagina gracias al trabajo del medidor de energía y esta muy currado, mi mas sincera enhorabuena.
    Yo estoy intentando hacer algo igual con una placa tb ACS712 de 30A pero ya ensamblada para usarla en arduino.
    http://img.alibaba.com/wsphoto/v0/498488806_2/F306-ACS712-module-30A-Hall-Current-Sensor-Module.jpg
    Ahora mismo mis conociminetos acerca de este tema dejan mucho que desear por eso a ver si me puedes hechar una mano.
    Partiendo del codigo del capitulo 19 que es mas resumido que el ultimo porque no lleva lo del xbee.
    yo he conseguido que a 510 sea estable la medicion, tomando como partida ese valor como 0 osea 2,5v.
    tus valores de los coeficientes de calibracion, salvo el CalFase que es 0.707 los demas no se como los has calculado.
    y en el filtrado el 0.996 de donde sale??
    Espero que me puedas hechar una mano, para lo que sea mi mail es riscking@hotmail.com
    Espero tu respuesta.
    UN saludo y aseguir asi.

    1. Carlos (Publicaciones Autor)

      Hola,

      bueno, esta entrada ya tiene unos meses, no tengo muy buena memoria. De todas formas me has preguntado por una de las cosas que menos entendí. Solo leyendo el 0,996 de tu mensaje ya se a lo que te refieres.

      Échale un ojo a este documento:
      http://www.atmel.com/dyn/resources/prod_documents/doc2566.pdf

      concretamente a la página 11:

      When the ISR is started its first task is to read sampled data from the ADC and store
      it in a location in SRAM where it cannot be overwritten by new data. The first task to
      perform on the data is then to remove any DC offset. This is carried out using a digital
      High-Pass Filter (HPF) of type Infinite Impulse Response (IIR). The transfer function
      of the filter can be written as follows:
      Equation 11. High-Pass Filter, Infinite Impulse Response Type.
      y[n] = 0.996 × y[n −1]+ 0.996 × x[n]- 0.996 × x[n -1],

      de aqui es de donde saqué la función para el filtro paso alto, entiendo que el número tiene que ver con la frecuencia de corte, pero no estoy seguro.

      El resto de las calibraciones es puramente empírico, ajustando las desviaciones de fase con un medidor de potencia. Al final los resultados eran bastante satisfactorios, pero había problemas posiblemente con los redondeos de ahi los ajustes finales de no detectar potencias menores de 4W y los que están entre esas líneas.

      En realidad muchas de las cosas que hay en el blog son experimentos inacabados, a los que falta darles un par de vueltas, espero ir retomando.

      Aunque esto parece más un cuaderno de bitácora pretendemos hacer un espacio participativo, así que no te cortes y si haces algo interesante publícalo.

      Saludos,

Dejar un comentario