Temperaturen messen mit dem Arduino

Eine Lösung für Power-User

Die 1Wire-Temperatursensoren DS18S20 lassen sich auf sehr einfache Weise direkt mit einem PC auslesen, nachzulesen im Artikel Temperaturen messen mit Linux. Der einzige Nachteil ist der hohe Stromverbrauch eines PCs. Selbst ein extrem sparsamer Rechner mit Atom-Prozessor erhöht die Stromrechnung um rund 25 Euro pro Jahr, wenn er 24 Stunden pro Tag läuft.

Für viele Mess- und Steueraufgaben tut es aber auch ein wesentlich sparsamer Mikrokontroller, wie z.B. der Arduino, der den Einstieg in die Mikrokontroller-Programmierung kinderleicht macht: Fertig aufgebaut und mit USB-Anschluss kostet der “Arduino Duemilanove” nur ca. 26 Euro (z.B. bei Segor). Mit der Java-Entwicklungsumgebung, die es kostenlos im Netz gibt, sind die ersten Erfolgserlebnisse nur eine Sache von Minuten.

Arduino Duemilanove

Der Arduino Duemilanove – und mein fliegender Testaufbau mit zwei Temperatursensoren, die ich zusammen in eine Buchsenleiste gewürgt habe. Nicht schön, aber zum Testen ok. Als Stromversorgung reicht zunächst der USB-Anschluss. Soll der Arduino autark werden oder reicht die Leistung des USB-Anschluss nicht aus, nimmt der Arduino auch 7-12V Gleichstrom an.

Die bereits erwähnten DS18S20 Temperatursensoren lassen sich auch mit dem Arduino auslesen. Eine Bibliothek, die das dazu notwendige 1Wire-Protokoll implementiert, gibt es hier. Um mehrere Temperatursensoren komfortabel zu verwalten, war allerdings noch etwas Programmierarbeit nötig, die nun folgende Anforderungen erfüllt:

  1. Mindestens 10 Sensoren sollen verwaltet werden
  2. Alle Sensoren sollen einmal pro Sekunde ausgelesen werden
  3. Messgenauigkeit besser als 0,5°C
  4. Der Arduino soll neben dem Auslesen der Sensoren (gleichzeitig) noch andere Dinge erledigen können, durch den Auslesevorgang also nur möglichst kurz “geblockt” werden

Das Ergebnis ist die C++-Klasse DS18S20_List, die diese Aufgabe auf wenige Zeilen Code reduziert. Das folgende Beispiel gibt die Messwerte aller Sensoren im 1-Sekunden-Takt über die USB-Schnittstelle aus:

#include <DS18S20.h>

DS18S20_List ds18s20(10); // pin 10

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  
  delay(1000);

  ds18s20.update(); 
  
  for (int i=0;i<ds18s20.count;i++)
    {
    Serial.print("Sensor ");
    Serial.print(i,DEC);
    Serial.print(", T=");
    print_temperature(ds18s20.get_temp(i));
    Serial.println("C");
    }
    
}

Des Weiteren ist es möglich, einen bestimmten Sensor über eine 16-bit Identifikationsnummer (id) anzusprechen. Das ist immer dann sinnvoll, wenn später Sensoren hinzugefügt, entfernt oder ausgetauscht werden sollen. Denn die Reihenfolge, in der die Messwerte ausgegeben werden, wird durch die Seriennummern der Sensoren festgelegt. Über die Sensor-Id lässt sich dagegen immer derselbe Sensor ansprechen:

#include <DS18S20.h>

DS18S20_List ds18s20(10); // pin 10

#define ID_OUTSIDE 0xABC5

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  
  delay(1000);

  ds18s20.update(); 
  
  float T_outside=ds18s20.get_temp_by_id(ID_OUTSIDE);

  Serial.print("Außentemperatur: ");
  print_temperature(T_outside);
  Serial.println("C");
  
}

Die Sensor-ID wird übrigens auch vom Demo-Programm (siehe Download) angezeigt, lässt sich also leicht ermitteln und kann dann “hardcoded” werden.

Downloads

  1. Bibliothek DS18S20. Dieser Ordner muss in das Verzeichnis arduino-0xx/hardware/libraries entpackt werden. Er enthält neben der DS18S20_List Klasse auch die notwendige OneWire-Bibliothek.
  2. Demo-Programm (Sketch für die Arduino-Entwicklungsumgebung)

