Leer y mostrar temperatura usando el sensor DS18B20 con CHIP

Leer y mostrar temperatura usando el sensor DS18B20 con CHIP Hace unos días compre unos CHIP y tenía pendiente usarlos. En la caja de la electrónica, había un sensor de temperatura DS18B20 y yo estoy con tiempo libre.

A este proyecto, le venía dando vueltas hace rato. Creo que es el "hola mundo!" de la electrónica (con Arduino y raspberry pi).

Vamos a dividir en 3 partes el proyecto, ya que hay que resolver algunos problemas antes:

  1. Como armar el circuito (o el como y donde conectar los cables)
  2. Como hacer que el sistema detecte el sensor
  3. Como registrar y mostrar los datos

Como armar el circuito (o el como y donde conectar los cables)

La forma de armar el circuito es "estándar". El sensor DS18B20, tiene tres pines. Uno va a tierra, otro a "la corriente" y el último que "trae" los datos. Este ultimo tiene que ir conectado a una resistencia de 4k7Ω.

Es decir algo como lo de la imagen:

diagrama-DS18B20_bb.png

A mi no me quedó tan bonito como el diagrama:

Como hacer que el sistema detecte el sensor

Después de leer dos hilos ([0] y [1]) en los , me puse a trastear con el sensor. Cuando miraba el sistema, efectivamente el dispositivo se creaba en /sys/bus/w1/devices/28-0000034f1e23 pero me faltaba el archivo w1_slave, que es el que facilitaba la lectura de los datos del sensor, tal como lo indicaba el primer link ([0]). ¿que me faltaba? Necesitaba cargar los módulos w1-therm y w1-gpio, pero estos no existían. Y tal como indicaban muchos usuarios, había que actualizar el kernel (en realidad era un downgrade, pasando de 4.4 a 4.3), lo que me hizo ariscar la nariz. Como con la actualización se corría el riesgo de "romper" el CHIP, preferí darle una vuelta y llegué al link [1] donde posteaban unos códigos en perl y python para leer los datos del sensor:

Perl

#!/usr/bin/perl
$file = "/sys/devices/w1_bus_master1/28-0000060f136f/rw";

sub send_command {
  my $cmd = shift;
  my $f;
  sleep(1);
  open($f, ">", $file) or die "can't open $file: $!";
  binmode($f) or die "can't binmode $file: $!";;
  syswrite($f, $cmd) or die "can't write $file: $!";
  close($f) or die "can't close $file: $!";
}

sub read_data {
  my $f;
  my $t;
  open($f, "<", $file) or die "can't open $file: $!";
  binmode($f) or die "can't binmode $file: $!";
  sysread($f, $t, 1000);
  close($f) or die "can't close $file: $!";
  return $t;
}

send_command(chr(0x44)); # Start temp conversion command
send_command(chr(0xbe)); # Read data command
my $ret = read_data();
print unpack("s", substr($ret,0,2))/16; # Grab the first 2 bytes, shift the decimal
print "\n";

Python

#!/usr/bin/python
import time

file = "/sys/devices/w1_bus_master1/28-000007011953/rw"

def send_command (cmd):
  time.sleep(0.75)
  with open(file, 'wb') as f:
     f.write(cmd) 

def read_data ():
  with open(file, 'rb') as f:
      t = f.read()
  return t

send_command(b'\x44')    # Start temp conversion command
send_command(b'\xbe')    # Read data command

ret = read_data()

buf0 = ord(ret[0])
buf1 = ord(ret[1])
t = buf1 << 8 | buf0
if t & 0x8000: # sign bit set
    t = -((t ^ 0xffff) + 1)
t = t / 16

print ('Temperature ', t)

Es decir, tal como estaba, con el kernel que viene de fábrica, ya estaba en condiciones de trabajar. Así que me puse manos a la obra:

  • Instalé apache
sudo apt-get install apache2
  • Actualicé el sistema
sudo apt-get update
sudo apt-get upgrade

Y como dentro de las actualizaciones, venia una actualización del kernel (en realidad era el mismo kernel, pero con fechas de compilación distinta) aproveché de reiniciar. Al volver, volví a ejecutar los scripts y ¡sorpresa! no funcionaban. Empecé a mirar que cosas habían cambiado y ¡paf! habian aparecido los módulos que me faltaban (el w1-therm y w1-gpio). El código python para probar de que funciona es este (gentileza de este blog):

#!/usr/bin/python

