29 nov. 2015

Monta tu propio Monitor de Energía por 30€ totalmente personalizable

Un Arduino UNO con TFT de 2,4" monitorizando el consumo de una casa

La crisis continúa, así que hay que ingeniárselas para dejar de enriquecer a las eléctricas, ya que el gobierno, con las subidas y el "peaje al sol", está en clara connivencia con ellas (no sea que se arruinen, se produzcan cortes y tengamos que rescatarlas entre todos... en fin, sin palabras).
Así que para saber si nos conviene bajar la potencia o cambiar a la TDH, o bien hacemos cálculos aproximados, o ponemos otro ICP (para saber si podemos bajar la potencia) o bien nos instalamos un monitor de energía, o, como vamos a ver en esta entrada, nos lo fabricamos nosotros mismos.
IMPORTANTE: Si tienes contador digital, es muy posible que puedas consultar consumos horarios en la web del suministrador y poder ver si te conviene la DH. (Las eléctricas tienen hasta 2018 para cambiar todos los contadores a Digitales con Telegestión, pero puedes pedir que te lo pongan ya por menos de 20€). 

Versión 1.0 del programa ya depurado
Es muy raro que en una vivienda no salga más a cuenta contratar la PVPC con TDH (Tarifa con Discriminación Horaria), ya que por las diferencias de precios y horarios, basta con consumir al menos el 30% por la noche para ahorrar; al 95% de las casas les convendría cambiarse, pasando a ahorrar algo sin cambiar los hábitos, o a ahorrar bastante cambiándolos:


De ahí ha surgido este pequeño proyecto de los ratos libres de un par de semanas, un Monitor de Energía basado en Arduíno, un RTC y una pequeña pantalla TFT, con muchas posibilidades de uso.

No es tan bueno como el "Mirubee Mirubox", que te dice con gráficas y todo tus hábitos de consumo, pero te ahorras 90€ y da la suficiente info... y siempre se puede mejorar el programa y placa Arduino hasta conseguir algo que se le acerque ;).
Cuando terminé el proyecto descubrí que no era el único con buenas ideas... como siempre. Hay muchos proyectos similares, la mayoría obtienen los pulsos del LED de su contador digital...

Materiales empleados:

Copia de Arduino UNO R3 con pantalla TFT de 2,4"


Circuito de reloj con el DS1307

Contador de KWh con salida de pulsos (un transistor optoacoplado cierra el circuito)

Instalando el contador de Kwh



Lo primero que he hecho es instalarlo para comprobar su funcionamiento; simplemente he intercalado el contador por pulsos entre el diferencial y la salida principal; en esta página de Nergiza viene explicado cómo instalar un ICP, no es lo mismo pero os dará la idea de dónde ponerlo, tiene que ir entre el ICP (Interruptor de Control de Potencia) y el diferencial. (Yo lo he puesto después, pero no afecta al funcionamiento del diferencial).
Hay un detalle; no es necesario colocar un cable gordo en el Neutro, ya que no hace falta que la carga pase por él (se utiliza sólo como referencia por lo que conectarlo con uno de 2 mm. al Neutro que sale del diferencial vale), lo importante es que los cables del tornillo 1 y 3 sean igual de gordos que el cable de Fase que sale del diferencial, ya que por ahí va a pasar toda la carga.
En el polo 1 la entrada desde el interruptor principal, y del 3 tiene que ir a todos los secundarios.
Importante no utilizar este medidor para cargas superiores a 30A, (6600W a 220V); se estropearía, cortando el paso de la corriente. Para eso hay otros más capaces y más caros.

Como no tenía cable de 4mm, he usado dos de 2,5mm en el conector 3 (abajo derecha del medidor)
Absteneos de andar en el cuadro si no tenéis experiencia con la electricidad. No se puede romper el precinto del Interruptor de Control de Potencia (corresponde a la potencia contratada). Este interruptor se aloja en una caja que se localiza "inmediatamente antes del resto de dispositivos, en un compartimento independiente y precintable". Se puede colocar en el mismo cuadro que los dispositivos generales de mando y protección, que deben estar siempre junto a la puerta de entrada. Nunca en dormitorios, baños u otras habitaciones. En nuestro caso se ve a la izquierda del cuadro general de interruptores:

Conectando el reloj externo al UNO

El reloj es imprescindible para control de horas, por lo que tenemos que conectarle uno con pila, para que mantenga la hora en caso de que el Arduino se desconecte.
En la siguiente imagen podéis ver la función de los pines más especiales; necesitamos el SCL y SDA para el circuito del reloj:
http://www.gammon.com.au/uno

Como la pantalla tapa todos los pines del UNO, he decidido soldar directamente en la parte trasera de la placa con cables finos al circuito del reloj conectando a los pines básicos; +5V, COM y SDA y SCL. 

Módulo RTC conectado a +5V, COM y SDA y SCL
Para actualizarle la hora he usado el siguiente programa (Nick Pyner del foro Arduino):

Code:
//Arduino 1.0+ Only
//Arduino 1.0+ Only
// pre-set the time in the void then use reset button to set it!


#include "Wire.h"
#define DS1307_ADDRESS 0x68
byte zero = 0x00; //workaround for issue #527

void setup(){
 Wire.begin();
 Serial.begin(9600);
     
 setDateTime(); //MUST CONFIGURE IN FUNCTION
printDate();
Serial.println("loopstart");
}

void loop(){

 printDate();
 
 delay(1000);
}

void setDateTime(){

 byte second =      30; //0-59
 byte minute =      24; //0-59
 byte hour =        0; //0-23
 byte weekDay =     5; //1-7
 byte monthDay =    19; //1-31
 byte month =       4; //1-12
 byte year  =       13; //0-99

 Wire.beginTransmission(DS1307_ADDRESS);
 Wire.write(zero); //stop Oscillator

 Wire.write(decToBcd(second));
 Wire.write(decToBcd(minute));
 Wire.write(decToBcd(hour));
 Wire.write(decToBcd(weekDay));
 Wire.write(decToBcd(monthDay));
 Wire.write(decToBcd(month));
 Wire.write(decToBcd(year));

 Wire.write(zero); //start 

 Wire.endTransmission();

}

byte decToBcd(byte val){
// Convert normal decimal numbers to binary coded decimal
 return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)  {
// Convert binary coded decimal to normal decimal numbers
 return ( (val/16*10) + (val%16) );
}

void printDate(){

 // Reset the register pointer
 Wire.beginTransmission(DS1307_ADDRESS);
 Wire.write(zero);
 Wire.endTransmission();

 Wire.requestFrom(DS1307_ADDRESS, 7);

 int second = bcdToDec(Wire.read());
 int minute = bcdToDec(Wire.read());
 int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
 int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
 int monthDay = bcdToDec(Wire.read());
 int month = bcdToDec(Wire.read());
 int year = bcdToDec(Wire.read());

 //print the date EG   3/1/11 23:59:59
 Serial.print(month);
 Serial.print("/");
 Serial.print(monthDay);
 Serial.print("/");
 Serial.print(year);
 Serial.print("     ");
 Serial.print(hour);
 Serial.print(":");
 Serial.print(minute);
 Serial.print(":");
 Serial.println(second);
}


Preparativos de la programación

Lamentablemente no puedo leer los pulsos mediante Irq's con esta pantalla colocada, ya que ésta utiliza los pines digitales 2 y 3 del UNO, que son los únicos que tiene para manejar interrupciones.
Por lo tanto la optimización del programa para hacerlo lo suficientemente rápido leyendo el sensor y evitar perder pulsos se ha llevado su buena parte de tiempo, ya que dibujar en la pantalla consume bastante tiempo (incluso un simple "borra pantalla"). Pero he logrado obtener algo funcional sin perder pulsos.
Pero bien podéis emplear otra combinación de hardware para la misma utilidad, o directamente mandar los datos por wifi a otra parte, etc, y en ese caso sí que se podría utilizar los pines de interrupciones del UNO o de la placa que use, para usar uno de ellos con los pulsos y así no perdernos ni uno, como se indica en el ejemplo de Arduino.

