Temperaturaufzeichnung mit dem Raspberry Pi

25. Mai 2020

Ziel war es die Temperatur in verschiedenen Räumen aufzuzeichnen. “Das Netz” drängt einem recht schnell den Temperatursensor DS18B20 auf, vorallem wegens seines Preises. Den Sensor gibt es schön präpariert mit 1 m Kabel, d.h. ich muss anstückeln, was mit einer Schlauchleitung machbar ist. Deren Querschnitt ist deutlich größer als der des Sensorkabels, aber da auch die Schlauchleitung “nichts” kostet…

Zutaten

Aufbau

Der prinzipielle Aufbau ist in nachfolgender Zeichnung gezeigt.

Der Raspberry Pi verträgt an seinen Pins nur 3,3V. Folglich darf der Sensor auf dem 1-Wire-Bus keinen höheren Pegel als 3,3V liefern, was gewährleistet ist, wenn der Sensor selbst mit nur 3,3V betrieben wird. Soll der Sensor an 5V betrieben werden, die er verträgt, braucht es für die Bus-Leitung einen Pegelwandler (bzw. einen “Nachbau"). Damit die Bus-Leitung ein definiertes Potential hat, wird es mittels Pull-Up-Widerstand auf 1 gezogen.

Die drei Leitungen müssen wie folgt verbunden werden.

Die gemeinsame Masse wird durch Verbinden des schwarzen Sensordrahts mit Pin 9 erreicht, die gelbe Bus-Leitung wird auf Pin 7 geführt und die Versorgungsspannung von 3,3V wird mit dem roten Sensordraht verbunden. In mancher Anleitung ist die Rede von einem 4,7k Widerstand, der die gelbe mit der roten Leitung verbinden soll, anderswo werden 10k vorgeschlagen. Da ich Verbindungsprobleme mit 5k hatte, stieß ich im Netz auf diesen Hinweis.

If you use the GPIO Onewire, your 1.5kΩ pullup (not 4.7kΩ as often advised!) has to go to +3.3V.

Mit den vorgeschlagenen 1,5k gab es keine Verbindungsprobleme mehr. Die äußern sich übrigens folgendermaßen.

$ ls /sys/bus/w1/devices/
00-800000000000  w1_bus_master1

$ ls /sys/bus/w1/devices/
00-400000000000  00-c00000000000  w1_bus_master1

$ ls /sys/bus/w1/devices/
00-200000000000  00-600000000000  00-a00000000000  w1_bus_master1

$ ls /sys/bus/w1/devices/
00-600000000000  00-a00000000000  00-e00000000000  w1_bus_master1

Neben dem Master tauchen immer wieder neue Geräte auf. Das Verhalten zeigt sich auch, wenn keinerlei Hardware (Sensoren, Pull-Up-Widerstand) angesteckt ist.

Inbetriebnahme

Das Linuxbetriebssystem bringt fast alles mit. Um über ssh arbeiten zu können, muss es aktiviert werden. Dazu erstellt man auf der boot-Partition der SD-Karte eine Datei mit dem Namen ssh. Beim nächsten Booten kann man sich dann über das Netzwerk mit dem Benutzernamen pi und dem Standardpasswort raspberry einloggen.

Dort aktiviert man als erstes den 1-Wire-Bus. Das geht mittels rasp-config recht einfach.

$ sudo raspi-config

Dann steigt man in der Menüstruktur über 5 Interfacing Options zu P7 1-Wire ab und bestätigt die Aktivierung mit Yes. Damit ist die Schnittstelle aktiviert und es Bedarf nur noch eines Neustarts. Händisch müsste man dafür an die Datei /boot/config.txt die Zeile dtoverlay=w1-gpio,gpiopin=4 anfügen.

Der Bus ist zwar verfügbar, aber der Sensor wird noch nicht erkannt, da dessen Treiber (Kernelmodul) noch nicht geladen wurde. Das geht händisch mit

$ sudo modprobe w1-gpio
$ sudo modprobe w1-therm

und automatisch durch einen Eintrag in /etc/modules.

$ sudo vi /etc/modules

Dabei gibt man dem gpio-Modul noch einen Parameter mit, über den der Treiber dann weiß, dass ein Pull-Up-Widerstand vorhanden ist.

$ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.

w1-gpio pullup=1
w1-therm

Unter /sys/bus/1w/devices finden sich nun die angeschlossenen Sensoren und der Bus-Master.

$ ls /sys/bus/1w/devices
28-030497940742  w1_bus_master1

Mit dem falschen Pull-Up-Widerstand wurden mehr Sensoren gefunden als vorhanden. Zusätzlich hatten sie eine Busadresse, die mit 00- begann und generell viele Nullen enthielt.

Die Temperatur kann nun einfach mit

$ cat /sys/bus/1w/devices/28-030497940742/w1_slave
46 01 55 05 7f a5 a5 66 a8 : crc=a8 YES
46 01 55 05 7f a5 a5 66 a8 t=20375

abgefragt werden. Dabei entspricht die Zahl 20375 einer Temperatur von 20,375°C. Wobei die Genauigkeit des Sensors im Datenblatt mit 0,5°C angegeben ist.

Messreihen aufnehmen

Das Abfragen des Sensors funktioniert, fehlt noch das automatische Aufzeichnen von Messreihen. In den anderen Anleitungen werden Pythonskripte bemüht. Ich nehme stattdessen die bash und wenn vorhanden rrdtool. Falls nicht werden die Sensorwerte einfach in eine csv-Datei geschrieben.

$ cat /home/pi/gettemp.sh
#!/bin/bash

#seconds
STEP=900
TIMEOUT=1200

#celsius
MINTEMP=-40
MAXTEMP=80

if ! [ -x "$(command -v rrdtool)" ]; then
	echo 'Error: rrdtool is not installed, using csv instead.' >&2
	USECSV=1
	LOGFILE="/home/pi/temperature.csv"
else
	USECSV=0
	LOGFILE="/home/pi/temperature.rrd"
fi

#
# Using a CSV file for storage
#
if [[ $USECSV -eq 1 ]]; then
	if [[ !	-f "$LOGFILE" ]]; then
		echo "creating logfile (${LOGFILE})"
		LOGFILEHEADER=""
		while read SENSOR; do LOGFILEHEADER="${LOGFILEHEADER}${SENSOR};"; done < /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves
		echo "${LOGFILEHEADER}" > ${LOGFILE}
	else
		echo "using logfile (${LOGFILE})"
	fi

	CSVLINE=""
	for SENSOR in `cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves`
	do
		string=`cat /sys/bus/w1/devices/${SENSOR}/w1_slave`
		if [[ $string =~ "YES" ]]
		then
			b=${string:69:2}
			c=${string:71:3}
			CSVLINE="${CSVLINE}${b}.${c};"
		else
			CSVLINE="${CSVLINE};"
		fi
	done
	echo ${CSVLINE} >> ${LOGFILE}
else
#
# Using a round robin database for storage
#
	if [[ !	-f "$LOGFILE" ]]; then
		echo "creating logfile (${LOGFILE})"
		TOTALSENSORS=" create ${LOGFILE} --step ${STEP} --start `date +%s` "
		COUNTER=0
		while read SENSOR; do
			TOTALSENSORS="${TOTALSENSORS} DS:temp${COUNTER}:GAUGE:${TIMEOUT}:${MINTEMP}:${MAXTEMP} "
			COUNTER=$((COUNTER+1))
		done < /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves

		TOTALSENSORS="${TOTALSENSORS} RRA:AVERAGE:0.5:1:960  RRA:MIN:0.5:96:3600  RRA:MAX:0.5:96:3600  RRA:AVERAGE:0.5:96:3600"
		echo "${TOTALSENSORS}"
		rrdtool ${TOTALSENSORS}
	else
		echo "using logfile (${LOGFILE})"
	fi

	DATA="`date +%s`"
	for SENSOR in `cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves`
	do
		string=`cat /sys/bus/w1/devices/${SENSOR}/w1_slave`
		if [[ $string =~ "YES" ]]
		then
			b=${string:69:2}
			c=${string:71:3}
			DATA="${DATA}:${b}.${c}"
		else
			CSVLINE="${DATA}:"
		fi
	done
	rrdtool update ${LOGFILE} ${DATA}
fi

Damit das Skript automatisch gestartet wird, registriert man es beim Cron-Daemon.

$ crontab -e

Anschließend folgende Zeile eingeben.

*/15 * * * * /home/pi/gettemp.sh > /dev/null 2>&1

Und das Skript wird alle 15 Minuten ausgeführt. Das schöne am rrdtool ist, dass es die Datenbank in der maximal notwendigen Größe anlegt. Damit weiß man bereits zu Beginn wie viel Platz die Messreihen maximal einnehmen. Die csv-Datei hingegen wird so lange wachsen, bis die Speicherkarte voll ist. Für einen Sensor wurden bei den im Skript stehenden Einstellungen 96 kB reserviert.

Die grafische Aufbereitung der csv-Datei bekommt man z.B. mit gnuplot hin, das rrdtool kann selbst Graphen generieren. Dazu muss ich jedoch erst ein paar Daten sammeln.