Wir basteln uns einen Datenlogger – Die Software, dem Logger sagen, dass er loggen soll

790px-Kaffeetasse_Milchkaffee_Cafe-au-Lait_CoffeeGut, mit dem Arduino, dem Loggershield und der Verdrahtung, sowie mit dem Sensor, haben wir jetzt alles beieinander und können unseren Logger zum Aufzeichnen bringen. Um das zu tun, müssen wir dem Arduino sagen, was er zu tun hat. Das tun wir mit der Arduino-Programmiersprache. Was ist das und warum brauchen wir das?

Wie wir im letzten Teil gesehen haben, ist ein Arduino nicht besonders intelligent. Zu jedem normalen Menschen kann man sagen: „Wärst Du so nett und bringst mir eine Tasse Kaffee?“ und er oder sie wird in der Lage sein, diesen Auftrag ohne weiteres auszuführen, vorausgesetzt, alles Notwendige ist vorhanden. Wenn man nun das gleiche von einer Maschine erwartet, muss man ihre Sprache sprechen (oder einen Übersetzer haben, der sich „Compiler“ nennt) und man muss über die Aufgabe für das Dings so nachdenken, als ob das Dings nichts von dieser Welt weiß. Was, genau betrachtet, ja für jede Maschine zutrifft. Also, um bei unserem Beispiel zu bleiben, um eine Maschine zu programmieren muss man sagen:

Wenn Du das Kommando „Wärst Du so nett und bringst mir eine Tasse Kaffee?“ hörst, tue folgendes:

1. Gehe in die Küche
2. Falls die Tür zu sein sollte, öffne sie, um in die Küche zu gelangen
3. Gehe zum Schrank
4. Öffne die Schranktür
5. Nimm eine Tasse heraus
6. Schließe die Schranktür
7. Platziere die Tasse unter der Kaffeemaschine
8. Drücke den ersten Knopf
9. Warte, bis die Flüssigkeit die Tasse gefüllt hat
10. Nimm die Tasse in aufrechter Position heraus
11. Bringe die Tasse der Person, die das Kommando gegeben hat

Blöd, oder? Sie würden wahnsinnig werden an einem Assistenten, der so präzise Anweisungen braucht. Das ist es, weswegen es manchen Menschen schwer fällt, zu programmieren – es ist sehr kompliziert, so einfach zu denken. Aber egal, wir wollen, dass unser Logger loggt, also lassen Sie uns Schritt für Schritt die Programmierung durchgehen. (Das komplette Programm können Sie aus dem Schnellstart heraus kopieren):

Der Teil, der durch /* eingeleitet wird und mit */ endet ist ein Kommentar, etwas, das für Menschen geschrieben wurde, nicht für den Arduino. Stellen Sie es sich so vor, wie man manchmal Wörter buchstabiert, damit Kinder sie nicht verstehen. Nun, bei Kindern kann man nie sicher sein, aber mit /* */ beim Arduino schon.

Sie verwenden Kommentare um sicher zu gehen, dass ein anderes menschliches Wesen versteht, was Sie mit einem bestimmten Teil der Programmierung bezwecken. Die Chance ist hoch, dass Sie dieses menschliche Wesen sind, denn nach einer gewissen Zeit werden Sie sich nicht mehr daran erinnern, warum Sie manche Dinge so und nicht anders gelöst haben. Kommentare sind Teil einer guten Dokumentation, etwas, was wir Sammlungsmenschen lieben, richtig?

Als nächstes fügen wir einige Bibliotheken, „libraries“, in unser Programm ein.

Wir haben ja schon gesehen, was ein Arduino-Gehirn braucht, um eine Tasse Kaffee holen zu können. Nun, jemand hat schon mal alle Schritte, angefangen von „1. Gehe in die Küche…“ in einer Bibliothek zusammengefasst. Wenn man jetzt seinen Arduino-Assistenten programmieren möchte, muss man nur am Anfang „#include <Kaffee.h>“ in das Programm einfügen und wann immer Sie sagen „Wärst Du so nett und bringst mir eine Tasse Kaffee?“ wird der Assistent in der Lage sein, alle notwendigen Schritte auszuführen, um Ihnen eine schöne, heiße Tasse Kaffee zu bringen. Die Bibliothek wird auch enthalten was er tun soll, wenn die Maschine abgeschaltet ist, der Wassertank leer ist, der Kaffee aus ist…