Por otro lado, el medidor 238-1 da 1000 pulsos/KWh, hay otros equipos que dan más pulsos y mayor exactitud de consumo instantáneo, pero no nos hace falta ni es recomendable a menos que utilicemos otro tipo de pantalla que no nos inutilice los pines de interrupciones (también podríamos emplear otra placa más completa como la Due o Mega).

Qué bien me lo paso programando :)

Otro tema era cómo obtener la hora; por suerte tenía un circuito RTC, y tras leer sobre el tema de coger la hora de un reloj externo y usar el chip RTC DS1307, que tiene la suficiente exactitud para lo que necesitamos (discriminar por horas), lo implementé en el programa, teniendo en cuenta el cambio horario invierno/verano.

Esta pantalla china es bastante mala, como comentan en este foro y en este otro (al menos mi versión, que aleatoriamente se queda en blanco tras un tiempo, aunque el Arduino sigue recogiendo los datos, y la van mejorando versión tras versión); además si alimentas el Arduino con fuente externa, es probable que frías la tarjeta microSD como uses una... por otro lado las librerías de Adafruit que he utilizado son bastante lentas haciendo según qué cosas (0,3 segundos para borrar la pantalla es una barbaridad), pero bueno, a caballo regalado... te apañas con lo que hay; la falsa economía de muchos productos chinos.
Así que me he divertido dándole vueltas para hacer un programa que funcione, no pierda pulsos, recoja los datos que necesito y también los muestre en pantalla; con librerías y todo he usado 23Ks de los 32Ks de memoria que tiene el UNO (con el IDE 1.0.6, con el 1.6.6 se reduce a 17Ks), y se puede reducir más capando librerías.
Otra cosa es encontrar la librería perfecta para tu pantalla; como atestiguan los muchos usuarios con problemas. Yo he usado las de Adafruit, con una línea cambiada para que saque los colores correctos (en las fotos se puede ver que al principio los colores no coincidían).
Actualización 26/07/2016: Intentando hacerla funcionar con un MEGA 2560, salía pantalla en blanco; gracias al cielo buhosoft ha corregido los errores de las librerías y las ha subido funcionando tanto en el UNO como en el MEGA.
Dado que la librería es muy lenta manejando texto, he recurrido a sobreescribir el dato de Vatios anterior en negro (colocándonos en la misma posición), y escribiendo sólo el valor numérico y "W".

Además la pantalla nos deja pocos PINES libres; pero sólo necesitaba un PIN digital para contar pulsos, y como están libres el 0 y 1, he usado el 0 (lo podéis cambiar al principio del código del programa).

