{"id":620,"date":"2010-10-31T01:06:06","date_gmt":"2010-10-31T00:06:06","guid":{"rendered":"http:\/\/www.zigbe.net\/?p=620"},"modified":"2010-10-31T01:06:06","modified_gmt":"2010-10-31T00:06:06","slug":"midiendo-la-energia-22-resuelto-el-camino-de-ida","status":"publish","type":"post","link":"https:\/\/blog.whatsbee.net\/?p=620","title":{"rendered":"Midiendo la energ\u00eda: 22 resuelto el camino de ida"},"content":{"rendered":"<div class=\"mceTemp mceIEcenter\">\u00a0<\/div>\n<p>Releyendo el art\u00edculo anterior veo que es bastante espeso, no es mi intenci\u00f3n aburrir, pero el art\u00edculo es fiel reflejo de lo aburrida que es esta fase.<\/p>\n<p>He mejorado mucho el c\u00f3digo de la PDU, que ha quedado bastante reducido, basicamente lo que he hecho es meter las variables en matrices y recorrer el c\u00f3digo n veces (donde n es el n\u00famero de puertos) dentro de un bucle for.<\/p>\n<p>He a\u00f1adido la librer\u00eda de Xbee y he migrado a la versi\u00f3n 21 del entorno Arduino, que ya es capaz de concatenar dos cadenas sin utilizar librer\u00edas adicionales. Estaba trabajando con la versi\u00f3n 17 y dedicado durante m\u00e1s de una hora a no ser capaz de construir una cadena que sea la concatenaci\u00f3n de dos variables.<\/p>\n<p>Desde la librer\u00eda Xbee genero un paquete, toda la info la meto en el payload, con el siguiente formato: \u00abPT=1;VRMS=220.0V;IRMS=0.0A&#8230;.\u00bb cada valor es extraido del algoritmo de la PDU y se mete en el payload del paquete.<\/p>\n<div id=\"attachment_621\" style=\"width: 410px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.whatsbee.net\/wp-content\/uploads\/2010\/10\/cpx-gateways_lg.jpg\"><img aria-describedby=\"caption-attachment-621\" loading=\"lazy\" class=\"size-full wp-image-621\" title=\"cpx-gateways_lg\" src=\"http:\/\/blog.whatsbee.net\/wp-content\/uploads\/2010\/10\/cpx-gateways_lg.jpg\" alt=\"\" width=\"400\" height=\"400\" srcset=\"https:\/\/blog.whatsbee.net\/wp-content\/uploads\/2010\/10\/cpx-gateways_lg.jpg 400w, https:\/\/blog.whatsbee.net\/wp-content\/uploads\/2010\/10\/cpx-gateways_lg-150x150.jpg 150w, https:\/\/blog.whatsbee.net\/wp-content\/uploads\/2010\/10\/cpx-gateways_lg-300x300.jpg 300w\" sizes=\"(max-width: 400px) 100vw, 400px\" \/><\/a><p id=\"caption-attachment-621\" class=\"wp-caption-text\">Gateways de Digi<\/p><\/div>\n<p>La direcci\u00f3n de destino de los paquetes es la 0x00000000 0x00000000, que env\u00eda directamente al coordinador de la red. El coordinador es el gateway de Digi, he creado un driver para manejar los paquetes espec\u00edficos de nuestro driver, que lo que hace es recoger los valores y asignarlos a propiedades. Los valores que se calculan a partir de los existentes se calculan en el driver y tambi\u00e9n se asignan a propiedades. Todo funciona correctamente, cada segundo la PDU env\u00eda un paquete correspondiente a los datos de un puerto que es interpretado por el gateway, representado en la web del equipo, etc.<\/p>\n<p>El siguiente paso ya es el final, adem\u00e1s de recibir lo que manda la PDU e interpretarlo debemos de ser capaces de enviar info del X4 a la PDU, para ser capaces de obtener el dato en el momento que queramos, activar y descativar los rel\u00e9s, etc.<\/p>\n<p>Existe un problema importante (que no tengo claro en este momento como voy a resolver), la PDU tarda aproximadamente un segundo en tomar 3000 valorores de tensi\u00f3n y corriente y en c\u00e1lcular las potencias. Durante este segundo no se pueden atender peticiones de la red Zigbee, porque el timing es importante, por lo que corremos el riesgo de que si intentamos cambiar el estado de un rel\u00e9 mientras mide la energ\u00eda la PDU no har\u00e1 la acci\u00f3n.<\/p>\n<p>Las posibles soluciones para resolver este tema son:<\/p>\n<ul>\n<li>Si la UART tiene cache, analizar el paquete pasado un segundo y apagar en ese momento el rel\u00e9 (con ese retraso)<\/li>\n<li>Si podemos disparar una interrupci\u00f3n en el momento en el que llegue el paquete podemos descartar esa medida que est\u00e1 tomando, atender la petici\u00f3n de la red y, si es necesario, tomar la medida posteriormente.<\/li>\n<\/ul>\n<p>Os mantendr\u00e9 informados&#8230;.<\/p>\n<p>La nueva versi\u00f3n del firmware de la PDU es la siguiente:<\/p>\n<pre>\/\/**********************************************************************************\n\/\/PDU gestionable por carlos Rodr\u00edguez\n\/\/http:\/\/www.zigbe.net\n\/\/El Sketch mide voltaje y corriente y calcula los valores de consumo de potencia\n\/\/Basado en los algoritmos del la app note de Atmel AVR465 con inspiraci\u00f3n en el c\u00f3digo de openenergymonitor\n\/\/**********************************************************************************<\/pre>\n<pre>\/\/******************************* X B E E **************************\n#include &lt;XBee.h&gt; \/\/Librer\u00eda Xbeee\nXBee xbee = XBee(); \/\/Crea el objeto de Xbee\nuint8_t payload[70]; \/\/Crea el puntero para el payload<\/pre>\n<pre>XBeeAddress64 addr64 = XBeeAddress64(0x00000000, 0x00000000);\nZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));\nZBTxStatusResponse txStatus = ZBTxStatusResponse();\n\/\/******************************************************************<\/pre>\n<pre>\/\/********************Variables de configuraci\u00f3n********************\nconst int NumeroDeMuestras = 3000; \/\/N\u00famero de muestras para las medidas de energ\u00eda, \nconst int NumPorts =4; \/\/Define el n\u00famero de puertos de la PDU\n\/\/Pines de entrada de voltaje y corriente\nint PinV = 1; \/\/Pin de medida de tensi\u00f3n\nint PinI[NumPorts+1] = {0, 2, 3, 4, 5}; \/\/Pines de medida de corriente (1 a 4)\n\/\/********************Coeficientes de calibraci\u00f3n********************\ndouble CalV = 1.62; \/\/Coeficiente de calibraci\u00f3n de tensi\u00f3n\ndouble CalI [NumPorts+1] = {0, 0.043, 0.043, 0.043, 0.043}; \/\/Coeficientes de calibraci\u00f3n de corriente\ndouble CalFase[NumPorts+1] = {0, 0.7, 0.7, 0.7, 0.7}; \/\/Coeficientes de calibraci\u00f3n de fase\n\/\/********************Variables para las muestras********************\nint UlMuestraV, UlMuestraI, MuestraV, MuestraI;\n\/\/********************Variables filtradas********************\ndouble UlFiltradoV, UlFiltradoI, FiltradoV, FiltradoI, filterTemp;\n\/\/********************Almacena el voltahe instantaneo con la fase calibrada********************\ndouble calibratedV;\n\/\/********************Variables de c\u00e1lculo de potencia********************\ndouble sqI,sqV,instP,sumI,sumV,sumP;\n\/\/********************Variables de salida********************\ndouble PotenciaReal[5];\ndouble PotenciaAparente[5];\ndouble FactorPotencia[5];\ndouble Vrms[5];\ndouble Irms[5];\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \n\/\/********************Variables de medida de la energ\u00eda********************\n\u00a0\u00a0\u00a0 \/\/Calculation of kwh\n\u00a0\u00a0\u00a0 \/\/time taken since last measurment timems = tmillis - ltmillis;\n\u00a0\u00a0\u00a0 unsigned long ltmillis, tmillis, timems;\n\u00a0\u00a0\u00a0 \/\/time when arduino is switched on... is it 0?\n\u00a0\u00a0\u00a0 unsigned long startmillis;\n\u00a0\u00a0\u00a0 \/\/kwhTotal is cumulative kwh today, the other 3 are historCalIX over the last 3 days for comparison.\n\u00a0\u00a0\u00a0 double kwhTotal =0.0;<\/pre>\n<pre>\/\/********************Variables de medida de la frecuencia********************\n\/\/Tiempo en microsegundos desde el \u00faltimo paso del voltaje por cero\nunsigned long vLastZeroMsec;\nunsigned long vPeriod;\n\/\/Suma de vPeriod para obtener el promedio\nunsigned long vPeriodSum;\n\/\/N\u00famero de periodos\nunsigned long vPeriodCount;\n\/\/Frequencia\nfloat freq;\n\/\/Se utiliza para filtrar lecturas err\u00f3neas de vPeriod\n\/\/50Hz -&gt; 20000, 60 Hz -&gt; 16666 \nunsigned long expPeriod = 20000;\nunsigned long filterWidth = 2000;<\/pre>\n<pre>String pzb; \/\/Contiene la cadena que se va a enviar en el payload\nint statusLed = 13;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \nvoid setup()\n{\n\u00a0 xbee.begin(9600);\n\/\/*******************Setup de la medida de energ\u00eda*******************\ntmillis = millis();\nstartmillis=tmillis;<\/pre>\n<pre>pinMode (13, OUTPUT);\n}<\/pre>\n<pre>void loop()\n{<\/pre>\n<pre>for (int Port=1; Port&lt;=NumPorts; Port++) {<\/pre>\n<pre>CalcularEnergia(Port); \/\/Calcula la energ\u00eda<\/pre>\n<pre>\/\/Parte del Zigbee<\/pre>\n<pre>pzb= \"PT=\" + floatToString(Port, 0) +\";P=\" + floatToString(PotenciaReal[Port], 1) + \"W;S=\" + floatToString(PotenciaAparente[Port], 1)+ \"VA;V=\" + floatToString(Vrms[Port], 1)+ \"V;I=\" + floatToString(Irms[Port], 1) + \"A;F=\" + floatToString(freq, 1) + \"Hz;\";\nfor (int n=0; n&lt;=pzb.length(); n++){\npayload[n] = pzb[n];\n}\nxbee.send(zbTx);<\/pre>\n<pre>}\u00a0<\/pre>\n<pre>}<\/pre>\n<pre>void CalcularEnergia(int Port)\n{\n\u00a0 UlMuestraV=607;\n\u00a0 UlMuestraI=607;\n\u00a0 MuestraV=607;\n\u00a0 MuestraI=607;\n\u00a0 UlFiltradoV=2.5;\n\u00a0 UlFiltradoI=2.5;\n\u00a0 FiltradoV=2.5;\n\u00a0 FiltradoI=2.5;\n\u00a0 \nfor (int n=0; n&lt;NumeroDeMuestras; n++)\n{\n\u00a0\u00a0 \/\/Variables utilizadas para eliminar el ofset de tensi\u00f3n\n\u00a0\u00a0 UlMuestraV=MuestraV;\n\u00a0\u00a0 UlMuestraI=MuestraI;\n\u00a0\n\u00a0\u00a0 \/\/Lee las muestras de corriente y voltaje\n\u00a0\u00a0 MuestraV = analogRead(PinV);\n\u00a0\u00a0 MuestraI = analogRead(PinI[Port]);<\/pre>\n<pre>if (MuestraI &lt;=610 and MuestraI &gt;=604)\n{MuestraI=607;\n}\n\u00a0\u00a0 \/\/Variables utilizadas para eliminar el ofset de tensi\u00f3n\n\u00a0\u00a0 UlFiltradoV = FiltradoV;\n\u00a0\u00a0 UlFiltradoI = FiltradoI;\n\u00a0 \n\u00a0\u00a0 \/\/Filtro digital de paso alto para eliminar el Ofset de 2,5V en las entradas\n\u00a0\u00a0 FiltradoV = 0.996*(UlFiltradoV+MuestraV-UlMuestraV);\n\u00a0\u00a0 FiltradoI = 0.996*(UlFiltradoI+MuestraI-UlMuestraI);<\/pre>\n<pre>\u00a0\u00a0 \/\/Calibraci\u00f3n de fase (para compensar el retraso en el conversor AD)\n\u00a0\u00a0 calibratedV = UlFiltradoV + CalFase[Port] * (FiltradoV - UlFiltradoV);\n\u00a0 \n\u00a0\u00a0 \/\/Calculo del voltaje RMS\n\u00a0\u00a0 sqV= calibratedV * calibratedV;\u00a0\u00a0 \/\/Eleva al cuadrado\n\u00a0\u00a0 sumV += sqV; \/\/A\u00f1ade al acumulador<\/pre>\n<pre>\u00a0\u00a0 \/\/Calculo de la corriente RMS\n\u00a0\u00a0 sqI = FiltradoI * FiltradoI; \/\/Eleva al cuadrado\n\u00a0\/\/Serial.println (sqI);\n\u00a0\u00a0 sumI += sqI; \/\/A\u00f1ade al acumulador<\/pre>\n<pre>\u00a0\u00a0 \/\/C\u00e1lculo de la potencia instantanea\n\u00a0\u00a0 instP = calibratedV * FiltradoI; \/\/Multiplica tensi\u00f3n por corriente\n\u00a0\u00a0 sumP +=instP;\u00a0 \/\/A\u00f1ade al acumulador\n\u00a0\u00a0 \n\u00a0\u00a0 \/\/*******************MEDICI\u00d3N DE FRECUENCIA**************************\n\u00a0\u00a0 if (n==0) vLastZeroMsec = micros();<\/pre>\n<pre>\u00a0\u00a0 if (UlFiltradoV &lt; 0 &amp;&amp; FiltradoV &gt;= 0 &amp;&amp; n&gt;1)\u00a0\u00a0\u00a0 \/\/Chequea el paso por cero\n\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0 vPeriod = micros() - vLastZeroMsec; \/\/Periodo de la forma de onda del voltaje\n\u00a0\u00a0\u00a0\u00a0 if (vPeriod &gt; (expPeriod-filterWidth) &amp;&amp; vPeriod&lt;(expPeriod+filterWidth)) \/\/Filtra las medidas de periodo err\u00f3neas\n\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 vPeriodSum += vPeriod;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 vPeriodCount++;\n\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0\u00a0 vLastZeroMsec = micros();\n\u00a0\u00a0 }\n\u00a0\u00a0 \/\/********************************************************************\n}\n\/\/Calcula tensiones y corrientes\nVrms[Port] = CalV*sqrt(sumV \/ NumeroDeMuestras); \/\/C\u00e1lculo de la raiz cuadrada del promedio del voltaje elevado al cuadrado (RMS) \/ Aplica los coeficientes de calibraci\u00f3n\nVrms[Port]= double(int(Vrms[Port]*10))\/10; \/\/Reduce a uno el n\u00famero de decimales\nIrms[Port] = CalI[Port]*sqrt(sumI \/ NumeroDeMuestras); \/\/C\u00e1lculo de la raiz cuadrada del promedio de la corriente elevada al cuadrado (RMS) \/ Aplica los coeficientes de calibraci\u00f3n \nIrms[Port]= double(int(Irms[Port]*20))\/20; \/\/Dos decimales, pero la segunda cifra solo puede ser 0 \u00f3 5\n\/\/calcula las potencias\nPotenciaReal[Port] = CalV*CalI[Port]*sumP \/ NumeroDeMuestras;\nPotenciaReal[Port] = double(int(PotenciaReal[Port]*10))\/10; \/\/Reduce el n\u00famero de decimales a uno\nPotenciaAparente[Port] = double(int((Vrms[Port] * Irms[Port] -4)*10))\/10;\/\/El cuatro es una calibraci\u00f3n para compensar el algoritmo de reducci\u00f3n de ruido\nif (PotenciaAparente[Port]&lt;PotenciaReal[Port])\n{PotenciaAparente[Port]=PotenciaReal[Port];} \/\/En alguna ocasi\u00f3n \"rara\" Ha dado alg\u00fan valor inferior que da factor de potencia negativo\nif (PotenciaReal[Port]&lt;4) \/\/ No se detecta una potencia menor de 4W, para evitar los errores que quedan del filtro pasa bajos\n{PotenciaReal[Port]=0;}\nif (PotenciaAparente[Port]&lt;4)\/\/ No se detecta una potenciao menor de 4VA, para evitar los errores que quedan del filtro pasa bajos\n{PotenciaAparente[Port]=0;}\nFactorPotencia[Port] = PotenciaReal[Port] \/ PotenciaAparente[Port];\n\/\/C\u00e1lculo de la frecuencia\nfreq = (1000000.0 * vPeriodCount) \/ vPeriodSum; \/\/divido por 1000 crc\nfreq= double(int(freq*20))\/20; \/\/El segundo decimal es cero o 5\nvPeriodSum=0; \nvPeriodCount=0;\n\/\/resetea los acumuladores\nsumV = 0;\nsumI = 0;\nsumP = 0;<\/pre>\n<pre>}\n\/\/-----------------------------------------------------------------------------------------<\/pre>\n<pre>\/\/--------------Funci\u00f3n para convertir las variables double en texto-----------------------\nString floatToString(double number, uint8_t digits) \n{ \n\u00a0 String resultString = \"\";\n\u00a0 \/\/ Handle negative numbers\n\u00a0 if (number &lt; 0.0)\n\u00a0 {\n\u00a0\u00a0\u00a0\u00a0 resultString += \"-\";\n\u00a0\u00a0\u00a0\u00a0 number = -number;\n\u00a0 }\n\u00a0 \/\/ Redondea correctamente (1.999, 2) se imprimem como \"2.00\"\n\u00a0 double rounding = 0.5;\n\u00a0 for (uint8_t i=0; i&lt;digits; ++i)\n\u00a0\u00a0\u00a0 rounding \/= 10.0;\n\u00a0\u00a0\u00a0 number += rounding;\n\u00a0 \/\/ Extrae la parte entera del n\u00famero y la imprime\n\u00a0 unsigned long int_part = (unsigned long)number;\n\u00a0 double remainder = number - (double)int_part;\n\u00a0 resultString += int_part;\n\u00a0 \/\/ Imprime el punto digital si hay decimales\n\u00a0 if (digits &gt; 0)\n\u00a0\u00a0\u00a0 resultString += \".\"; \n\u00a0 \/\/ Estrae los d\u00edgitos que quedan de uno en uno\n\u00a0 while (digits-- &gt; 0)\n\u00a0 {\n\u00a0\u00a0\u00a0 remainder *= 10.0;\n\u00a0\u00a0\u00a0 int toPrint = int(remainder);\n\u00a0\u00a0\u00a0 resultString += toPrint;\n\u00a0\u00a0\u00a0 remainder -= toPrint; \n\u00a0 } \n\u00a0 return resultString;\n}\n\/\/--------------------------------------------------------------------------------------<\/pre>\n<pre>\/\/------------Funci\u00f3n para mostrar por el led los c\u00f3digos de error----------------------\nvoid flashLed(int pin, int times, int wait) {\n\u00a0\u00a0\u00a0 for (int i = 0; i &lt; times; i++) {\n\u00a0\u00a0\u00a0\u00a0\u00a0 digitalWrite(pin, HIGH);\n\u00a0\u00a0\u00a0\u00a0\u00a0 delay(wait);\n\u00a0\u00a0\u00a0\u00a0\u00a0 digitalWrite(pin, LOW);\n\u00a0\u00a0\u00a0\u00a0\u00a0 if (i + 1 &lt; times) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 delay(wait);\n\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0 }\n}\n\/\/--------------------------------------------------------------------------------------<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u00a0 Releyendo el art\u00edculo anterior veo que es bastante espeso, no es mi intenci\u00f3n aburrir, pero el art\u00edculo es fiel reflejo de lo aburrida que es esta fase. He mejorado mucho el c\u00f3digo de la PDU, que ha quedado bastante&hellip; <br \/><a class=\"read-more-button\" href=\"https:\/\/blog.whatsbee.net\/?p=620\">Leer m\u00e1s<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0},"categories":[16,17,2,9,10],"tags":[28,35,37,56,76,102,109,110,111,173,175,197],"_links":{"self":[{"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=\/wp\/v2\/posts\/620"}],"collection":[{"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=620"}],"version-history":[{"count":0,"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=\/wp\/v2\/posts\/620\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=620"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=620"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.whatsbee.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}