domingo, 28 de abril de 2013

Crear una estacion meteorologica con raspberry pi y un sensor dth11

Ya puestos a trastear con la raspberry y sus conexiones al GPIO, vamos a ver como podemos crear una pequeña estacion meteorologica con la raspberry pi, y un sensor dth11.





Que necesitamos??

Una raspberry pi
Un sensor dth11 (unos 2 € en dealextreme)
Cables para conectarlo

Notas sobre los materiales necesarios
El sensor que utilizo yo, y el que aparece en la imagen, viene con una resistencia de 10k(amarillo violeta rojo oro) incorparada entre el conector de señal y nuestra conexion al gpio. Si lo compramos suelto, habra que hacer un circuito en un protoboard tal como aparece en esta imagen:






Montaje de los componentes:


Conectamos el sensor dth11 al gpio de la raspberry asi:

RPi VCC (pin 1) -> DHT11 pin 1
RPi GPIO4 (pin 7) -> DHT11 pin 2
RPi GND (pin 6) -> DHT11 pin 4 (pin3 si solo tiene 3 pins)
 
Creando un programa para poder acceder al sensor y leer temperatura y humedad:
 
Una vez realizadas las conexiones, vamos a crearnos un programa que realice 
las lecturas, y que nos saque los resultados por la pantalla.
 
He elegido el lenguaje de programacion en C

Instalamos lo necesario:
nota: Todo lo voy a hacer desde el terminal. Esto me permitira hacerlo todo, 
con una conexion SSH hacia mi raspberry.


Me situo en mi directorio HOME
cd ~

Ejecuto estos comandos:
sudo apt-get install git-core build-essential
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build
 
Con esto ya tenemos lista la libreria wiringPi, para poder usar el puerto GPIO 
de la raspberry de una manera mas sencilla
 
Creamos el siguiente archivo con este comando:
nano dth11.c
 
Escribimos el siguiente codigo:
 
------------------------------------------ 
CODIGO DESDE AQUI - Archivo dth11.c
------------------------------------------
 
 //Incluimos librerias necesarias
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

//Definimos constantes
#define MAX_TIME 85
#define DHT11PIN 7
#define ATTEMPTS 5

//Definimos un vector global
int dht11_val[5]={0,0,0,0,0};

/////////////////////////////////////////////////////////////
//Funcion principal para leer los valores del sensor.
int dht11_read_val(){
 uint8_t lststate=HIGH;
 uint8_t counter=0;
 uint8_t j=0,i;
 for(i=0;i<5;i++){
  dht11_val[i]=0;
 }
 pinMode(DHT11PIN,OUTPUT);
 digitalWrite(DHT11PIN,LOW);
 delay(18);
 digitalWrite(DHT11PIN,HIGH);
 delayMicroseconds(40);
 pinMode(DHT11PIN,INPUT);
 for(i=0;i<MAX_TIME;i++){
  counter=0;
  while(digitalRead(DHT11PIN)==lststate){
   counter++;
   delayMicroseconds(1);
   if(counter==255){
    break;
   }
  }
  lststate=digitalRead(DHT11PIN);
  if(counter==255){
   break;
  }
  //Las 3 primeras transiciones son ignoradas
  if((i>=4)&&(i%2==0)){
   dht11_val[j/8]<<=1;
   if(counter>16){
    dht11_val[j/8]|=1;
   }
   j++;
  }
 }
  
 // Hacemos una suma de comprobacion para ver si el dato es correcto. Si es asi, lo mostramos
 if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF))){
  printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);
  return 1;
 }else{
  return 0;
 }
}

////////////////////////////////////////////////////////////////
//Empieza nuestro programa principal.
int main(void){
 //Establecemos el numero de intentos que vamos a realizar
 //la constante ATTEMPTS esta definida arriba
 int attempts=ATTEMPTS;
 
 //Si la libreria wiringPi, ve el GPIO no esta listo, salimos de la aplicacion
 if(wiringPiSetup()==-1){
  exit(1);
 }
 
 while(attempts){
  //Intentamos leer el valor del gpio, llamando a la funcion
  int success = dht11_read_val();
  
  //Si leemos con exito, salimos del while, y se acaba el programa
  if (success){
   break;
  }
  
  //Si no lee con exito, restamos 1, al numero de intentos
  attempts--;
  
  //Esperamos medio segundo antes del siguiente intento.
  delay(500);
 }
 return 0;
}
 
------------------------------------------ 
CODIGO HASTA AQUI-------- FIN DE ARCHIVO dth11.c
------------------------------------------ 
   
Se puede cambiar esto...:
printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]); 

por esto:
printf("%d,%d\n",dht11_val[0]*256+dht11_val[1],dht11_val[2]*256+dht11_val[3]); 
 
