martes, 29 de julio de 2014

Censado de datos. Monitorea un ambiente.

Censado de datos. Monitorea un ambiente.

Categoría: 1. Programación y electrónica.

Este ejercicio es de utilidad para realizar un registro puntual o permanente de las condiciones de temperatura y luminosidad de un ambiente que se maneja automáticamente. De esta manera verificar que las condiciones que se programaron o se ajustaron en los equipos acondicionadores funcionan correctamente. Los datos se registran en un archivo de texto cada 10 minutos. También se despliegan en una pagina web muy simple que se refresca cada cinco segundos.

Una de las ventajas de este programa es que mientras estás visualizando la página web, los datos se refrescan cada 5 segundos (esto puede modificarse en el código) pero si cierras la página, el registro de datos continúa, refrescándose cada 60 segundos por default (también lo puedes programar con otra frecuencia).

El hardware para este ejercicio consiste en lo siguiente:
- Placa de desarrollo Arduino UNO R3.
- Shield Ethernet Arduino.
- Placa para prototipos.
- Tres termorresistencias (termistor) conectadas a GND con una resistencia de 10k_Ohm y calibradas.
- Una fotorresistencia de 2M_Ohm, conectada a GND con una resistencia de 10k_Ohm.
- Un led con resistencia de 1k_Ohm conectada a GND.
- Un buzzer.
- Fuente de alimentación 9Vcc.

En esta aplicación se combina el uso de las funciones de Ethernet y de la micro SD de Arduino Ethernet Shield. Se utilizaron como base los ejemplos DataLogger y WebServer que acompañan al IDE de Arduino. Además se trata de poner este equipo a trabajar de manera indepenciente. Es decir, no necesita estar conectado al puerto USB de la computadora, pero se pueden visualizar los datos en tiempo real en una computadora conectada a la misma red donde se conecta el servidor (Ethernet Shield).

De esta manera, no se depende del monitor serial para el despliegue de los datos censados ni se dedica una conexión USB para alimentar el circuito. 

Como aplicaciones para este sistema se sugiere el monitoreo de temperatura e iluminación durante un periodo de tiempo determinado, 24 a 48 h por ejemplo, y así detectar si la temperatura se mantiene dentro del rango deseado, o si se está fuera de las condiciones durante ciertas horas del día o la noche. También podemos calcular con cierta precisión la hora en que se encienden y apagan las luces. Esto puede ser útil si tenemos un sistema controlado automáticamente, pero queremos tener un registro de que esto está ocurriendo realmente. Podemos poner por ejemplo el caso en que una luminaria quede fuera de servicio. El equipo funciona correctamente, pero la iluminación no se encendió a la hora requerida. 

Esta aplicación se puede llevar a un invernadero donde las plantas necesitan un fotoperiodo mínimo para crecer o para no florear. Un laboratorio de cultivo de tejidos vegetales in vitro, donde se necesita exponer los cultivos a un fotoperiodo mínimo, normalmente 16 horas de iluminación por 8 horas de oscuridad. Una instalación donde se crían peces o aves y que requieren que la temperatura ambiente (aire o agua) estén siempre dentro de rango óptimo. 

En ambos casos, iluminación y temperatura, podemos agregar una alerta sonora cuando una de las condiciones salga de rango.

El sistema se puede manejar de manera permanente, teniendo cuidado de vaciar el archivo de destino cada semana o cada mes. Para no saturar la capacidad de la tarjeta de la memoria micro SD del Shield Arduino.

Este ejemplo se puede ampliar a otro tipo de mediciones, como pH, potencial redox, humedad relativa, presión atmosférica, concentración de CO (monóxido de carbono), entre otros. Contando con los sensores necesarios.

Así mismo es posible aumentar el número de sensores hasta 6 en el caso de ArduinoUNO. Si se trata de censar mayor número e variables se puede usar el Arduino Mega (16 entradas analógicas), que también es compatible con Ethernet Shield.


Gráfico 1. 

