domingo, 6 de marzo de 2011

Termómetro graficador con LM35 y display HD44780 compatible

El circuito integrado LM35 es muy interesante. Se comporta como un regulador de tensión controlado por temperatura. El resultado es que por su salida entrega 10 milivolts por grado celsius.

En la documentación figuran varios circuitos de aplicación pero para este experimento vamos a hacerlo lo más básico posible. Lo conectaremos así:



Para la medición, lo único que tenemos que hacer es leer la entrada analógica y calcular cuantos milivolts son. Lo dividimos por 10 y listo.

Un detalle con respecto al montaje físico del LM35: no conviene conectarlo directamente sobre el Arduino. Aunque no lo parezca la placa genera bastante calor como para aumentar la temperatura un par de grados. Es mejor ponerle un calbe y que esté lejos.

Con temperaturas normales, rara vez mediremos más de 100 grados, o sea que rara vez tendremos más de 1 volt en la salida. Así que para obtener mejor precisión en la medida podríamos usar como referencia del conversor analógico digital, la tensión interna de 1,1 Volt.

  analogReference(INTERNAL); //referencia de 1.1V 

¿Por qué usar una referencia de 1,1V en lugar de dejarlo en la normal que son 5V?  Para tener mejor precisión en la medida. Aunque tampoco sería tan importante ya que el LM35 tampoco es super exacto. Puede tener un error de medio grado, lo cual parece mucho pero en comparación con otras alternativas está muy bien. Otros tienen tolerancias de un grado o más.

El conversor analógico digital mide con una precisión de 10 bits, o sea que la función analogRead nos devuelve un valor entre 0 y 1023. Dado que 1023 corresponde a 1.1 volt, lo que son 110 grados, la fórmula para convertir la información en grados es sencilla. Si a es el valor medido, la temperatura es:

t=a*110/1023.0;

Dado que la entrada está sujeta a múltiples interferencias externas, el valor de entrada no es totalmente estable. Entonces, para conseguir una medición más exacta lo que vamos a hacer es promediar todos los valores medidos durante una fracción de segundo. Los vamos sumando en la variable s y contando con c. Al final dividimos s por c y tenemos la temperatura (variable a):

m=millis();
s=0;
c=0;
while(millis()-m <300) {
     s+=analogRead(A1);
     c++;
     } 
a=s/c;


Ahora bien. Usando la rutina del post anterior, tenemos 24 barritas para mostrar datos. Así que podríamos mostrar una barrita por hora y así graficar las últimas 24 horas. Crearemos una variable llamada m10m donde guardaremos los milisegundos de la última barra mostrada, y lo compararemos con los milisegundos actuales. Eso nos dará los milisegundos que han transcurrido. Si lo dividimos por 1000 lo tendremos en segundos, y cuando esto de 3600 será que ha pasado una hora. Algo así:


dif=(millis()-m10m)/1000;
if (dif>3600){....

Para ver que tal funcionan las cosas en la pantalla mostraremos el valor devuelto por el conversor analógico-digital, la gráfica, la medición de temperatura, y el tiempo transcurrido.

Una vez conformes con el resultado los datos se pueden reubicar. Recordemos que la gráfica sale donde pongamos los caracteres 0 a 7.

Al final a mi me ha quedado así:



Y el programa que lo hace es este:

#include <LiquidCrystal.h>

LiquidCrystal lcd(8,9,4,5,6,7);



byte grafica[24]; //valores entre 0 y 8

void setup(){
  lcd.begin(16, 2);

  Serial.begin(9600);

  analogReference(INTERNAL); //referencia de 1.1V
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("OK");

  lcd.setCursor(8,0);
  lcd.print('\0');
  lcd.print('\1');
  lcd.print('\2');
  lcd.print('\3');
  lcd.print('\4');
  lcd.print('\5');
  lcd.print('\6');
  lcd.print('\7');

  memset(grafica,(byte)9,sizeof(grafica));  

  mostrar_grafica();  
  }

/////////////////////////////////////////////////////////////


long a;
float t;
long m,s,c;

long s10m=0;
long c10m=0;
long m10m=millis();
int i,indice_registro=0;
long dif;
int k;
/////////////////////////////////////////////////////////////

void loop(){

m=millis();
s=0;
c=0;
while(millis()-m <300) {
     s+=analogRead(A1);
     c++;
     }

a=s/c;

lcd.setCursor(0,0);
lcd.print(a);
lcd.print("  ");

t=a*110/1023.0;
lcd.setCursor(0,1);
lcd.print(t);

//cada 10 minutos guardar el promedio

s10m+=a;
c10m++;;

dif=(millis()-m10m)/1000;
lcd.setCursor(8,1);
lcd.print(dif);
lcd.print("       ");
if (dif>3600){ 
   //guradar valor
   
   a=s10m/c10m;
   memcpy(grafica,grafica+1,23); //desplazar hacia la izquierda   
   grafica[23]=8 * (1.0*t-10.0)/12;

   s10m=0;
   c10m=0;
   m10m=millis();
   mostrar_grafica();
   }

}


//////////////////////////////////////////////////////

void mostrar_grafica(){
int d,dp;
byte bm[8];
int c,columna,fila,posicion,x;
int i;
for ( c=0;c<8;c++){ 
    memset(bm,0,8);//llenamos el caracter con ceros
    
    // cada caracter representa 3 datos, que comienzan por el...
    
    //poner puntitos en la primera columna (entre 0 y 8)
    x=0;
    dp=c * 3;

    for (columna=0;columna<5;columna+=2){
        i=dp + 2-( columna/2 );
        d=grafica[i];
                   
        for (fila=0;fila<8;fila++){
            
            if (d>8) // dato invalido
              bm[fila] |= (fila & 1) <<columna;
            else 
              if (fila>=9-d) bm[fila] |= 1<<columna;
                 
            
            }//for fila
        x++;
        }//for columna
    lcd.createChar(c,bm);  
    }

}