El funcionamiento básico del programa es el siguiente:

  1. En la parte de setup sincronizamos el reloj interno con el RTC externo, inicializamos pantalla y leemos los datos guardados en la EEPROM de la sesión anterior (importante iniciar los datos descomentando las líneas que se indican en el código la primera vez, y luego volver a cargar el programa volviendo a comentarlas con //, ya que se guardan en formato long y sino obtendremos datos basura en pantalla).
  2. En la rutina principal comprobamos si hay pulso activado, si no lo hay pero lo hubo la vez anterior, entonces comparamos el tiempo transcurrido entre pulsos para averiguar los W de potencia instantánea, y actualizamos la pantalla con el valor, además de actualizar los valores de punta de Vatios y contar el tiempo si es mayor de la consigna de Vatios (con el objetivo de saber si nos conviene bajar la potencia contratada).
  3. Cada minuto, si tenemos tiempo para actualizar la pantalla con toda la información, la mostramos (comparando el tiempo que le llevó la última vez con el tiempo entre pulsos), si no da tiempo, la actualizamos sólo cada dos horas para perder los mínimos pulsos posibles.
  4. Cada media hora, si ha habido cambios, actualizamos la información de la EEPROM, indicándolo en pantalla. Según mis cálculos, se actualiza cada celda FLASH EEPROM 2 veces cada día, por lo que aunque lo hiciéramos cada 10 minutos, no se dañarían hasta dentro de 45 años (aguantan 100.000 escrituras).
Versión 1.0 del programa ya depurado

Por ahora el programa nos muestra en pantalla:
  1. Potencia instantánea en W.
  2. Potencia instantánea máxima registrada.
  3. Total consumo diario en Kwh.
  4. Total consumo nocturno en Kwh.
  5. Minutos durante los que se ha superado la consigna (Por si estamos consumiendo siempre menos de lo contratado, para bajar potencia).
  6. Total de Kwh por cada hora del día acumulados (se guardan en caso de irse la luz).
  7. Fecha y hora actuales.
  8. Fecha y hora del último salvado a la EEPROM de los datos (cuando se produzca).

Tenéis el proyecto en GitHub, y éste sería el programa v. 1.0 (compilado con Arduino 1.0.6; para versiones posteriores tendréis que hacer ajustes en las librerías y programa):

Programa Monitor de Energía con pantalla TFT con Arduino:

/*
 * ARCHIVO: Monitorización DDS238-1
 *   AUTOR: David Losada
 *   FECHA: 25/11/2015
 *        URL: http://miqueridopinwino.blogspot.com/monta-tu-propio-monitor-de-energia-por-30-eurillos.html
 *
 * OBJETIVO: Prototipo para comprobar consumos s/horarios
 * Cálculo TDH en invierno; en verano es 13 a 23h http://nergiza.com/tarifa-nocturna-sigue-existiendo-es-rentable/
 * TODO: Tener en cuenta si es verano o invierno (configurado para invierno)
 * Configurado para el medidor de KWh DDS238-1: Da un pulso cada Vatio/h (1000 pulsos/KWh)
 * (Cambiar las variables del inicio para que funcione con el que uses).
 * (COMPILA SÓLO EN 1.0.6)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 */

// IMPORTANT: Adafruit_TFTLCD LIBRARY MUST BE SPECIFICALLY
// CONFIGURED FOR EITHER THE TFT SHIELD OR THE BREAKOUT BOARD.
// SEE RELEVANT COMMENTS IN Adafruit_TFTLCD.h FOR SETUP.

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <EEPROM.h> //Para usar la memoria EEPROM
//Arduino con Tiny RTC I2C http://zygzax.com
#include <Wire.h>
#include <DS1307RTC.h>
#include <Time.h> //Manejo reloj interno Arduino

// The control pins for the LCD can be assigned to any digital or
// analog pins...but we'll use the analog pins as this allows us to
// double up the pins with the touch screen (see the TFT paint example).
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

// When using the BREAKOUT BOARD only, use these 8 data lines to the LCD:
// For the Arduino Uno, Duemilanove, Diecimila, etc.:
//   D0 connects to digital pin 8  (Notice these are
//   D1 connects to digital pin 9   NOT in order!)
//   D2 connects to digital pin 2
//   D3 connects to digital pin 3
//   D4 connects to digital pin 4
//   D5 connects to digital pin 5
//   D6 connects to digital pin 6
//   D7 connects to digital pin 7
// For the Arduino Mega, use digital pins 22 through 29
// (on the 2-row header at the end of the board).

// Assign human-readable names to some common 16-bit color values:
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
// If using the shield, all control and data lines are fixed, and
// a simpler declaration can optionally be used:
// Adafruit_TFTLCD tft;

int PowerSensor = 0; //pin de entrada de pulsos medidor eléctrico
unsigned int Consigna=2300; //Vatios sobre los que queremos controlar los minutos en los que lo superamos para saber si merece la pena la bajada de potencia
unsigned int PulsosKW = 1000; //IMPORTANTE DEFINIR AQUÍ los que correspondan a tu aparato; pulsos por KW
//(Con un sensor de 1000 máximo vamos a tener 1,3 pulsos/seg. para un consumo de 5 KW)
//Es decir, van a pasar máximo 1,33 seg. entre pulsos (1,3+0,03 que dura)
//Energía: 1000 imp = 1 KWh -> 1 imp = 1 Wh;
unsigned long Horas[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //Matriz para guardar los pulsos según la hora
unsigned long millisAnt = 0; //Milisegundos para comprobar tiempo pasado
unsigned long millisPrevious=0; //Para saber el tiempo transcurrido desde el último pulso
long Tiempo_Trans=0; //Guardamos los ms entre cada pulso detectado
unsigned long millisEEPROM=0; //Salvado de Kwh consumidos en EEPROM
unsigned long millisPant=0; //Guardamos tiempo de intervalo pantalla completa
unsigned long millisRTC=0; //Actualizar reloj
boolean Pulso = false; //Detectado pulso
boolean NoPulso = false; //Detectado que no hubo pulso después de un pulso
unsigned long PulsosDia =0; //Contador total KWh
unsigned long PulsosNoche =0;
long Vatios=0; //Cálculo vatios
long VatiosAnt=0;
long MaxVatios=0; //pico consumo vatios
unsigned long TimeMax=0; //Ms con vatios >2300
unsigned long TimePrevious=0; //Control de tiempo de dibujado de pantalla
unsigned long TimeFinal=0;

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

//Descomentar las líneas siguientes para sincronizar tiempo por el Serial
//  if(Serial.available()) 
//  {
//     time_t t = processSyncMessage();
//     if(t >0)
//     {
//        RTC.set(t);   // set the RTC and the system time to the received value
//        setTime(t);          
//     }
//  }
//   digitalClockDisplay();  
//   delay(1000);
//}

  //sync internal time to the RTC
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  Serial.println();
  if(timeStatus()!= timeSet) 
     Serial.println("Unable to sync with the RTC");
  else
     Serial.println("RTC has set the system time");
  Wire.begin();
  //RTC.begin();
  //Si quitamos el comentario de la linea siguiente, se ajusta la hora y la fecha con la del ordenador
  //RTC.adjust(DateTime(__DATE__, __TIME__));
  Serial.println(F("Arduino Working OK"));

#ifdef USE_ADAFRUIT_SHIELD_PINOUT
  Serial.println(F("Using Adafruit 2.8\" TFT Arduino Shield Pinout"));
#else
  Serial.println(F("Using Adafruit 2.8\" TFT Breakout Board Pinout"));
#endif

  Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height());
  tft.reset();

  uint16_t identifier = tft.readID();

  //Mandamos por el serial el ID por si hubiera algún problema con la pantalla
  if(identifier == 0x9325) { 
    Serial.println(F("Found ILI9325 LCD driver"));
  } else if(identifier == 0x9327) {
    Serial.println(F("Found ILI9327 LCD driver"));
  } else if(identifier == 0x9328) {
    Serial.println(F("Found ILI9328 LCD driver"));
  } else if(identifier == 0x7575) {
    Serial.println(F("Found HX8347G LCD driver"));
  } else if(identifier == 0x9341) {
    Serial.println(F("Found ILI9341 LCD driver"));
  } else if(identifier == 0x8357) {
    Serial.println(F("Found HX8357D LCD driver"));
  } else if(identifier == 0x0154) {
    Serial.println(F("Found S6D0154 LCD driver"));
  } else {
    Serial.print(F("Unknown LCD driver chip: "));
    Serial.println(identifier, HEX);
  }
  tft.begin(identifier);
  
  pinMode(PowerSensor, INPUT);
  
  //Cogemos los valores de pulsos salvados
   for(int i=0; i<24; i++) {               
     Horas[i]=EEPROMReadlong(i*4);
      }
   TimeMax=EEPROMReadlong(200);
   MaxVatios=EEPROMReadlong(204);
}

