Python exemplarisch - RPi Tutorial
deutsch     english

TEMPERATURSENSOR, 1-WIRE

 

Alle Programme können von hier heruntergeladen werden.


 

Die Temperatur ist eine der wichtigsten physikalischen Grössen, die in technischen Systemen gemessen werden. Es gibt viel verschiedene Arten von Temperatursensoren: Einige liefern direkt eine Spannung, andere führen zur Änderung einer anderen Grösse (beispielsweise zu einer Widerstandsänderung), die dann in eine Spannungsänderung umgewandelt wird. Bei den modernen Temperatursensoren ist oft auch eine elektronische Schaltung integriert, und die Temperaturausgabe erfolgt digital mit einem gut etablierten Protokoll (in der Regel I2C, SPI oder 1-Wire).

 

 

Thermistoren

 

Die Thermistoren sind temperaturempfindliche Widerstände. Die Beziehung zwischen der Temperatur T und dem Widerstand R ist nichtlinear. Die meisten Thermistoren haben einen negativen Temperaturkoeffizienten (NTC) und zeigen eine Abnahme des Widerstandes mit zunehmender Temperatur. Die Kennlinie kann durch eine abnehmende Exponentialfunktion approximiert werden. Es gibt auch Thermistoren mit einem positiven Temperaturkoeffizienten (PTC).

Die Widerstandsänderungen werden mit einem Seriewiderstand in eine Spannungsänderung umgewandelt, mit einem ADC digitalisiert und die digitalen Daten an einen Mikroprozessor zur Verarbeitung übertragenen.

 
Typische Charakteristik des NTCs und PTCs
 
Typische Anwendung

(R und RS können vertauscht sein)

Ziel:
Die Raumtemperatur mit einem einfachen NTC messen und diese alle Sekunden auf der Konsole (oder einem angeschlossenen Display) ausschreiben.

Schaltschema:


Verwenden Sie eine Steckplatte und realisieren Sie eine Verkabelung mit Ihrem bevorzugten ADC. Hier wird der PCF8591 verwendet.

Den Seriewiderstand müssen Sie dem verwendeten NTC anpassen. Er sollte etwa so gross wie der Widerstand des NTC bei Zimmertemperatur sein.

temp10

Als Programm nehmen Sie eines aus dem Kapitel ADC, das dem von Ihnen verwendeten ADC angepasst ist.

Bemerkungen:
Das Ergebnis ist aus folgenden Gründen ungenau:

  • Die Spannungsänderung ist klein. Dies führt bei der Verwendung eines 8-Bit ADC im Bereich 0..3.3V zu einer schlechten Temperaturauflösung
  • Die Beziehung zwischen der Spannung und der Temperatur ist nichtlinear. Die Umwandlung ist kompliziert
  • Der Thermistor erfährt durch den Stromfluss eine selbstheizende Wirkung, was zu einer Verfälschung des Resultats führt

Thermistoren werden vor allem verwendet, um ein bestimmtes Temperaturniveau zu erfassen, z.B. zur Stabilisierung der Temperatur in einer Heizung, einem Kühlschrank, usw. Bei solchen Anwendungen spielt der genaue, absolute Wert der Temperatur keine grosse Rolle.

 

 

IC-basierte Sensoren mit Analogausgang

 

Die LM35 und TMP35/36/37 Familien sind Temperatursensoren mit einem eingebautem Chip. Gegenüber einem NTC besteht ihr grosser Vorteil besteht darin, dass sie eine Ausgansspannung abgeben, die linear zur gemessenen Temperatur ist. Der Proportionalitätsfaktor ist 10-20 mV/Grad.

In diesem Beispiel verwenden wir den LM35. Es handelt sich um ein 3-pin Bauelement mit einer Ausgangsspannung, die proportional zur Temperatur in Grad Celsius ist und das keine Kalibrierung benötigt. Weitere Informationen finden Sie im Datenblatt.

Sie benötigen keine zusätzlichen Komponenten. Schliessen Sie einfach den mittleren Pin an einen Ihrer sonst verwendeten ADC an.

 

Das Programm zeigt die aktuelle Temperatur in Grad Celsius an. Betrachten Sie die folgende Umwandlung: Der Sensor mit 0.01 V pro Grad Celsius gibt bei der Temperatur T die Spannung u = 0.01 * T ab. Wenn Sie die Spannung 3.3V an einen 10-Bit ADC anlegen, der mit 3.3V versorgt ist, so gibt dieser den digitalen Wert 1023 ab. Legen Sie u an, so ist der digitale Wert also v = 1023 / 3.3 * u, also v = 1023 / 3.3 * 0.01 * T = 3.1 * T.