Nun muss ich zugeben, dass ich auch nicht alle Bibliotheken verstehe, die bei unserem Logger eingebunden wurden. Von manchen weiß ich nur so viel, dass ich sie brauche und das weiß ich, weil sie in einigen Beispielprogrammen eingebunden waren. Ich denke, das ist als ob man eine Restauratorin/Restaurator bräuchte – natürlich muss ich wissen, was er oder sie können sollte, aber man muss nicht vollständig verstehen, was er oder sie tut. Obwohl es natürlich auch so ist, dass, je besser man versteht was er oder sie macht, es desto effektiver ist, zusammen zu arbeiten.

Bei unserem Logger haben wir einige Bibliotheken eingebunden, damit er:

  • einige Funktionen der Programmiersprache C versteht (stdlib.h)
  • weiß, wie man mit Zeit umgeht, also versteht, dass es Sekunden, Minuten, Stunden, Tage,… gibt (Time.h)
  • weiß, wie man die Echtzeitumhr auf dem Loggershield abliest (DS1307RTC.h)
  • weiß, wie man über I2C kommuniziert (Wire.h)
  • versteht, was unser Sensor ihm zu sagen hat (DHT.h)
  • weiß, wie man mit Peripheriegeräten wie einem SD-Karten-Leser kommuniziert (SPI.h)
  • weiß, wie man eine SD-Karte liest und auf ihr schreibt (SD.h).

Als nächstes definieren wir, wo unser Sensor ist und welchen Typ wir verwenden. Die DHT-Bibliothek, die wir eingebunden haben kann nämlich unterschiedliche Sensoren verstehen: DHT11, DHT21 und DHT22, also müssen wir angeben, dass wir einen DHT22 verwenden und diesen an Pin 9 angeschlossen haben. Die Bemerkungen hinter den // sind wieder Kommentare für den Menschen, nicht für den Arduino:

Als nächstes bekommt der Sensor einen Namen, so dass wir ihm Befehle geben können.

Um die Sache einfach zu machen, haben wir ihn „dht“ in Kleinbuchstaben genannt, aber wir hätten ihn genau so gut „Walter“, „Gretchen“ oder „sensor1“ nennen können. Es ist nur wichtig, dass er konsequent so genannt wird und dass wir mit der Groß- und Kleinschreibung aufpassen. Denn für unseren Arduino ist „Gretchen“ etwas anderes als „gretchen“ und das Programm wird nicht laufen, wenn wir hier einen Fehler machen.

Die nächste Zeile stellt sicher, dass wir die SD-Karte verwenden können, obwohl wir ein Shield verwenden. In unserer Bibliothek ist nämlich der Pin 4 für eine bestimmtes Aktion vorgesehen, aber der wird vom Shield für etwas anderes verwendet. Also muss der Arduino statt dessen Pin 10 verwenden.

Bisher haben wir nur dafür gesorgt, dass der Arduino grundsätzlich ein paar Dinge weiß. als nächstes gehen wir ins „setup“. Stellen Sie sich einfach vor, ihr neuer Assistent kommt zur Tür herein. Bevor Sie ihm irgendwelche Aufträge erteilen können, müssen Sie ihn herumführen. Wo ist die Toilette? Wo ist die Küche? Wo ist die Kaffeemaschine… Das passiert alles innerhalb der geschweiften Klammern nach „void setup“.

Tatsächlich sagen wir unserem neuen Assistenten erst einmal, wie er/sie mit uns reden soll. Unser Arduino wird in der Lage sein, uns zu sagen was er tut, in dem er etwas verwendet, was „serial communication“ heißt. Er wird in der Lage sein, Informationen über das USB-Kabel zu schicken, die wir dann auf dem Serial Monitor unserer Arduino-Software lesen können. Die Zeile Serial.begin(9600) ist als ob wir unserem Assistenten sagen, dass er deutsch mit uns reden soll.