void loop() {
  //Descomentar las siguientes líneas para resetear la la eeprom que se usa en el programa
  //rellenamos de 0 los datos de EEPROM con los pulsos
  //for(int i=0; i<24; i++) {               
  //   EEPROMWritelong(i*4,0);
  //    }
  //EEPROMWritelong(200,0);
  //EEPROMWritelong(204,0);
  //Serial.println("Borrado de EEPROM terminado");
  //while(1) {delay(500);} //bucle infinito para borrarlo sólo una vez

  time_t t = now(); //Cogemos la hora
  int sensorState = digitalRead(PowerSensor);
  
  if (sensorState == 0) //Hay pocos pulsos por segundo, y cada uno dura >=30 mseg; lo importante es "capturarlos"
    {
      if (Pulso) { //Si se detectó pulso previamente, se contabiliza y se muestra WATTS consumo en pantalla
        //En esta operación tarda 29 ms en el Arduino UNO;
        Tiempo_Trans=millis()-millisPrevious;
        millisPrevious=millis();
        Pulso=false;
        //Guardamos el pulso en la hora correspondiente
        Horas[hour(t)]=Horas[hour(t)]+1;
        //3600 segundos=1 KWh = 1000 pulsos 
        //Sacamos los pulsos por segundo que hemos tenido y sacamos los W
        Vatios=((3600/PulsosKW)/(Tiempo_Trans/1000))*1000; //Todas las variables utilizadas en el cálculo deben ser float para exactitud
        if (MaxVatios<Vatios) {//Guardamos máximo
          MaxVatios=Vatios; 
        }
        if (Vatios>=Consigna){//Guardamos el tiempo durante el cual los Vatios superan los vatios que queremos controlar (bajada de potencia)
          TimeMax=TimeMax+(Tiempo_Trans);
        } 
        //Cada vez que hay un pulso se actualiza sólo los Vatios/hora para acelerar el dibujado; así sólo tarda 29 ms
        tft.setCursor(120, 0);
        tft.setTextColor(BLACK);  tft.setTextSize(3);
        tft.print(VatiosAnt);
        tft.print(" W");
        tft.setCursor(120,0);
        tft.setTextColor(YELLOW);
        tft.print(Vatios);
        tft.print(" W");
        VatiosAnt=Vatios;  
      }
    }
    else {
      Pulso=true; //Se marca detectado pulso
    }  
  //Hay que tener en cuenta que escribir todo esto tarda 1,7 segundos en el UNO; prefiero no refrescar la pantalla antes que perder pulsos
  //He añadido contadores de tiempo para sacar el valor de la vez anterior y si el tiempo entre pulsos>tiempo que tarda en refrescar pantalla->refresca
  //A mi se me queda la pantalla en blanco cada cierto tiempo ¿? ni idea xq pasa, pero se arregla al refrescarse
  //También escribimos datos si acaba de encenderse (millis se resetea cada 50 días)
  //Y si no se cumple nada de eso, actualizar al menos cada 2 horas aunque perdamos algún pulso
  if (millis()-millisPant >= 60000 && Pulso && (Tiempo_Trans>TimeFinal-TimePrevious) or millis()<2000 or (millis()-millisPant) >=7200000) 
  {
    TimePrevious=millis();//Comienzo contador tiempo  
    millisPant=millis();
    tft.reset(); //Reseteamos la pantalla; lo hago cada minuto porque a veces se me quedaba en blanco
    uint16_t identifier = tft.readID(); //Leemos el identificador de pantalla, en mi caso 9325
    Serial.print("Display ID: ");
    Serial.println(identifier);
    tft.begin(identifier); //Inicializamos la pantalla
    tft.setRotation(1); //Girada la pantalla 90º para ponerla horizontal   
    tft.fillScreen(BLACK); //Es muy lento
    tft.setCursor(0, 0);
    tft.setTextColor(YELLOW);  tft.setTextSize(3);
    tft.print("WATTS:");
    tft.setCursor(120, 0); //Establecemos posición escritura Watts
    tft.print(Vatios);
    VatiosAnt=Vatios;
    tft.println(" W");
    //Calculamos PulsosDia y Noche
    //POR HACER: Cambiar el programa para que calcule consumo según horario verano teniendo en cuenta las fechas del reloj
    //De momento sólo cuenta los pulsos según las horas y calcula KWh día y Noche
    PulsosNoche=0; //Iniciar variables
    PulsosDia=0;
    for(int i=0; i<24; i++) { 
      if (i>21 or i<12) { //A partir de las 22:00 hasta las 12:00 horario invierno
      PulsosNoche=PulsosNoche+Horas[i];
    }
      else {
      PulsosDia=PulsosDia+Horas[i];
      }
    }
    //Mostramos total dia y noche
    tft.setTextColor(MAGENTA);
    tft.print("WATTS MAX: ");
    tft.println(MaxVatios);    
    tft.print("TOTAL DIA: ");
    tft.print(PulsosDia/PulsosKW,1);//Indicamos Kw diarios con 1 decimal
    tft.println(" KWh");
    tft.print("TOT NOCHE: ");
    tft.print(PulsosNoche/PulsosKW,1);
    tft.println(" KWh");
    //Mostramos minutos durante los que se ha superado la consigna
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Minutos>2300Wh: ");
    tft.println(TimeMax/60000.1);//Con 1 decimal
    //Imprimimos en pantalla KW por cada hora para más info
    tft.setTextColor(CYAN);
    for(int i=0; i<24; i++) {               
     tft.print(i); 
     tft.print(":"); 
     tft.print(Horas[i]/PulsosKW,1); 
     tft.print(" ");
     if (i==3 or i==7 or i==11 or i==15 or i==19 or i==23){
      tft.println(); 
       }
     }
    //Mostramos fecha y hora
    tft.setTextColor(GREEN);
    tft.print(day(t));
    tft.print(+ "/") ;
    tft.print(month(t));
    tft.print(+ "/") ;
    tft.print(year(t)); 
    tft.print( " ") ;
    tft.print(hour(t));  
    tft.print(+ ":") ;
    tft.println(minute(t));
    //tft.print(":") ;
    //tft.println(second(t));
    TimeFinal=millis(); //Final contador tiempo
  }
  
  //Cada media hora salvamos a la EEPROM los datos que han cambiado (no hacerlo más frecuente para prevenir el envejecimiento prematuro de la FLASH)
  if (millis()-millisAnt>=1800000) { 
    for(int i=0; i<24; i++) {               
     if (EEPROMReadlong(i*4)!=Horas[i]) { //comprobamos si el valor ha cambiado; múltiplos de 4 para almacenar el valor
        EEPROMWritelong(i*4,Horas[i]); //guardamos el valor de consumo diario
       }
      }
     if (EEPROMReadlong(200)!=TimeMax) { //Guardamos valor de tiempo en ms superando 2300W
        EEPROMWritelong(200,TimeMax);
       }
     if (EEPROMReadlong(204)!=MaxVatios) { //Guardamos valor de vatios máximo
        EEPROMWritelong(204,MaxVatios);
       }
     tft.setTextColor(GREEN);
     tft.setTextSize(1);
     tft.setCursor(0,230);
     tft.print("Data saved ");
     tft.print(day(t));
     tft.print(+ "/") ;
     tft.print(month(t));
     tft.print(+ "/") ;
     tft.print(year(t)); 
     tft.print( " ") ;
     tft.print(hour(t));  
     tft.print(+ ":") ;
     tft.println(minute(t));

   }
  
  delay(20);
}
//RUTINAS
//This function will write a 4 byte (32bit) long to the eeprom at
//the specified address to address + 3.
void EEPROMWritelong(int address, long value)
      {
      //Decomposition from a long to 4 bytes by using bitshift.
      //One = Most significant -> Four = Least significant byte
      byte four = (value & 0xFF);
      byte three = ((value >> 8) & 0xFF);
      byte two = ((value >> 16) & 0xFF);
      byte one = ((value >> 24) & 0xFF);

      //Write the 4 bytes into the eeprom memory.
      EEPROM.write(address, four);
      EEPROM.write(address + 1, three);
      EEPROM.write(address + 2, two);
      EEPROM.write(address + 3, one);
      }
   
