jueves, 26 de febrero de 2015

Simple termómetro digital basado en Arduino.

Hoy venimos con nuestro primer "gran" proyecto de electrónica. En realidad, es un proyecto prácticamente académico, pero que debido al uso del blog y la intención del mismo, encaja totalmente con lo pretendido. 

El proyecto es muy sencillo, un termómetro digital para medir temperatura ambiente. Como todo termómetro, se basará en tres componentes: 

- Primero, el sensor de temperatura. 
- Segundo, el procesador que interprete la salida del sensor. 
- Tercero, el sistema de visualización. 

El sensor de temperatura. 


Como sensor de temperatura se escogió, erróneamente, el LM335. ¿Por qué erróneamente? Pues porque es un sensor de relativa baja precisión (¡hasta 5º C de error!) cuando en el mercado existen otros que permiten errores de menos de 1º C. No obstante, ya que se elaboró la primera versión de este termómetro con este sensor, se decidió dejar así, aunque no dudamos que nuestros lectores serán capaces de adaptar el circuito a otro tipo de sensor si quisieran. Si no, no os preocupéis; en los comentarios os puedo ayudar a modificarlo. 

Este sensor ofrece incrementos de 10 mV por cada grado Kelvin cuando funciona con una resistencia en serie tal y como se muestra en la siguiente figura, extraída de las hojas de características de Texas Instruments. 

El valor de la resistencia R1 que aparece en la imagen se debe escoger, según lo que dice el fabricante, de forma que 1mA sea la corriente que atraviesa el sensor, ya que se pueden inducir mayores errores si existen variaciones de la corriente o el voltaje de alimentación. En un sistema típico de 5V de alimentación, esta resistencia será de cerca de 5000 kΩ. Habría que entrar en consideraciones de la caída de tensión que posee el circuito, las posibles fluctuaciones de la fuente, etc; pero como sabemos, la propia resistencia tendrá un valor de tolerancia, por lo que no tiene sentido calcular cosas de este tipo en un proyecto casero. 

Sistema de visualización. 


Está basado en displays de 7 segmentos, cuya explicación podéis ver pinchando aquí.  Con tres de estos elementos se puede representar una temperatura con la precisión deseada, que sería de un decimal. Estamos hablando de restringir el rango de visualización desde 0º C hasta 50 º C, que es una amplia ventana para un uso de interior. Cierto es que si se añade la opción  de temperatura negativa habría que buscar un sistema para posibilitarlo, sobre todo al pasar de - 10 ºC, que requeriría 3 dígitos para las tres cifras (por ejemplo, -13,5 ºC) y uno adicional para simbolizar el negativo. 

No obstante, si nos ceñimos a una aplicación de interior, donde la temperatura puede oscilar entre 10 y 30 º C, no es necesario añadir ningún dígito o estrategia adicional. 

Desde el punto de vista del hardware, por lo visto en la entrada del blog dedicada a este elemento, se tiene un elemento que necesita de 8 salidas para controlarlo (9 si queremos controlar el punto decimal, aunque otra opción es dejarlo permanente). 8 x 3 salidas para controlar cada uno serían un total de 24 salidas que tendría que tener el procesador. Esto imposibilitaría usar arduino para controlarlo o requeriría de alguna shield de arduino para aumentar el número de salidas, que es de 13 en Arduino Uno (la versión quizá más extendida). 

Para hacer posible que puedan conectarse 3 displays se acude al proceso de la multiplexación, es un proceso interesante que probablemente en el futuro realice una entrada sobre él para explicarlo. Pero a grandes rasgos, se trata de coger y en lugar de que estén encendidos los 3 displays al tiempo, encender uno en cada instante; pero hacer que estos instantes se sucedan tan rápido que al ojo humano no le dé tiempo a detectar que está apagado y la sensación sea que los tres displays están mostrando la información.

El procesador. 


Todo sistema electrónico debe llevar un "cerebro", sin importar el tipo que sea (digital, analógico, lógica cableada,...) que interprete lecturas o que tome decisiones. En este caso, como se avanza en el título, se usará un sistema en base arduino; aunque podría ser perfectamente posible usar un PIC o un microprocesador de otra marca. También podría ser posible usar circuitos combinacionales, pero con la tecnología existente y los precios actuales, lo más sencillo es realizar un programa que controle el sistema. No pondré el software completo, más que nada  por la extensión del mismo; pero sí que pondré las partes clave para que cada uno pueda desarrollar su propio programa. 