Y asi saldra correctamente los datos. Gracias a seta43 por el aviso! 
 
Una vez escrito, lo compilamos con el siguiente comando:
gcc -o dth11 dth11.c -L /usr/local/lib -l wiringPi

 
Si compila bien, ejecutamos con este comando:
sudo ./dth11
 
(GRACIAS A seta43 por la advertencia!!, habia confundido dth11 por dht11, asi que cuidar a ver!)
 
Deberia mostrarnos la temperatura, y la humedad separadas por comas.
 
 
Siguiente paso!!!
 
Como lo que nos interesa realmente, es tener unos datos actualizados cada minuto, 
y en forma de grafica, vamos a instalar un servidor web, y crearnos asi una
pagina web donde podamos consultar los resultados que nos dara nuestra medicio
de una forma grafica. Para eso, instalamos apache, y creamos la web necesaria.
 
Instalacion de apache:
 
Ejecutamos el siguiente comando:
apt-get install apache2
 
Arrancamos apache:
service apache2 start
 
Una vez instalado el 
 
 
Creacion automatica de la medicion de temperatura y humedad:

Vamos a crearnos un fichero, para que vaya guardando de forma automatica
los valores de temperatura y humedad, asi como la fecha en la que se ha 
tomado dicha medicion.

Nos situamos de nuevo en el directorio HOME:
cd ~

Creamos el siguiente fichero con el siguiente codigo:
nano dth11.sh

------------------------------------------ 
CODIGO DESDE AQUI - Archivo dth11.sh
------------------------------------------

#!/bin/bash
FECHA=$(date +\%Y\%m\%d\%H\%M\%S)
COMA=","
TEMP=$(/home/usuario/dth11)
echo "$FECHA$COMA$TEMP" >> /var/www/temp.log
 
------------------------------------------ 
CODIGO HASTA AQUI-------- FIN DE ARCHIVO dth11.sh
------------------------------------------ 
 
Con esto lo que hacemos, es un script que nos dara como resultado la fecha
en el formato adecuado, seguido de la temperatura y humedad, y lo almacenara 
en un archivo llamado temp.log, en el directorio raiz de APACHE por defecto, 
para su lectura posterior.
 
Tarea programada con cron
 
A continuacion, le vamos a decir a cron (nuestro programador de tareas en linux), 
que ejecute el script cada minuto. Para eso ejecutamos el siguiente comando:
 
sudo crontab -e
 
Y añadimos la siguiente linea al final:
 
* * * * * /home/usuario/dth11.sh
 
Creamos la pagina web para mostrarnos los resultados leidos:
 
Nos situamos en el directorio de nuestro servidor web con el siguiente comando:
cd /var/www 

Si existe un archivo index.html, lo borramos con el comando:
rm index.html

Descargamos la libreria dygraphs para poder generar la grafica:
wget -P /var/www http://dygraphs.com/dygraph-combined.js
 
Nos creamos el archivo index.html para almacenar la web:
nano index.html
 
------------------------------------------ 
CODIGO DESDE AQUI - Archivo index.html
------------------------------------------
 
 
<html>
<head>
<script type="text/javascript" src="dygraph-combined.js"></script>
</head>
<body>

<h1>Grafica con temperaturas y humedad</h1>

<p>Esta gr&aacute;fica, esta generada a partir de las mediciones tomadas por un sensor dth11, conectado a una raspberry pi. Se accede al sensor mediante lenguaje de programacion C, usando las librerias wiringPi, para poder leer y utilizar el conector GPIO de la raspberry Pi.</p>



<hr/>

<div id="graphdiv" style="width:750px; height:400px;"></div>
<script type="text/javascript">
 
 //Funcion para añadir ceros a los numeros de un solo digito
 function addZero(num){
  var s=num+"";
  if (s.length < 2){
   s="0"+s;
  }
  return s;
 }

 //Funcion para dar formato a la fecha, devolviendo la misma formateada
 function dateFormat(indate){
  var hh = addZero(indate.getHours());
  var MM = addZero(indate.getMinutes());
  //var ss = addZero(indate.getSeconds()); //No usamos los segundos
  var dd = addZero(indate.getDate());
  var mm = addZero(indate.getMonth()+1);
  var yyyy = addZero(indate.getFullYear());
  return dd+'/'+mm+' '+hh+':'+MM;
 }

 //Funcion principal para generar el grafico. Se genera en el id="graphdiv"
 g = new Dygraph(
 document.getElementById("graphdiv"),
 "temp.log",
 {
   xValueParser: function(x) {
  var date = new Date(x.replace(
    /^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/,
    '$4:$5:$6 $2/$3/$1'
  ));
  return date.getTime();
   },
   axes: {
  x: {
    ticker: Dygraph.dateTicker,
    axisLabelFormatter: function(x) {
   return dateFormat(new Date(x));
    },
    valueFormatter: function(x) {
   return dateFormat(new Date(x));
    }
  }
   },
   labelsDivWidth: 310,
   rollPeriod: 30,
   strokeWidth: 2.0,
   labels: ['Date','Humedad (%)','Temp (&deg;C)']
 }
 );
</script>

<hr/>

</body>
</html> 
 
------------------------------------------ 
CODIGO HASTA AQUI-------- FIN DE ARCHIVO index.html
------------------------------------------ 
 
Probandolo todo!!!

Si todo va bien, podemos probar que todo esto funciona, desde nuestro navegador
de paginas web, apuntando a la direccion ip de nuestra raspberry

http://ip.de.mi.pi

Deberia aparecer algo como esto:




Resumen:

Espero que os sirva de utilidad. A mi me ha funcionado todo a la perfeccion.

Un saludo a todos!!!


Fuente de informacion:

http://chrisbaume.wordpress.com/2013/02/10/beer-monitoring/

http://dygraphs.com/

http://rpidude.blogspot.com.es/2012/11/temp-sensor-and-wiringpi.html

http://dx.com/es/p/arduino-digital-temperature-humidity-sensor-module-121350

http://ubuntuone.com/6mT9cTREz90BUfvQD1AGNy


Agradecimientos:

Muchas gracias a Alberto, que me ha dejado todo el hardware necesario para poder
hacer mis pinitos con todo esto. Un abrazo!!!

 




16 comentarios:

  1. Buenas noches, te hago una consulta, estoy intentando compilar, pero me tira un error al tirar el gcc.

    root@xbian:/var/www# ls -l
    total 24
    -rw-r--r-- 1 root root 2086 May 6 22:05 dth11.c
    drwxr-xr-x 2 root root 4096 May 5 15:52 images
    -rw-r--r-- 1 root root 1465 May 5 18:42 index.html


    root@xbian:/var/www# gcc -o dhtll dhtll.c -L /usr/local/lib/ -l wiringPi

    gcc: error: dhtll.c: No such file or directory
    root@xbian:/var/www#


    tenes alguna idea, mientras tanto sigo buscando info.
    Gracias!!

    Jonathan

    ResponderEliminar
  2. Problema resuelto, error de tipeo en el archivo dht11.c

    Gracias!

    ResponderEliminar
  3. La Raspberry Pi la podéis encontrar también en http://www.tienda.meya.es
    Es la única tienda física que la tiene en Madrid. Hasta ahora casi no ha tenido problemas de stock y la sirven en 24 horas. Además tiene bastantes accesorios en la web para nuestra raspberry pi. También la web tiene foro propio. Un saludo a todos.

    ResponderEliminar
  4. Muy buena entrada!

    La he seguido y no consigo que funcione... llego a 'sudo ./dht11' pero no obtengo resultado ninguno, ni error ni medida.

    Alguna idea de lo que pueda fallar??

    Gracias por adelantado.

    ResponderEliminar
  5. Hola buenas tardes,
    Podría decirme dónde ha comprado ese sensor con la resistencia ya integrada?
    Por cierto, es ¡un buen proyecto!

    ResponderEliminar
    Respuestas
    1. Hola Paula,

      puedes encontrarlo aquí:

      http://www.adafruit.com/products/386

      Saludos!

      Aitor

      Eliminar
  6. Buenas,

    Una consulta, se podrían conectar dos sensores a la misma raspberry y así tener datos del interior e del exterior, por ejemplo??

    muchas gracias!

    ResponderEliminar
  7. Buen tuto. Lo acabo de probar y va bastante bien. Sin embargo con el código de jscript no acaba de aparecer la temperatura y sí la humedad en mi caso. En el log de temperaturas las lecturas las genera perfectas.

    ¿Habría forma de eliminar de la gráfica la humedad y dejar sólo la temp?. No acabo de ver dónde se generan las llamadas a los valores 'parseados' del temp.log en el propio jscript.

    ResponderEliminar
  8. Muy buenas,

    Me parece un tema muy interesante. Lo he seguido y salvo algunas complicaciones (debidas a mi falta de conocimientos ;)), no es difícil hacer que funcione.
    Ahora tengo un par de consultas:

    1. Se podría conectar más de un sensor y recibir los datos de los dos, por ejemplo para medir la temperatura interior y exterior??, y

    2. Cuando ejecuto el comando "sudo ./dht11" obtengo los datos de humedad y temperatura con precisión en unidades, los dos salen '.0', y lo mismo en el archivo log; pero en la gráfica los datos tienen dos decimales. Se podrían exportar los datos con decimales al archivo log???

    Lo dicho, gracias por la información!!!

    Un saludo!!

    ResponderEliminar
  9. Hola

    He estado viendo tu montaje y lo he visto muy interesante, por lo que lo he montado.
    Existen dos errores:
    El primero es que editas el fichero:
    nano dth11.c
    y luego lo intentas compilar con:
    gcc -o dht11 dht11.c -L /usr/local/lib -l wiringPi
    has cambiado las letras dth11 por dht11.

    El segundo es que la función que devuelve los grados y temperatura, no devuelve bien los datos. Deberias transformar el numero de 2 bytes a un numero decimal, no separarlos solo por comas.
    Creo que te has confundido al escribir el código en la página web, o lo has hecho a proposito para que la gente se lo curre.
    Yo lo he cambiado por:
    printf("%d,%d\n",dht11_val[0]*256+dht11_val[1],dht11_val[2]*256+dht11_val[3]);
    No he querido poner decimales, por lo que los grados y la humedad salen multiplicados por 10.

    Muy buen artículo, espero el siguiente.

    Saludos
    SETA43

    http://seta43.blogspot.com.es/



    ResponderEliminar
  10. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  11. Hola,
    He seguido el tuto con las correcciones de los comentarios y todo perfecto. Sin embargo, intento usar diferentes pines y no obtengo ningún resultado. En lugar de 1, 7, 6 uso 17, 23, 20. Cambio el código c asignando 23 a la constante DHT11PIN, compilo y ejecuto pero no me da las medidas.
    Alguna idea?
    Gracias

    ResponderEliminar
  12. Buenas tardes. La verdad que muy bueno el post, me funciona perfectamente, yo lo use con el dht22. Ahora bien, una consulta, a alguien le pasa que cuando colocan el script en el crontab en el log de fecha y datos le tira lo siguiente??? "Lock file is in use, exiting..."

    Manualmente si corro el script funciona perfecto, pero cuando lo agrego en el crontab no me genera la data de temperatura y humedad.
    Aclaro que lo estoy corriendo como root y el script tiene permisos totales (chmod 777)



    20140212132143,56.00,14.20
    20140212140607,63.80,15.40
    20140212142201,57.00,14.80
    20140212143640,57.50,14.50
    20140212143701,Lock file is in use, exiting...
    20140212143802,Lock file is in use, exiting...

    algo muy raro, todavía no lo puedo solucionar. de todas formas sigo investigando.
    Gracias!!

    ResponderEliminar
  13. Gracias por el tutorial me sirvio mucho, tuve problemas para hacer funcionar el script desde crontab, asi que directamente en crontab agregue la siguiente linea:

    * * * * * echo `date +\%Y\%m\%d\%H\%M\%S`,`/home/pi/dht11` >> /var/www/temp.log

    y en el archivo dht11.c Agregue la siguientes lineas para que a la hora de hacer la grafica me diera hasta dos decimales:

    // Hacemos una suma de comprobacion para ver si el dato es correcto. Si es asi, lo mostramos
    if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF))){
    float f, h;
    h = dht11_val[0]*256+dht11_val[1];
    h /= 10;

    f = dht11_val[2]*256+dht11_val[3];
    f /=10.0;
    printf("%f,%f\n",h , f);
    }else{
    return 0;
    }
    }

    Gracias de nuevo y espero y esto le sirva a alguien.

    Saludos.

    ResponderEliminar
  14. Te escribo para agradecerte la guía que has creado y que me ha resultado de mucha utilidad.
    Yo he hecho mis modificaciones para mejorar el funcionamiento y optimizar la vida de los discos y el consumo de energía.
    1º he creado un directorio en /var/web y una entrada en crontab del usuario root con el comando: */60 * * * * cp /var/www/* /var/web de manera que cada hora en punto se hace una copia de seguridad de la web.
    2º colocado esto en el archivo fstab: tmpfs /var/www tmpfs defaults,noatime,nosuid,mode=0755,size=50m 0 0
    De modo que el directorio donde está la web se crea como un disco virtual de RAM de 50Mb para que el disco duro en mi caso solo se mueva cada 60 minutos al hacer la copia de seguridad.
    3º colocado esto en el crontab de root: @reboot cp /var/web/* /var/www
    Para que nos restaure la copia de seguridad en caso de desconexión de la raspberry pi.
    La desventaja es que si se nos apaga perdemos las lecturas desde la última hora en punto pero ganamos en vida del disco duro que suele ser una SD o un disco usb y reducimos el consumo.
    Luego para evitar los errores de escritura del programa dht11 he colocado esto en el crontab de root: */5 * * * * dht11.sh de modo que la lectura se hace cada 5 minutos para reducir el trabajo del procesador. Los cambios en la temperatura del ambiente no tienen por qué ser tan inmediatos como para necesitar un registro tan instantáneo.

    ResponderEliminar
  15. no sale la grafica solo el texto no se que hacer

    ResponderEliminar