def read_temperature(file):
  tfile = open(file)
  text = tfile.read()
  tfile.close()
  lines = text.split("\n")
  if lines[0].find("YES") > 0:
    temp = float((lines[1].split(" ")[9])[2:])
    temp /= 1000
    return temp

file = "/sys/bus/w1/devices/28-0000034f1e23/w1_slave"
t = read_temperature(file)
print ('Temperature ', t)

Como registrar y mostrar los datos

Usaremos rrdtool, cron, python y javascriptRRD para mostrar los datos (porque soy perezoso).

  1. Instalar los paquetes de rrd
sudo apt-get install rrftool python-rrdtool 
  1. crear una base rrd para mantener los datos:
rrdtool create temperatura.rrd \
    --start now --step 60 \
    DS:a:GAUGE:120:-50:50 \
    RRA:AVERAGE:0.5:1:12 \
    RRA:AVERAGE:0.5:1:288 \
    RRA:AVERAGE:0.5:12:168 \
    RRA:AVERAGE:0.5:12:720 \
    RRA:AVERAGE:0.5:288:365

Armar el siguiente HTML:

<html>
  <head>
    <script type="text/javascript" src="lib/javascriptrrd.wlibs.js"></script>
    <title>Monitoreo de Temperatura con CHIP</title>
  </head>
  <body>
    <h1 id="title">Monitoreo de Temperatura con <a href="http://getchip.com/pages/chip">CHIP</a></h1>
    <div id="temperatura">
    </div>
    <p style="text-align:center"><img src="https://pbs.twimg.com/media/C2eStdXW8AAOUZz.jpg"/></p>
    <script type="text/javascript">
	    var graph_opts={legend: { noColumns:4}};
        var ds_graph_opts={
			'Oscilator':{ color: "#ff8000", 
			    lines: { show: true, fill: true, fillColor:"#ffff80"} },
            'Idle':{ label: 'IdleJobs', color: "#00c0c0", 
                lines: { show: true, fill: true} },
            'Running':{color: "#000000",yaxis:2}};
        var rrdflot_defaults ={graph_width:"700px"};
        flot_obj=new rrdFlotAsync("temperatura","temperatura.rrd",null,graph_opts,ds_graph_opts,rrdflot_defaults);
		</script>
	</body
</html>

En el mismo directorio donde dejamos el html anterior, debemos dejar los archivos de javascriptrrd y la base rrd. Algo así debiera ser la estructura:

├── chip.png
├── index.html
├── lib
│   ├── binaryXHR.js
│   ├── javascriptrrd.js
│   ├── javascriptrrd.wlibs.js
│   ├── rrdFile.js
│   ├── rrdFilter.js
│   ├── rrdFlotAsync.js
│   ├── rrdFlot.js
│   ├── rrdFlotMatrix.js
│   ├── rrdFlotSupport.js
│   └── rrdMultiFile.js
├── sensor.py
└── temperatura.rrd

Finalmente, necesitamos que cron esté leyendo el sensor y escriba sobre nuestra base de datos. Esto lo haremos con el script sensor.py, en el cual hay que configurar donde estará la BD (temperatura.rrd) y el nombre del sensor (en este caso el 28-0000033f1e11). El script es:

#!/usr/bin/python
import time
import rrdtool
 
databaseFile = "/home/chip/temperatura.rrd"
MIN_TEMP = -50
ERROR_TEMP = -999.99
rrds_to_filename = {
  "a" : "/sys/bus/w1/devices/28-0000033f1e11/w1_slave",
}

def read_temperature(file):
  tfile = open(file)
  text = tfile.read()
  tfile.close()
  lines = text.split("\n")
  if lines[0].find("YES") > 0:
    temp = float((lines[1].split(" ")[9])[2:])
    temp /= 1000
    return temp

def read_all():
  template = ""
  update = "N:"
  for rrd in rrds_to_filename:
    template += "%s:" % rrd
    temp = read_temperature(rrds_to_filename[rrd])
    update += "%f:" % temp
  update = update[:-1]
  template = template[:-1]
  rrdtool.update(databaseFile, "--template", template, update)


read_all()

Y finalmente, programamos en cron, para que esta tarea se ejecute cada 1 minuto

  • Ejecutamos:
crontab -e
  • Editamos con lo siguiente:
* * * * * /home/chip/sensor.py

Y basta disponibilizar la web (el html + scripts + temperatura.rrd) usando apache.

El resultado se puede visualizar en este enlace.

Añadir un comentario

El código HTML se muestra como texto y las direcciones web se transforman automáticamente.

Agregar un retroenlace

URL de retroenlace : http://trasto.net/blog/index.php?trackback/11

Arriba