Elektrik

Alle drei Beinchen der Sensoren müssen verdrahtet werden. Ground geht an den Masse-Pin der Arduino-Platine, Data geht an einen beliebigen digitalen Pin und gleichzeitig über einen 5kOhm-Pull-up-Widerstand an +5V, die +5V Pins von Sensor und Arduino werden direkt miteinander verbunden. Grundsätzliches zum Aufbau eines 1Wire-Netzes findet sich hier.

Details zur Umsetzung

Punkt 1) ist mit einem Bugfix in der OneWire-Bibliothek erledigt.

Punkt 2) ist leider etwas aufwändiger: Ein Sensor benötigt knapp 1 Sekunde, um eine Messung vorzunehmen. Im “Parasite-Power-Mode”, wenn die Sensoren also nur mit zwei Adern angeschlossen sind (Masse+Data), müssen die Sensoren jedoch nacheinander die Temperaturmessung vornehmen: Für den gleichzeitigen Betrieb stellt die Datenleitung nicht genügend elektrische Leistung zur Verfügung. Wird das dritte Beinchen der Sensoren jedoch auf +5V gelegt, können alle Sensoren gleichzeitig die Messung vornehmen und die Messwerte können einmal pro Sekunde von allen Sensoren abgefragt werden.

Punkt 3) Die Messgenauigkeit lässt sich mit einem im Datenblatt beschriebenen Algorithmus, der neben dem eigentlich Messwert noch zwei weitere Registerwerte verwendet, von den nominellen 9 bit (=0,5°C) auf ca. 0,1°C verbessern.

Punkt 4) erfordert eine Umorganisation des Programmablaufs: Anstelle einer Funktion “Temperatur_messen()”, die den Messvorgang starten, auf das Ende warten (ca. 1s) und die Daten auslesen würde, stehen die beiden Funktionen “convert()” und “read()”, wobei erstere nur den Messvorgang startet und letztere nur die Messwerte, die zunächst im Register des Sensors abgelegt werden, ausliest. Um das etwas komfortabler zu gestalten, kommt die Funktion “update()” zu Hilfe, die read() und danach convert() nur dann aufruft, wenn die Sensoren die letzte Messung schon beendet haben. Um das Timing muss man sich damit nicht mehr selbst kümmern.

Befehls-Referenz

class DS18S20_List : public OneWire
{
  [...]
public:
  int count;     

  DS18S20_List(unsigned char pin);
  
  void update();
 
  float get_temp(unsigned char i); 
  float get_temp_by_id(unsigned int id);
  unsigned int get_id(unsigned char i); 

  void convert();
  void read(unsigned char i); 
  void read();   
};
DS18S20_List(unsigned char pin)

Der Konstruktor benötigt die Nummer des Pins, mit dem die Daten-Beinchen der Sensoren (mittleres Beinchen) verbunden sind.

void update()

Diese Funktion prüft zunächst, ob die Sensoren noch mit einer Temperatur-Messung beschäftigt sind. Falls ja, wird die Funktion sofort wieder verlassen, damit der Arduino noch andere Dinge erledigen kann. Diese Funktion muss also regelmässig aufgerufen (gepollt) werden. Ist die Messung beendet, werden die Messwerte an den Arduino übertragen (read()). Danach wird ein neuer Messvorgang gestartet (convert()).

float get_temp(unsigned char i)

Liefert den letzten Messwert des i-ten Sensors in der Liste zurück. Dabei wird der im Speicher des Arduino zwischengespeicherte Wert verwendet, es wird an dieser Stelle also nicht mit dem Sensor kommuniziert.

float get_temp_by_id(unsigned int id)

Liefert den letzten Messwert des Sensors mit der Identifikationsnummer ‘id’ zurück. Dabei wird der im Speicher des Arduino zwischengespeicherte Wert verwendet, es wird an dieser Stelle also nicht mit dem Sensor kommuniziert.

unsigned int get_id(unsigned char i)

Liefert die ID des i-ten Sensors in der Liste zurück.

Die folgenden Funktion werden intern verwendet:

void convert()

Startet den Messvorgang auf allen Sensoren, wartet aber nicht auf dessen Beendigung.

void read(unsigned char i)

Überträgt den letzten Messwert des i-ten Sensors in den Speicher des Arduino.

void read()

Überträgt die Messwerte aller Sensoren in den Speicher des Arduino.


Posted

in

, ,

by

Tags: