domingo, 10 de noviembre de 2013

Proyecto: Evitar inyección con Arduino

Hola a todos.

Llevo trabajando en mis poco tiempo libre…..un tiempo, en un proyecto que me permitirá controlar el excedente de la instalación para evitar la inyección a red.

Si recordáis, el immersun que tengo instalado tiene un margen de 120w que no es modificable, por lo que necesito algo más preciso.

Para desarrollarlo, me he basado en esta web http://openenergymonitor.org/emon/, que contiene todo el conocimiento necesario para entender como medir la energía, como controlarla, etc.

 

El concepto

El concepto es sencillo, solo queremos poder controlar la energía que consumimos, cuando, y hacia donde queremos enviarla. el sistema actual que tengo es el siguiente:

image

Es sencillo, la parte del cuadro azul es lo que se quiere alcanzar con este proyecto. Lo demás está ya instalado.

Por cierto, el medidor de voltaje y amperios casero, es semi-casero, lo hice con una carcasa de disco duro reciclado.

 

El Material

La premisa es sencilla, tiene que ser de bajo coste, por lo que reaprovecharemos todo lo que podamos y básicamente, consta de:

1.- Sensor de corriente (reciclado de un OWL que me sobraba)

 DSC05103

2.- Un arduino + pantalla comprado por internet

DSC05110

3.- Relays para poder encender o apagar cosas

DSC05111 

4.- Resistencias y componentes reciclados de TV o trastos varios

DSC05112

 

 

La estrategia

La estrategia a seguir es fácil:

1.- Medir la energía que pasa por el cable

2.- Medir si es excedentaria o no

3.- Si es excedentaria, aprovecharla para una estufa o frigorífico

En este primer tutorial, os contaré como se ha hecho el punto 1

 

Medir Watios de una instalación

Para medir la energía que pasa por un cable necesitamos un sensor CT, y algo que sea capaz de interpretar el resultado de la sonda.

La energía que pasa por el sensor, es muy pequeña, por lo que el arduino apenas es capaz de leerla, hay que amplificar el voltaje entre 0 y 5 voltios. Para ello se usan las resistencias. Yo he puesto 120 ohmios. Son la fila de resistencias grises

image

La onda en corriente alterna, es algo como esto: Medido con Arduino, ahora os cuento como:

image

Necesitamos que el centro del voltaje en las medidas sea 2,5 V, ya que así mediremos la onda completa. Si partiéramos de cero, solo mediríamos la mitad de la onda.

Eso quiere decir, que dividiremos los 5v que nos proporciona el arduino con dos resistencias:

image

Y con esto, ya conseguimos medir valores.

Arduino leerá voltajes de 0 a 1024, por lo que la media, es decir si no hay corriente, será de 512 aproximadamente. Esta será nuestra referencia, o punto de corte.

Este gráfico aclara un poco lo que hemos hecho: Podéis ver más información en http://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface

image

Y por último, el resultado. Es una prueba, como podéis ver por el lío de cables

image DSC05104 DSC05105

El programa que he hecho para el arduino es este:

 

#include <LiquidCrystal.h>
#include <LCDKeypad.h>
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
char msgs[5][16] = {"Right Key OK ",
                    "Up Key OK    ",              
                    "Down Key OK  ",
                    "Left Key OK  ",
                    "Select Key OK" };
int adc_key_val[5] ={50, 200, 400, 600, 800 };
int NUM_KEYS = 5;
int adc_key_in;
int key=-1;
int oldkey=-1;

const int currentPin = A1;
const unsigned long sampleTime = 100000UL;                           // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 2500UL;                               // choose the number of samples to divide sampleTime exactly, but low enough for the ADC to keep up
const unsigned long sampleInterval = sampleTime/numSamples;  // the sampling interval, must be longer than then ADC conversion time
const int adc_zero = 514;                                                     // relative digital zero of the arudino input from ACS712 (could make this a variable and auto-adjust it)

void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2);
  lcd.clear();
}