El algoritmo seguido se puede ver en la siguiente imagen: 

imagenalgoritmo.jpg

Las funciones esenciales son: 

- El tratamiento de la lectura del sensor.

El sensor que usamos es un sensor con salida lineal entre 0 V para la temperatura mínima y la tensión de alimentación. Por tanto, en nuestro circuito arduino estaremos midiendo una tensión y tendremos que interpretarla. Así, nos llevará tres pasos:
          -  analogRead(sensorPin) // esto nos dará un valor entre 0 y 1024 correspondiente a la salida del           convertidor analógico digital de la placa arduino. 
          - volts = analogRead(sensorPin)*0.00488 // esto lo que hace es convertir el valor digital al 
          valor correspondiente analógico, es una simple regla de tres que podemos ver justificada en 
          la siguiente ecuación:
          - temp = volts/0.01 - 273 //da el valor de temperatura a partir del valor de tensión. Recordemos 
          que el sensor ofrecía 10 mV cada grado Kelvin, y que los grados Kelvin y Celsius están          
          espaciados por 273. 


De esta forma, tenemos un valor de temperatura en formato float. Ponemos un ejemplo a continuación de un valor: 
          

- La conversión del dato, recibido en formato flotante dígito a dígito.

Probablemente, haya librerías o alguna función "mágica" que haga esta operación; pero creo que de vez en cuando es conveniente estrujarse un poco el seso y no limitarse a buscar la solución por internet, sobre todo pensando en que un día puede que no encontremos una solución y tengamos que desarrollar nosotros una; por lo que esto se puede ver como un entrenamiento. 

En cualquier caso, tenemos un valor float y que necesitaríamos separar dígito a dígito para informar a cada uno de los displays de 7 segmentos de qué valor debe mostrar. De esta forma, creamos una rutina similar al funcionamiento de un convertidor analógico digital por aproximación. Lo que haremos, a groso modo, es multiplicar el valor por 10, para quedarnos sólo con los 3 dígitos más importantes (recordemos un termómetro con un decimal, para poder mostrar temperaturas desde 00,0 hasta 49,9). 

Entonces, comienza una rutina de comparación. Fundamentalmente, es comparar el valor con el límite, así por ejemplo, si tenemos 443 (siguiendo con el ejemplo anterior), se compararía y el programa devolvería que el número está entre el 400 y el 500; con lo que el primer dígito será un 4; después en la siguiente comparación se vería que está entre 40 y 50 y por lo tanto el segundo dígito será un 4; y en la última no se compara intervalo, si no que se iguala; y se pregunta si el dígito es igual al 1, al 2, al 3... y cuando llega al que iguala, coge ese dato; en el ejemplo el 3. El código se puede ver aquí:

/********************Aqui comienza la rutina para desglosar el dato float de temperatura en tres números por separado***************/  

  tdosdeci=int(t*10);  //tdosdeci hace referencia a ese valor multiplicado por 10. 

  if(tdosdeci<100) dig[0]=0;
  else if(100<tdosdeci&&tdosdeci<200) dig[0]=1; //dig[] es un vector de 3 valores que almacena los dígitos enteros. 
  else if(200<tdosdeci&&tdosdeci<300) dig[0]=2;
  else if(300<tdosdeci&&tdosdeci<400) dig[0]=3;
  else if(400<tdosdeci&&tdosdeci<500) dig[0]=4;
  
  u=tdosdeci-dig[0]*100; //se elimina el primer dígito restando lo obtenido en la primera  aproximación por 100. 
  if(u<10) dig[1]=0;
  else if(10<u&&u<20) dig[1]=1;
  else if(20<u&&u<30) dig[1]=2;
  else if(30<u&&u<40) dig[1]=3;
  else if(40<u&&u<50) dig[1]=4;
  else if(50<u&&u<60) dig[1]=5;
  else if(60<u&&u<70) dig[1]=6;
  else if(70<u&&u<80) dig[1]=7;
  else if(80<u&&u<90) dig[1]=8;
  else if(90<u&&u<100) dig[1]=9;
  
  w= u-10*dig[1];  //se elimina el segundo dígito restando lo obtenido en la primera aproximación por 10. 
    Serial.print(w);
  if(w==0) dig[2]=0;
  else if(w==1) dig[2]=1;
  else if(w==2) dig[2]=2;
  else if(w==3) dig[2]=3;
  else if(w==4) dig[2]=4;
  else if(w==5) dig[2]=5;
  else if(w==6) dig[2]=6;
  else if(w==7) dig[2]=7;
  else if(w==8) dig[2]=8;
  else if(w==9) dig[2]=9;