Programm: [►]

# RaspEasy7c.py
# LM35 temperature sensor

import smbus
import time
#from OLED1306 import OLED1306

def readData(port = 0):
    if port == 0:
        adc_address = 0x48
    elif port == 1:    
        adc_address = 0x4D
    rd = bus.read_word_data(adc_address, 0)
    data = ((rd & 0xFF) << 8) | ((rd & 0xFF00) >> 8)
    data = data >> 2
    return data

print "starting..."
#oled = OLED1306()
#oled.setFontSize(50)

bus = smbus.SMBus(1) 
while True:
    v = readData()
    T = v / 3.1
    print "T = %4.1f centigrades" %T
#    oled.setText(str(int(T + 0.5))) # rounded to int
    time.sleep(1)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

 

IC-basierte Sensoren mit digitalem Ausgang

 


Sensoren mit I2C-Bus

Der MCP9808 ist ein billiger und vielseitig einsetzbarer digitaler Temperatursensor hoher Präzision mit einem I2C Interface. Seine Genauigkeit beträgt typisch +-0.25 °C (maximal +-0.5 °C) im Bereich -20 to 100 °C. Es handelt sich um eine der besten Möglichkeiten, Umgebungstemperaturen mit dem Raspberry Pi zu messen.


Da der Chip nur in im SMT-Gehäuse hergestellt wird, muss man ihn entweder auf einen SMT-Adapter löten oder man verwendet ein MCP9808 Breakout-Board, wie es beispielsweise von Adafruit hergestellt wird. Es genügt die Pins VCC, GND, SDA und SCL anzuschliessen und die Adressen-Pins auf Ground zu setzen, wodurch die I2C-Adresse auf 0x18 gesetzt wird.

temp9

hand1

Falls Sie eine Breakout-Board verwenden, sollten Sie es mit 3.3V speisen. Bei 5V-Speisung müssen Sie die Pullup-Widerstände auf den SDA und SCL-Leitungen entfernen.

Wie Sie aus dem Datenblatt entnehmen, genügt ein einziger I2C-Lesebefehl, um den Temperaturwert zu holen. Er ist 16 bit-kodiert, aber es braucht etwas Bit-Jonglieren, um die Temperatur als Float zu extrahieren. Das Datenblatt enthält ein Beispielprogramm, aus dem die nötigen Operationen zu entnehmen sind. Das Beispielprogramm ist hier auf Python portiert und enthält zum besseren Verständnis einige Kommentarzeilen.

Programm: [►]

# Temp3.py
# Temperature sensor MCP9808

import smbus
import time

i2c_address        = 0x18
temp_register      = 0x05

def readTemp():
    v = bus.read_word_data(i2c_address, temp_register)
    hiByte = v & 0x00FF  # SMBus with reversed byte order
    loByte = (v >> 8) & 0x00FF
    hiByte = hiByte  & 0x1F # clear flag bit
    if hiByte & 0x10 == 0x10:  # temp < 0
        hiByte = hiByte & 0x0F  # clear sign
        temp = 256 - hiByte * 16 + loByte / 16.0 # scale
    else:
        temp = hiByte * 16 + loByte / 16.0 # scale
    return round(temp, 1)

print "starting..."
bus = smbus.SMBus(1) 
while True:
    t = readTemp()
    print "T =", t, "centigrades"  
    time.sleep(1)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

Temperatursensor mit 1-Wire-Bus (Eindraht-Bus)
Der DS18B20-Chip kann als ein digitales Thermometer im Bereich -55 bis 125 oC mit einer Genauigkeit von +- 0.5 oC eingesetzt werden. Da es direkt an den GPIO des Raspberry Pi angeschlossen werden kann, ist es eine ideale Lösung, um Innen- und Aussentemperaturen zu messen. Der Sensor kommuniziert mit dem Raspberry Pi mit dem 1-Wire-Kommunikationsprotokoll, das von Dallas Semiconductor entwickelt wurde.

Das Protokoll verwendet eine einzige Datenleitung (Bus genannt), die sowohl als Stromversorgung als auch als Sende- und Empfangsleitung genutzt wird. Zusätzlich ist eine GND-Verbindung erforderlich. Der Raspberry Pi dient hier als Bus-Master und es können beliebig viele Bus-Slaves angeschlossen werden. Jedes Gerät kann entweder Empfänger oder Sender sein, jedoch nicht beides zugleich.