void loop()
{
  unsigned long currentAcc = 0;
  unsigned int count = 0;
  unsigned long prevMicros = micros() - sampleInterval ;
  while (count < numSamples)
  {
    if (micros() - prevMicros >= sampleInterval)
    {
      int adc_raw = analogRead(currentPin) - adc_zero;
      currentAcc += (unsigned long)(adc_raw * adc_raw);
      ++count;
      prevMicros += sampleInterval;
    }
  }
  //float rms = (sqrt((float)currentAcc/(float)numSamples) * (75.7576 / 1024.0));
  float rms = (sqrt((float)currentAcc/(float)numSamples) * (75.7576 / 1024.0));
  Serial.println(rms);
  lcd.clear();
  String watios="Wats: ";
  int apparent_power=rms*231;

  if(apparent_power<10)apparent_power=0;
  if(apparent_power>4000){
    watios= watios + " ERROR";
  } 
  else{
    watios= watios + apparent_power;
  }

  lcd.print(watios);
}

 

 

En fín, esto es todo por hoy, me queda bastante por trabajar, pero de momento, un pequeño avance motiva para seguir.

 

Saludos!!

6 comentarios:

Frank dijo...

Me encantan tus entradas. Ánimo a ver si consigues aprovechar el excedente para no dejar escapar nada de electricidad generada.
Hace 6 meses instalé un kit de autoconsumo de 250W y le he visto hasta los 1300W al día en verano y 400 en invierno.

Texter dijo...

Hola Jose.

Llevo tiempo siguiéndote y me parece muy ilustrativo todo lo que has publicado.

Tomate esto como una critica constructiva y no pienses que lo digo para fastidiar.

Se nota que no eres programador, ya que has cometido un par de errores, (has copiado el código, vamos).

1. Todas las constantes en mayúscula, ayuda a identificar los diferentes elementos.
2. Todas la variables globales juntas.
3. La creación de variables tiene que ir fuera del bucle infinito ya que crear una variables es mucho mas costoso que asignar un valor.
4. Tamaño de linea máximo de 80 caracteres, ayuda a leer.

Pues según lo que te he puesto el código queda:
NO ME HA DEJADO PUBLICARLO, LO PUBLICARE COMO RESPUESTA A ESTE U OTRA RESPUESTA.
Su HTML no es aceptable: Debe contener como máximo 4.096 caracteres


Si, yo mismo no he cumplido la linea 4, pero sino lo hago no hubiera quedado claro el código que he escrito.
He comentado todo lo que me ha parecido, ya que he deducido que es un código divulgativo.

Si hay algo que no se entienda, no dudes en preguntar.

Texter dijo...

/*Librerias que el compilador de C insertara*/
#include
#include
/*Fin de las librerias*/
/*Macros para el preprocesador de C*/
#define NUM_KEYS 5

/* Sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains*/
#define SAMPLE_TIME 100000UL //Puede fallar, no se que significa UL, unsigned long?

/* Choose the number of samples to divide sampleTime exactly, but low enough
* for the ADC to keep up*/
#define NUM_SAMPLES 2500UL //Lo mismo, unsigned long?

/* Relative digital zero of the arudino input from ACS712 (could make this a
* variable and auto-adjust it)*/
#define ADC_ZERO 514

#define COM 9600 //Velocidad de transmision
#define CAR 16 //Numero de caracteres del LCD
#define LINES 2 //Numero de lineas del LCD
#define VOLTIOS 231
// #define WATIOS "Wats: " //Tengo C oxidado, de esta linea no estoy seguro
/*Fin de las macros*/

/*Variables necesarias para el LCD*/
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
char msgs[5][16] = {"Right Key OK ",
"Up Key OK ",
"Down Key OK ",
"Left Key OK ",
"Select Key OK" };
int adc_key_val[5] ={50, 200, 400, 600, 800 };

// int NUM_KEYS = 5;//Esta linea es necesaria? Se utiliza en el LCD?
// int adc_key_in;//Esta linea es necesaria? Se utiliza en el LCD?
// int key=-1;//Esta linea es necesaria? Se utiliza en el LCD?
// int oldkey=-1;//Esta linea es necesaria? Se utiliza en el LCD?
/*Fin de las variables para el LCD*/

/*Varibles de nuestro programa*/
/*Constantes*/
const int CURRENT_PIN = A1;//Pin del Arduino donde se lee el valor
/* The sampling interval, must be longer than then ADC conversion time*/
const unsigned long SAMPLE_INTERVAL = SAMPLE_TIME/NUM_SAMPLES;
const String WATIOS="Wats: ";
/*Fin de las constantes*/

/*Variables Globales*/
int adc_raw;//Lectura actual

unsigned long currentAcc;//Current accumulator, acumulador de las lecturas tomadas
unsigned int count;//Contador
unsigned long prevMicros;//Variable donde se guarda el valor anterior del clock.

float rms;//Real Measure, lectura real.(¿Amperaje?)
int apparent_power;//Watios actuales
/*Fin de las variables globales*/

/*Variables para explicar el valor que toma rms*/
float media;//Media de las medidas tomadas.

//Para que es esta referencia?
const float REFERENCIA = 75.7576 / 1024.0;//Así se hace una sola vez la division y se ahorran ciclos de CPU.
/*Fin de las variables para rms*/

/*Fin de las variables del programa*/

/*Codigo que se ejecuta al iniciar el sistema*/
void setup(){
/*Inicializamos el sistema*/
Serial.begin(COM);
lcd.begin(CAR, LINES);
lcd.clear();
}

/*Este codigo se va a repetir de infinitamente*/
void loop(){
//Clock anterior
prevMicros= micros() - SAMPLE_INTERVAL ;
count=0;
currentAcc = 0;

while (count < NUM_SAMPLES){
//Mientras el numero de lecturas no sea el correcto.
if (micros() - prevMicros >= SAMPLE_INTERVAL){
//Si el clock es mayor el intervalo de lectura
//Leer
adc_raw = analogRead(CURRENT_PIN) - ADC_ZERO;
currentAcc += (unsigned long)(adc_raw * adc_raw);//Hace un cast a unsigned long para que no falle por desbordamiento
++count;//preincremento? bueno, hace la funcion que toca.
// count++;//Yo hubiera utilizado esta linea. Pero esto es mania personal
prevMicros += SAMPLE_INTERVAL;//Te situa en el siguiente intervalo de lectura.
}
}
/*Como este codigo es divulgativo la linea
* rms = (sqrt((float)currentAcc/(float)NUM_SAMPLES) * (75.7576 / 1024.0));//Porque haces la segunda division cada vez?
* la divido en varias lineas.
* El propio compilador de C se encargara de optimizar este codigo.*/
media=(float)currentAcc/(float)NUM_SAMPLES;//Valor total/numero de muestras
rms = (sqrt(media) * (REFERENCIA));//Sigo sin entender porque hace esto.

Serial.println(rms);
lcd.clear();

//Transforma a WATIOS
apparent_power=rms*VOLTIOS;//Deduzco que 230 Voltios

if(apparent_power<10)apparent_power=0;

if(apparent_power>4000){
lcd.print(WATIOS+" ERROR");
}else{
lcd.print(watios+apparent_power);
}
}

Jose Miguel Jiménez dijo...

Hola Text.

Tienes razón, para ser un codigo divulgativo necesitaría haberlo "limpiado" y aclararlo mejor. Por eso decía que tengo que trabajarlo un poco más.

Este es el primer "test" que funciona, tras cientos de pruebas..... y el uso del LCD incluye librerías con variables que requiere (y que yo no uso, como bien dices)

En la próxima entrada lo pondré mejor explicado.

Lo "malo" de todo es que sí soy programador, jajajaja En casa del herrero......

Gracias por tu aportación.

Jiro dijo...

Enhorabuena por el trabajo que estas haciendo.

Voy a seguir tu blog a ver si aprendo algo, ya que estoy aprendiendo algo de arduino y tengo ademas unas plaquillas con autoconsumo ( 4000W) y desperdicio bastante energía. Pronto me pondrán contador digital y aparecerá el problema grave

Jose Miguel Jiménez dijo...

Hola Jiro. Pues si te animas no dudes en contactar, te ayudo en lo que quieras. Cuanta más gente aporte más cosas podemos meterle al proyecto.

Saludos!!

Publicar un comentario