Los datos almacenados durante las 24h del día en un archivo de texto pueden procesarse con una hoja de cálculo (Microsoft Excel en este caso) para hacer un análisis visual de un gráfico. En él se puede distinguir claramente las horas de insolación y las horas de oscuridad. Para eso es necesario colocar la hora de inicio del censado, esto debe hacerse manualmente abriendo el archivo de texto y agregando la hora y fecha en el encabezado. En el editor de textos, menú [Editar] [Hora y fecha]. Es recomendable colocar en el encabezado del archivo de texto las ecuaciones de los sensores y la entrada analógica a la que se conectaron A0-A5 (para Arduino UNO).

Las temperaturas interior y en el sistema monitoreado (agua) se mantienen dentro de un rango estrecho, mientras que la temperatura exterior muestra una amplia variación, que durante el día se comporta de manera semejante a la intensidad luminosa (Ampliar el gráfico para ver detalles).

Los sensores de temperatura se encapsularon en un tubo de ensayo (vidrio) con bolitas de plomo unidas con silicón en el fondo (para evitar flotación). El tapón del tubo se selló con silicón para mantener siempre seco el termistor (Imagen 1).

Imagen 1.

La calibración de los termistores se hizo contra un termómetro de mercurio (Ver: calibración de un termistor). Colocando agua con hielo y tomando lecturas analógicas (0 - 1023) y de temperatura simultáneamente, hasta que el agua alcanza la temperatura ambiente. Otra serie de datos se toma en un vaso con agua caliente, hasta que se temperatura es la misma que el ambiente.

A continuación se muestra el código del programa (IDE Arduino).

/**********************************************
DataLogger_&_WebServer

Basado en:
DataLogger
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe

WebServer
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe

**********************************************/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

int buzzer = 6;
int redLed = 9;
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,177);  // 192.168.1.177
EthernetServer server(80);
const int chipSelect = 4;
float sensor[5];
long double tiempoActual,tiempoPrevio,tiempoPrevio2;
long double intervalo = 600000;  // 10 minutos
long double intervalo2 = 60000;  // 1 minuto

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

  Serial.print("Inicializando SD card ...");
  pinMode(10,OUTPUT);
  if (!SD.begin(chipSelect))
  {
    Serial.println("Falla en tarjeta. Inserte tarjeta ...");
    return;
  }
  Serial.println("Se inicalizo tarjeta SD ...");

  pinMode(buzzer,OUTPUT);
  pinMode(redLed,OUTPUT);
}


void loop() {
  tiempoActual=millis();
  EthernetClient client = server.available();

  if (client)
  {
    digitalWrite(redLed,HIGH);
    refreshWebServer();
    digitalWrite(redLed,LOW);
  }

  else
  {
    if (tiempoActual - tiempoPrevio2 > intervalo2)
    {
      digitalWrite(redLed,HIGH);
      refreshOutWeb();
      tiempoPrevio2 = millis();
      digitalWrite(redLed,LOW);
    }
  }

  if (tiempoActual - tiempoPrevio > intervalo)
  {
    digitalWrite(buzzer,HIGH);
    digitalWrite(redLed,HIGH);
    escribeArchivo();
    tiempoPrevio=millis();
    digitalWrite(buzzer,LOW);
    digitalWrite(redLed,LOW);
  }
}

/*********************        *********************/

void escribeArchivo()
{
  String dataString = "";
  int dato;  // Solo acepta int en la funcion String(dato);
  int tiempo = tiempoActual/60000;

  dataString += "\n";
  dataString += String(tiempo);
  dataString += "\t";

  for (int analogPin = 0; analogPin < 4; analogPin++)
  {
    dato = sensor[analogPin];
    dataString += String(dato);
    dataString += "\t";
  }

  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile)
  {
    dataFile.println(dataString);
    dataFile.close();
    Serial.println(dataString);
  }
  else {
    Serial.println("error opening datalog.txt");
  }
}


/*******************         **********************/

void refreshWebServer()
{
  EthernetClient client = server.available();

    digitalWrite(redLed,HIGH);
 
    Serial.println("new client");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
 client.println("Refresh: 5");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          for (int analogChannel = 0; analogChannel < 4; analogChannel++) {
            float sensorReading = analogRead(analogChannel);
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is \t");
            if (analogChannel == 0)
            {
              client.print("Interior ");
              sensorReading = 0.1591*sensorReading - 56.113;
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Celsius degree.");
            }
            if (analogChannel == 1)
            {
              client.print("Exterior ");
              sensorReading = 0.1116*sensorReading - 35.358;
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Celsius degree.");
            }
            if (analogChannel == 2)
            {
              client.print("Sistema ");
              sensorReading = 0.13318*sensorReading - 43.9853;
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Celsius degree.");
            }
            if (analogChannel == 3)
            {
              client.print("Iluminacion ");
              sensor[analogChannel]=sensorReading;
              client.print(sensorReading);
              client.print(" Digital value (0-1023).");
            }
            else
            {
              client.println(" ");
            }
            client.println("<br />");    
          }
          client.println("</html>");
          break;
        }
        if (c == '\n') {
         currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
    Serial.println("client disonnected");
 
    digitalWrite(redLed,LOW);
}


/*******************        ***********************/

void refreshOutWeb()
{
          for (int analogChannel = 0; analogChannel < 4; analogChannel++)
          {
            float sensorReading = analogRead(analogChannel);
            if (analogChannel == 0)
            {
              sensorReading = 0.1591*sensorReading - 56.113;
              sensor[analogChannel]=sensorReading;
            }
            if (analogChannel == 1)
            {
              sensorReading = 0.1116*sensorReading - 35.358;
              sensor[analogChannel]=sensorReading;
            }
            if (analogChannel == 2)
            {
              sensorReading = 0.13318*sensorReading - 43.9853;
              sensor[analogChannel]=sensorReading;
            }
            if (analogChannel == 3)
            {
              sensor[analogChannel]=sensorReading;
            }
            else
            {
            }
          }
}

Los datos de calibración de los termómetros se graficaron en Excel y se ajustaron a una recta por el método de regresión por mínimos cuadrados. Las ecuaciones obtenidas son las siguientes:

Termometro1. Temperatura interior:              y=0.1591*x-56.113               Pin: A0
Termometro2. Temperatura exterior:             y=0.1116*x-35.358               Pin: A1
Termometro3. Temperatura del sistema:        y=0.13318*x-43.9853           Pin: A2

La fotorresistencia no se calibró. Para este dispositivo se registran los valores digitales leídos por el convertidor analógico digital (valores de 0 a 1023).

Figura 2.

Para poder visualizar los datos censados, se abre una ventana en el explorador y en la barra de direcciones se escribe la dirección IP que se utiliizó en el código del programa. Los valores se refrescan cada 5 segundos, lo cual es adecuado, ya que por ser una página web tan sencilla casi no consume recursos de la computadora en que se despliegan.

El valor de tiempo registrado en el archivo de texto está en minutos. Para poderlo manejar en Excel se recomienda convertir los datos de esa columna a valores de hora (00:00 a 23:59). Esto se hace fácilmente con la función TIEMPO(hora,minuto,segundo). Como el dato está en minutos, se colocan ceros en hora y segundo, en el atributo minuto se escribe la dirección de la celda que se va a convertir). En la primer celda de la siguiente columna se coloca la hora de inicio y a partir de la segunda se coloca una fórmula que suma esa primera celda (fija, como en $C$7+B8).

En el siguiente enlace puedes encontrar un proyecto para la construcción de un medidor de pulso utilizando optoelectrónica.

Dispositivos electrónicos programables como el que se utilizó en este ejemplo, pueden servir para la automatización de otras aplicaciones. En este enlace puedes ver unas notas sobre automatización de un invernadero. O en forma de presentación (automatización de un invernadero).






No hay comentarios:

Publicar un comentario