Der Master ruft einen Slave mit seiner eindeutigen Adresse auf. Diese besteht aus 8 Bytes und setzt sich aus einem 1-Byte-Family-Code, einer 6-Byte-Serienummer (Unique-Device-ID) und einem 1-Byte-CRC (Prüfsumme) zusammen.

Im Ruhezustand (Idle) wird der 1-Wire-Bus über einen Pullup-Widerstand auf HIGH-Pegel gezogen, damit sich die Speicherkondensatoren der Slaves, die sich im Listening-Zustand befinden, laden können. Diese Kondensatoren dienen als Spannungsversorgung der Slaves (falls keine andere vorhanden ist). Während der Kommunikation wird der Bus durch die Geräte auf LOW gezogen.

Um eine logische "1" zu schreiben, wird der Bus vom Master während 1-15 us auf LOW gezogen, bei einer logischen "0" während 60-120 us. Um eine Antwort zu erhalten, geht der Master in den Listening-Zustand und der Slave sendet Daten im gleichen Format. So können Master und Slave Sender oder Empfänger sein, aber in einem bestimmten Zeitpunkt nur eines von beidem. Im folgenden Beispiel wird das Byte b0011'0011 = 0x33 gesendet (zuerst LSB):

 

Das Protokoll kann man mit Morsecode vergleichen, das aus Punkten und Strichen besteht. Man kann es auch als eine Art von Pulse Width Modulation (PWM) aufgefassen.