Als nächstes sagen wir unserem Arduino dass er Pin 7 und 8 als Ausgang (output) verwenden soll. Dort sind unsere beiden LEDs angeschlossen, aber das weiß unser Arduino nur, wenn wir es ihm sagen. Es gibt für einen Pin zwei Möglichkeiten: entweder er ist ein Ausgang oder ein Eingang (input). Wenn wir ihn als Ausgang definieren können wir an ihn Signale senden, die dann mit dem Ding, das dort angeschlossen ist, etwas tun. In unserem Fall können wir ein „HIGH“ Signal senden, dann schaltet sich die LED an, oder ein „LOW“ Signal, dann schaltet sie sch aus.
Wenn wir einen Pin als Eingang definieren, dann „hört“ der Arduino darauf, was an diesem Pin passiert. Wenn der Arduino dort ein Signal erhält, kann er darauf entsprechend reagieren. Aber in unserem Fall brauchen wir nur einen Ausgang für die LEDs.

Jetzt testen wir, ob unsere SD-Karte funktioniert. Zunächst schicken wir den Satz „Initializing SD card…“ an unseren Serial Monitor, so dass wir es sehen können.
Dann definieren wir noch einen Pin, Pin 10, als Ausgang, da unser SD-Karten-Leser es so braucht (das wissen wir aus dem Beispielprogramm).

Nun testet der Arduino, ob er die SD-Karte lesen kann. Wenn er sie nicht lesen kann, schickt er die Nachricht „Card failed, or not present“ an den Serial Monitor.
Aber „in freier Wildbahn“ haben wir kein USB-Kabel und keinen Computer, nur den Logger an sich. Also benutzen wir unsere rote LED um uns die gleiche Nachricht mitzuteilen. Wenn der Arduino den SD-Karten-Leser nicht erkennt, schaltet er für 5 Sekunden die rote LED an. In Arduino-Sprache werden Zeitintervalle in Millisekunden angegeben. Sie sehen, dass wir der LED ein „HIGH“ Signal senden, dann für 5000 Millisekunden warten (delay), um dann ein „LOW“ Signal zum Abschalten der LED zu senden.
Hier kommt nun auch das Wattestäbchen zum Einsatz: Wenn die rote LED anzeigt, dass die SD-Karte fehlt oder nicht richtig eingeschoben ist, kann man die SD-Karte erneut einsetzen und auf das Wattestäbchen drücken, das im Inneren des Gehäuses mit dem Reset-Knopf verbunden ist. Der Arduino macht einen Neustart und versucht es noch einmal.

Wenn der Arduino die SD-Karte dagegen lesen kann, sendet er die Nachricht „card initialized.“ an den Serial Monitor. Als nächstes schickt er „DHTxx test!“. Wieder haben wir keine Ahnung, ob die SD-Karte gelesen werden konnte, also schalten wir die grüne LED an Pin 8 für 5 Sekunden ein wenn alles in Ordnung ist.

Mit dem einfachen Befehl „dht.begin();“ teilen wir unserem Sensor mit, dass er anfangen soll, seine Umgebung zu überwachen.

Damit ist unser setup beendet und wir können unserem Assistenten sagen, was er oder sie den lieben langen Tag tun soll. Das tun wir in der „loop“-Funktion. Diese Funktion wird sich ständig wiederholen, wenn wir nichts hineinschreiben, was sie stoppt (oder wenn der Stecker gezogen wird).

Was wir immer wieder machen wollen ist zu lesen, wie hoch die relative Luftfeuchtigkeit und Temperatur in unserem Raum ist, richtig? Um die Werte von unserem Sensor zu lesen, rufen wir „dht.readHumidity“ für die Feuchtigkeit und „dht.readTemperature“ für die Temperatur.
Wenn wir diese Werte noch ein paar Mal in unserem Programm brauchen, benutzen wir etwas, was sich „Variable“ nennt. Eine Variable ist so etwas wie eine Tasche. Wir können einen Wert darin speichern und mit uns herumtragen. In unserem Fall nennen wir eine Variable „h“ für Feuchtigkeit (humidity) und „t“ für Temperatur. Taschen gibt es in allen Größen und Formen, genau so Variablen. Sie würden die kleine, schwarze Handtasche für eine Essenseinladung nehmen und Ihren Rucksack für einen Tagesausflug, eben für jeden Anlass und Platzbedarf genau das richtige. Unsere Daten vom Sensor sehen z.B. so aus: 14.5 oder 34.8, sie kommen als Fließkommazahlen. Also wählen wir den Variablentyp „float“ (Fließkommazahl) für sie. Es gibt noch viele andere Variablentypen, aber für heute reicht uns erst einmal zu wissen, dass „float“ der richtige Typ für unsere Sensorwerte ist.
Zusammengefasst: Die folgenden Zeilen unseres Programmes speichern die Sensorwerte in den Variablen „h“ und „t“. Wenn wir diese Variablen im folgenden Teil des Codes aufrufen, werden sie die aktuellen Sensorwerte ausspucken.

Aber was passiert, wenn unser Sensor etwas ausgibt, das kein gültiger Wert für Feuchtigkeit oder Temperatur ist? Der nächste Teil des Programms überprüft genau das und reagiert dementsprechend.

Wenn entweder der Feuchtigkeitswert in „h“ oder der Temperaturwert in „t“ keine Zahl ist, wird uns der Arduino darüber informieren, indem er auf dem Serial Monitor schreibt: „Failed to read from DHT sensor!“ Der Ausdruck dafür, dass etwas keine Zahl ist heißt „isnan“ (für IS Not A Number). Statt „oder“ zwischen diesen beiden Bedingungen zu schreiben, müssen wir eine Sprache verwenden, die der Arduino versteht, das sind hier die beiden vertikalen Striche || (davon gibt es wieder einige, wie && für „und“, > für „größer als“ oder < für „kleiner als“ ).

Wieder können wir das in freier Wildbahn nicht sehen, also lassen wir unsere rote LED wie verrückt blinken, wenn die Sensorwerte keinen Sinn ergeben, z.B. weil der Sensor falsch angeschlossen wurde. Es mag wesentlich elegantere Methoden geben so etwas zu programmieren, aber ich bin schließlich nur Depotleiterin, kein IT-Profi.

Als nächstes lassen wir uns die ausgelesenen Werte auf dem Serial Monitor anzeigen, für den Fall dass ein Computer angeschlossen ist. Inzwischen sollten Sie in der Lage sein zu verstehen, was passiert:

Jetzt brauchen wir die Zeit von der Echtzeituhr. Übrigens: vergessen Sie nicht, dass Sie die Zeit am Anfang einmal einstellen müssen, wenn Sie die korrekte Zeit haben möchten, das passiert mit dem Beispielprogramm aus der RTC library. Im Grunde genommen sagen wir mit den folgenden Zeilen einfach „schau auf die Uhr und merke dir alles, was Du liest in der Variablen „tm“.“ Auf diese Weise können wir später nach dem spezifischen Tag, Monat, Stunde, Minute, Sekunde… fragen.

Was nun folgt, ist vielleicht ein wenig verwirrend zu erklären und anzusehen. Wir wollen unsere Daten auf der SD-Karte speichern, auf eine Art und Weise, bei der jeder Datenpunkt durch ein Komma vom nächsten getrennt ist. Auf diese Art können wir die Daten später in jedem beliebigen Tabellenkalkulationsprogramm verarbeiten, indem wir die Daten in einem Format einlesen, das CSV für „comma separated values“ heißt. Das Problem ist, unsere Daten sind Nummern. Sie erinnern sich, dass wir unsere Sensorwerte als Fließkommazahlen definiert haben? Genau!

Was wir aber brauchen, um die Daten abspeichern zu können sind Zeichen, in anderen Worten, einen „string“. Um noch genauer zu sein, wir brauchen einen string, eine Aneinanderreihung von Zeichen, der alle Daten umfasst, die wir speichern wollen. Wir möchten etwas erhalten, was in etwa so aussieht:
„34.8, 14.5, 2017, 04, 14, 2, 45, 23,“
Das können wir dann so in unser Tabellenkalkulationsprogramm importieren, dass wir eine Zeile erhalten, die 34.8 % Luftfeuchtigkeit, 14.5 Grad Celsius am 14. April 2017 um 14:45 (und 23 Sekunden) angibt.