- La correspondencia entre las salidas activas y el número que se quiere visualizar.

Esta parte tiene a su vez dos partes; por un lado está en sincronizarse para que en cada instante las salidas de la placa sean las correspondientes a uno de los dígitos; y por otro lado, está en saber qué mostrar según el dígito. Lo mismo que antes, casi seguro habrá una librería o librerías destinadas a trabajar con displays de 7 segmentos, pero yo preferí hacer mi propia versión. 

Para la primera parte, se hace una rutina que se repita siempre que habilita en cada instante uno de los 3 displays. Esto se consigue tal y como vemos en el siguiente fragmento de código:

switch (D7ON)  
  {
    case 0:
    D7segmentos (dig[0]); //esta es la función creada para establecer las salidas en función del dígito. 
    digitalWrite(10, HIGH); // estas tres líneas de código establecen el nivel del ánodo común del display. 
    digitalWrite(9, LOW);
    digitalWrite(8, LOW);
    delay(5);
    D7ON++;
    break;
    
    case 1:
    D7segmentos (dig[1]);
    digitalWrite(10, LOW);
    digitalWrite(9, HIGH);
    digitalWrite(8, LOW);
    delay(5);
    D7ON++;
    break;
    
    case 2:
    D7segmentos (dig[2]);
    digitalWrite(10, LOW);
    digitalWrite(9, LOW);
    digitalWrite(8, HIGH);
    delay(5);
    D7ON=0;
    break;
       
  }

En cuanto a la segunda parte, se creó una función que simplemente establecía una relación entre las salidas a activar en función del dígito de entrada. A modo de ejemplo, pongo la declaración de la función y alguno de los dígitos:

NOTA: es un display de ánodo común, por lo que las salidas activas lo son a nivel bajo.

int D7segmentos (int dig)
{
   if(dig==1)
{
  digitalWrite(11,HIGH); //a
  digitalWrite(2,LOW);//b
  digitalWrite(3,LOW);//c
  digitalWrite(4,HIGH);//d
  digitalWrite(5,HIGH);//e
  digitalWrite(6,HIGH);//f
  digitalWrite(7,HIGH);//g
}

...

  if(dig==4)
{
  digitalWrite(11,HIGH);
  digitalWrite(2, LOW);
  digitalWrite(3,LOW);
  digitalWrite(4,HIGH);
  digitalWrite(5,HIGH);
  digitalWrite(6,LOW);
  digitalWrite(7,LOW);
}

...

  if(dig==7)
{
  digitalWrite(11,LOW);
  digitalWrite(2, LOW);
  digitalWrite(3,LOW);
  digitalWrite(4,HIGH);
  digitalWrite(5,HIGH);
  digitalWrite(6,HIGH);
  digitalWrite(7,HIGH);
}

  if(dig==8)
{
  digitalWrite(11,LOW);
  digitalWrite(2, LOW);
  digitalWrite(3,LOW);
  digitalWrite(4,LOW);
  digitalWrite(5,LOW);
  digitalWrite(6,LOW);
  digitalWrite(7,LOW);
}

...

}

Está creado como función puesto que es perfectamente reutilizable en cuantos proyectos queramos que usen un display de 7 segmentos, sólo se tiene que llamar a la función indicando el número que queremos que muestre.

Sistema final.  


Pronto publicaré fotos del sistema ;-)

Materiales empleados. 

Hardware.

- Protoboard (placa para prototipos).
- Cables macho-macho.
- Sensor LM335.
- Chip ATMEGA328 (mismo que Arduino UNO).
- Display 7 segmentos.
- Placa Arduino Duemilanove/UNO
- Cable USB estándar tipo B (para conectar Arduino a PC). 

Software.

- Software Arduino (http://arduino.cc/en/Main/Software). 


No hay comentarios:

Publicar un comentario