long EEPROMReadlong(long address)
      {
      //Read the 4 bytes from the eeprom memory.
      long four = EEPROM.read(address);
      long three = EEPROM.read(address + 1);
      long two = EEPROM.read(address + 2);
      long one = EEPROM.read(address + 3);

      //Return the recomposed long by using bitshift.
      return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
      }


Para testearlo basta con unir el PIN 0 del arduino a un PIN COM y soltar; cada vez que hagamos esta operación contará un pulso (o más dependiendo de la calidad de la conexión).

Finalmente colocamos la pantalla alimentada como queramos (yo he usado un transformador de 12VDC, pero si tenéis uno de menor voltaje, entre 6VDC y 9VDC, mejor (para alargar la vida del regulador que trae el Arduino), y en la caja que queramos, yo he usado un Tapper porque no va a estar ahí para siempre ;), hay que economizar.

He pegado la pantalla colocando un poco de silicona en los lados
La silicona no ha funcionado muy bien, pero con una esponja a presión al menos se ha quedado en su sitio.

He metido el enchufe en una regleta para evitarme andar soldando ;)

Finalmente quitamos la tapa de la caja de diferenciales principal, y he conectado la alimentación de la fuente del Arduino a un interruptor secundario, y el positivo (importante para que funcione), osea el PIN 0 al positivo del contador de KWh (SO+) y el negativo al otro (SO-).
He atornillado el tapper a la pared (el cable colgando es el USB para actualizar el Arduino:

En la pantalla una versión no acabada del programa, con un par de errores

Y voilà! otra chapucilla que funciona, tras 2 días ya vemos que nos conviene cambiar a la TDH 2.0, pero mejor esperar una semanita más ;) para solicitarla. La gran mayoría de hogares hacen más del 30% de gasto por la noche, así que preveo un cambio en masa a la TDH 2.0 ahorrando entre un 10 y un 40% en la factura de la luz...