Mit der aktuellen Version der NOOBS-Distribution ist es sehr einfach, das 1-Wire-Protocol für den DS18B20 Sensor zu verwenden, da der Treiber ein Bestandteil des Linux-Kernels ist. (In unserer aktuellen SD-Karten-Distribution, ist das 1-Wire Protocol bereits für GPIO 4 (Pin #7) aktiviert.) Falls Sie den Treiber selbst aktivieren müssen, so editieren Sie die Datei /boot/config.txt wie folgt:

sudo nano /boot/config.txt

Fügen Sie die folgende Zeile ein:

dtoverlay=w1-gpio,gpiopin=4

Setzen Sie den gpiopin-Parameter für den GPIO-Pin, den Sie reservieren möchten. Default wird GPIO 4 (Pin #7) verwendet. Schliessen Sie den 1-Wire-Device an und booten Sie neu. Beachten Sie, dass Sie den 1-wire GPIO-Pin nicht als gewöhnlichen I/O-Port verwenden sollten.

Ziel:
Verwenden Sie den DS18B20 Digital-Thermometer-Chip (Datenblatt), um alle Sekunden einen Temperaturwert zu messen und auf der Konsole und (falls vorhanden) am angeschlossenen Display anzuzeigen.

Schaltschema:

Der DS18B20 IC wird in einem TO-92 oder SMT-Gehäuse fabriziert. Als SMD verwenden Sie ein Modul (von Adafruit und anderen) oder löten den Chip auf einen SMT-Adapter. In den meisten Modulen ist den 4.7k Pullup-Widerstand bereits enthalten, anderseits fügen Sie ihn hinzu. Schliessen Sie die Datenleitung am konfigurierten GPIO-Port (Standard ist Pin #7).

 

Das Design des 1-Wire-Treibers ist etwas ungewöhnlich, weil die Daten vom Sensor nicht durch einen direkten Funktionsaufruf gelesen werden. Vielmehr wird nach dem Booten des Raspberry Pi der Sensor vom Betriebssystem laufend in regelmässigen Abständen ausgelesen und die Ergebnisse in eine Datei geschrieben, die dann mit einem Anwenderprogramm analysiert werden kann. Dies ist ein bekannter Trick, um Daten von einem Prozess zu einem anderen zu übertragen.

(Aufgrund der hohen zeitlichen Anforderungen mit Impulsen in der Grössenordnung von 10 µs) kann die 1-Wire-Kommunikation nicht direkt mit Python programmiert werden.)

Beim Booten, schreibt der Treiber die Adressen aller aktiven 1-Wire-Geräte in die Datei

<masterFolder>w1_master_slaves

wobei wir hier die folgende Schreibweise verwenden:

<masterFolder> = /sys/devices/w1_bus_master1/

Für jeden aktiven Slave wird ein Unterverzeichnis in <masterFolder> erstellt. Als Name werden hexadezimale Ziffern der Adresse in der Form:

ff-nnnnnnnnnnnn

verwendet, wobei ff der Family-Code (1 byte) und nnn die ID (6 bytes) ist.

Wenn Sie dtoverlay- Zeile in /boot/config.txt wegnehmen und damit den Treiber deaktivieren, wird die Verzeichnis-Struktur vom <masterFolder> beim nächsten Booten gelöscht.

 

Beispiel für den Ordner <w1-root>

Im Unterverzeichnis <masterFolder> sehen Sie die rechts abgebildete Verzeichnisstruktur. Die empfangenen Daten sind in der Datei w1_slave gespeichert. Es gibt zwei Zeilen, die etwa so aussehen:

6d 01 4b 46 7f 03 10 70 : crc=70 YES
6d 01 4b 46 7f 03 10 70 : crc=70 t=22812

Die aktuelle Temperatur finden Sie am Ende der zweiten Zeile (ab Position 29).

Diese Informationen sind wichtig, damit Sie mit Ihrem Python-Programm den Temperaturwert extrahieren können.

 

Beispiel für den Slave-Ordner

Programm: [►]

# Temp1.py

import time
from py7seg import Py7Seg # xxx

masterFolder = "/sys/devices/w1_bus_master1/"

def getSlaveFolders():
    # Get list of all 1-Wire slave folders
    file = open(masterFolder + "w1_master_slaves")
    slaveFolders = file.read().splitlines()
    file.close()
    return slaveFolders

def getTemperature(slaveFolder):
      # Read content of corresponding w1_slave file. Format:
      # 6f 01 4b 46 7f ff 01 10 67 : crc=67 YES
      # 6f 01 4b 46 7f ff 01 10 67 t=22937
      file = open(masterFolder + slaveFolder + '/w1_slave')
      lines = file.read().splitlines()
      file.close()
    
      # Extract temperature from second line
      temperature = float(lines[1][29:]) / 1000
      return temperature

ps = Py7Seg() # xxx
slaveFolders = getSlaveFolders()
while True:
    # Extract temperature from first slave
    temp = getTemperature(slaveFolders[0])
    print("T = %6.2f deg" %temp)
    w = "%4.1f" %temp
    ps.showText(w[0] + w[1] + w[3] + '#'], dp = [0, 1, 0])  
    time.sleep(1)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:

Die Temperaturwerte sollten nicht häufiger als einmal pro Sekunde abgefragt werden, da die Funktion getTemperature() blockiert, bis das System den neuen Wert in die Datei geschrieben hat. Wenn Sie keinen ELV- Display angeschlossen haben, kommentieren Sie die Zeilen mit xxx aus.

Temperatur- und Feuchtigkeitssensor mit 1-Wire Bus
DHT11, DHT22 (auch AM2302 genannt ) sind Module, die Temperatur und Feuchtigkeit messen und die digitalisierten Daten über den 1-Wire-Bus übertragen. Um das Modul zu verwenden, benötigen Sie einen speziellen Treiber. Zurzeit stehen Treiber von Adafruit und Pigpio zur Verfügung. Die Installation beider Treiber ist einfach, aber bei Pigpio muss vor der Programmausführung ein daemon gestartet werden.

Die Installation des Adafruit-Treibers erfolgt gemäss den Anleitungen auf der Website http://www.adafruit.com.

Ziel:
Jede Sekunde die Raumtemperatur und die Luftfeuchtigkeit mit dem DHT11 oder DHT22/AM2302 Sensor messen und die Ergebnisse in der Konsole oder auf einem angeschlossenen Display (falls vorhanden) anzeigen.

Schaltschema:

Wenn Sie ein Sensormodul verwenden, das auf einer kleinen Adapterplatine montiert ist, so ist der Pullup-Widerstand in der Regel integriert. Schliessen Sie die Datenleitung am Port an, den Sie für die 1-Wire-Kommunikation konfiguriert haben (standardmässig Pin #7).

 

hand1 Schliessen Sie die Adapterplatine an der 3.3 V Spannungsversorgung an. Für den Betrieb mit 5 V müssen Sie den Pullup-Widerstand entfernen.

Programm: [►]

# DHT1.py

import Adafruit_DHT
import time
from py7seg import Py7Seg # xxx

ps = Py7Seg() # xxx
SENSOR_TYPE = Adafruit_DHT.DHT11
P_DHT = 4 # GPIO numbering (Pin # 7)
while True:
    hum, temp = Adafruit_DHT.read_retry(SENSOR_TYPE, P_DHT)
    if temp == None:
        t = "--.-"
    else:
        t = "%2.1f" %temp
    if hum == None:
        h = "--.-"
    else:
        h = "%2.1f" %hum
    print "Temperature", t, "Humitity", h
    ps.showText("t" + t[0] + t[1] + t[3], dp = [1, 0, 0]) # xxx 
    time.sleep(3)    
    ps.showText("h" + h[0] + h[1] + h[3], dp = [1, 0, 0]) # xxx    
    time.sleep(3)  
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)