Um das zu erreichen, nehmen wir eine neue Variable, unsere „Tasche“ heißt dieses Mal „dataString“ und soll alle unsere Daten aufnehmen. Ich muss zugeben, dass ich nicht verstanden habe, was Zeile 116 wirklich tut, aber sie hat etwas damit zu tun, welcher Platz für unsere Werte zur Verfügung steht.

Was als nächstes passiert ist, dass wir alle unsere Werte, die wir speichern wollen in unsere „Tasche“ namens „dataString“ packen. Das machen wir Stück für Stück, genau so, wie wir im wirklichen Leben unsere Tasche öffnen würden, um das Maßband, die Handschuhe, den Lippenstift,… hineinzutun. Das Tückische ist, dass wir erst unsere Nummern in Zeichen umwandeln müssen. Hmmm… vielleicht so, wie wenn man Flüssigkeiten in eine Tasche tun will. Man muss sie erst in einen Behälter packen. Naja, vielleicht nicht ganz so, aber so ähnlich.

Also tun wir unseren Feuchtigkeitswert in ein Behältnis. Wir nennen das Behältnis „stringH“. Die Funktion „dtostrf“ macht das mit unserer Variablen „h“, die, wie wir wissen, die Fließkommazahl unseres Luftfeuchtigkeitswertes ist. Dann tun wir unseren Behälter „stringH“ in unsere Tasche „dataString“:

Wir haben gesagt, wir wollen am Ende durch Komma getrennte Werte, also müssen wir jetzt noch ein Komma anhängen. Wir nehmen unsere „dataString“ Tasche und tun ein Komma rein, indem wir „+=“ als Kommando zum Anfügen verwenden. Los geht’s:

Das gleiche für unseren Temperaturwert:

Was ist jetzt in unserer Tasche? Etwas, was folgendermaßen aussieht: „34.8, 14.5,“. Man kann sicher gehen, wenn man sich das über den Serial Monitor bestätigen lässt, indem man folgende Zeile einfügt:

Das haben wir jetzt mal gelassen. Statt dessen sammeln wir nun in unserer Tasche nach und nach die Werte für den Tag, Monat, Jahr, Stunde, Minute und Sekunde, alle durch Komma getrennt. Randnotiz: Ich habe später herausgefunden, dass man nicht alle Werte hätte mit Komma trennen müssen, aber dazu vielleicht später in der Serie. Im Moment wissen wir erstmal nur, dass es funktioniert.

Puh, das ist eine Menge Programm. Unsere „Tasche“ dataString hat jetzt folgenden Inhalt: „34.8, 14.5, 2017, 04, 14, 2, 45, 23,“. Als nächstes schreiben wir das auf die SD-Karte. Dafür müssen wir erst einmal die Datei öffnen, auf die der string geschrieben werden soll.

Wenn der Arduino die Datei „Mylogger.csv“ auf der Karte findet, öffnet er sie, schreibt den Inhalt von „dataString“ darauf (er hängt ihn ans Ende aller darauf befindlichen Daten an) und schließt die Datei wieder. Auftrag ausgeführt!

Was daran großartig ist: wenn es keine Datei namens „Mylogger.csv“ auf der SD-Karte gibt, erzeugt sie der Arduino einfach. Nur in dem Fall, in dem eine solche Datei existiert, aber nicht geöffnet werden kann oder wenn die SD-Karte fehlt brauchen wir einen Fehlercode, der uns über den Serial Monitor informiert und die rote LED bis zum nächsten Durchgang anschaltet:

Zuletzt müssen wir noch definieren, wie lange der Arduino zwischen den Messungen warten soll. Je häufiger man ausliest, desto mehr Daten erhält man, was natürlich detaillierter ist, aber auch mehr Speicherplatz braucht. Im Beispiel warten wir 5 Minuten zwischen den Messungen, 300000 Millisekunden. Für ein 10-Minuten-Interval müsste man es auf 600000 Millisekunden setzen und so weiter.

Das ist alles, das ist das ganze Programm. Da ist natürlich noch viel Spielraum für Verbesserungen, zum Beispiel wenn wir die Temperatur in Fahrenheit messen oder den Taupunkt berechnen möchten. Aber das wird ein anderer Teil der Serie sein…

Facebooktwittergoogle_plusredditpinterestlinkedintumblrmail

This post is also available in: Englisch

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.