A por el próximo proyecto!

Más info:
Modificaciones a aparatos con resistencias para bajar su potencia en Nergiza
Cómo conseguí bajar la factura de 25 viviendas: Nergiza.com
Arduino Data Logger
Uso del DDS238-1 con el Excontrol
Cambio horario
Descripción completa de los pines del Arduino UNO
Qué potencia mínima puedo contratar, de Nergiza.com
Cómo asegurarse qué potencia mínima contratar, de Nergiza

5 comentarios:

  1. Hola,

    Muy interesante el blog, y tus exposiciones (te leo hace tiempo), pero ahora quedo a cuadros con éste post, y te pregunto lo siguiente: ¿de verdad has consumido 22.2 Kw en sólo 24 Hs...? ...

    Un saludo desde Valencia.

    ResponderEliminar
    Respuestas
    1. No sé por que dices que en 24 hrs; más bien fueron 2 o tres días (lo comento al final), sí, es bastante; unos 200 kW mensuales, gran parte de ello se lo llevan 3 congeladores de arcón y 2 neveras (en algún sitio tenemos que guardar los cadáveres ;).
      Mi intención es aprovechar la DH y por ejemplo, dejar los congeladores desconectados "aguantando la caló un rato que no les afecte" antes de las 22:00 (23:00 en invierno), para aprovechar mejor la tarifa nocturna; tiene su miga ya que podemos echar a perder la comida, pero por ejemplo si están a -12ºC no creo que pase nada porque pasen una hora o dos a -8ºC... así a bote pronto, teniendo en cuenta la inercia térmica e historias... ya os comentaré. Igual con un arduino y sensores de temp. metidos en los conges... siempre que no se descongelen nada, no creo que les afecte una "súpercongelación" por la noche a -20 y luego por el día que suba a -10 durante esas 10 horas... no sé, tengo que investigarlo, pero esto sólo podría aplicarse a los congeladores de tapa horizontal, por sus bajas pérdidas. Menuda chapa que he soltao.
      Gracias y saludos desde Bilbao querido lector!

      Eliminar
    2. Al final descarté el tema de los congeladores; andar jugando con los grados de congelación no le viene bien a la comida; lo único que he hecho es poner un programador wifi Contros SP3 que los desconecta de 21 a 22 horas para aprovechar un poco más la tarifa nocturna sin peligro.

      Eliminar
  2. Muy interesante el proyecto con ARDUINO. Un saludo desde Medellín , Colombia

    ResponderEliminar

Puede dejar su comentario. Los comentarios descalificativos o sin relación ninguna con el tema tratado serán eliminados sin previo aviso. Antes de plantear una duda, asegúrate de que la respuesta no está en otra entrada del tema visitando la etiqueta que hay al final del artículo para verlos todos; muchas veces lo que planteas puede haber sido corregido o comentado en otra entrada posterior.