Nun ist es soweit: Seit ein paar Minuten steht meine Stevia-Pflanze unter totaler Überwachung, mit Unterstützung von Arduino natürlich. Und das 24 Stunden lang oder länger. Das ist gewissermassen mein erstes echtes Projekt, das nicht mehr unter „Mit Arduino herumspielen“ läuft. Allerdings musste ich erst etwas umdisponieren. Ursprünglich wollte ich ja auf meinem Balkon für meine Blumen Werte wie Temperatur, Feuchtigkeit, Bodenfeuchtigkeit und Licht messen.
Dabei war das Fernziel, den Arduino autonom zu betreiben. Das bedingt aber auch die Verwendung einer Real Time Clock (RTC) und eines SD-Card-Moduls (die teurere WiFi-Variante oder Funk-Variante wollte ich mir sparen). Mit etwas Herumknobeln brachte ich sogar SD-Card und RTC zum Laufen, aber die Initialisierung der Zeit auf dem 1302-Chip wollte nie so richtig klappen. Zwar lief die RTC-Komponente, aber eigentlich nie mit der aktuellen Zeit. Und irgendwann beim Herumprobieren habe ich dann das RTC-Modul vermutlich zerstört. Auf jeden Fall erhalte ich seit geraumer Zeit nur noch 1.1.2000 zurück.
Da man ein RTC-Modul nicht gleich um die Ecke kaufen kann, war ich gezwungen umzudisponieren. Mein Netbook ist ohnehin schlecht ausgelastet, und im Moment ist nicht die Jahreszeit für Freiluftmessungen. Es lag also nahe, die Daten nicht direkt im Arduino zu verarbeiten, sondern im Netbook, wo ich sie komfortabel mit Datum und Uhrzeit versehen kann. Da die Einbindung von COM-Ports in Java nicht ganz einfach ist, und ich schon öfter Webanwendungen in CSharp geschrieben habe, lag es nahe, dafür Visual Studio Express 2012 und CSharp zu verwenden.
Seit dieser Einleitung ist fast ein Monat vergangen. Erst jetzt habe ich mich aufgerafft, den Artikel fertig zu schreiben. Dafür gibt im Abschnitt Demo aber eine Datenreihe über diesen Zeitraum zu sehen.
Projektziel
In diesem Projekt misst man mit mehreren Sensoren für Pflanzen relevante Umgebungswerte wie Temperatur, Luftfeuchtigkeit, Bodenfeuchtigkeit und Helligkeit. Die Daten werden via serielle Schnittstelle an einen Computer gesendet, der sie mit einer CSharp-Konsolenapplikation entgegennimmt, mit Datum und Uhrzeit versieht und in eine CSV-Datei speichert.
Als Versuchspflanze bot sich meine Stevia an. Sie ist nicht frosthart, so dass ich sie im Spätherbst vom Balkon in die Wohnung übersiedeln muss, was ihr meist mangels Licht nicht sonderlich gut bekommt. Sie hängt an einem Blumat-Pfropfen, so dass ich dabei auch gleich überprüfen kann, wie gut diese Bewässerungsart funktioniert.
Komponenten
Die Komponenten für dieses Projekt stammen abgesehen vom Lichtsensor nicht aus meinem Arduino-Experimentierkit, sondern ich habe sie mir speziell für diesen Zweck gekauft.
Elektronische Komponenten
- 1 Arduino UNO R3
- 1 Steckbrett
- ganz viele Jumperkabel M/M und M/F
- 1 kombinierter Feuchtigkeits- und Temperatursensor: DHT11; Achtung mein Sensor weist nur 3 Pins auf und hat eine etwas andere Pin-Belegung als im Datenblatt
- 1 Sensor für die Bodenfeuchtigkeit: SKU:SEN0114
- 1 Lichtsensor aus Kit, vermutlich Excelitas Tech LDR VT90N2 oder VT935G
- 1 Widerstand 10kOhm
- 1 Netbook, Notebook oder sonstiger Computer mit installiertem .NET-Framework und Entwicklungsumgebung Visual Express Studio 2012 oder älterer Version
- 1 USB-Kabel zur Verbindung von Netbook und Arduino
Nichtelektronische Komponenten
- eine Stevia oder irgendeine andere Pflanze, die in der Erde wächst (ich würde mal vermuten, dass der Feuchtigkeitssensor mit Hydrokultur nicht wirklich gut funktioniert)
Bestückungsplan
Zur Zeit sind drei Sensoren in der Schaltung verbaut (Details siehe Komponentenliste):
- Sensor für Bodenfeuchtigkeit, als schwarzes Mystery-Teil dargestellt
- Photowiderstand als Lichtsensor unten links
- Feuchtigkeits- und Temperatursensor, blaues Kästchen rechts
Code
Der Code umfasst bei diesem Projekt nicht nur den Arduino- sondern natürlich auf den mit Visual Studio Express 2012 erstellten CSharp-Code, der die Daten aus der Schnittstelle entgegennimmt.
Arduino
Der Code für den DHT11 stammt von DFRobot.
//Dieses Programm macht nur mit der zughörigen CSharp-Anwendung Sinn! //CSharp liest Daten, gibt das Datum dazu //und hängt die Daten einer CSV-Datei an //generelle Einstellungen char tab = 9; unsigned long myDelay = 10000; int val = 0; //eingelesener Wert aus serieller Schnittstelle //Type ist nötig, weil Multiplikation mit 1000 zu integer führt unsigned long faktor = 1000; //Eingabe des Delays in Sekunden char incoming[5] = {}; //Lichtsensor int lightPin = 1; #define dht11_pin 14 //Analog port 0 on Arduino Uno byte read_dht11_dat() { byte i = 0; byte result=0; for(i=0; i< 8; i++) { while (!digitalRead(dht11_pin)); delayMicroseconds(30); if (digitalRead(dht11_pin) != 0 ) bitSet(result, 7-i); while (digitalRead(dht11_pin)); } return result; } void setup(){ pinMode(dht11_pin, OUTPUT); digitalWrite(dht11_pin, HIGH); Serial.begin(9600); } void loop(){ //Delay ändern int j = 0; //über die serielle Schnittstelle kann man das Intervall für die //Datenerfassung bestimmen if (Serial.available() > 0) { //sonst bleiben die 0 erhalten 60 -> 600 memset(incoming, 0, sizeof(incoming)); while (Serial.available() > 0 && j < sizeof(incoming) - 1) { incoming[j] = Serial.read(); j++; delay(3); } //array of char in int wandeln val = atoi(incoming); myDelay = faktor * val; Serial.print("neues Delay "); Serial.print(val); Serial.println(" Sekunden"); } //Daten für Temperatur- und Feuchtigkeitssensor auslesen byte dht11_dat[5]; byte dht11_in; byte i;// start condition digitalWrite(dht11_pin, LOW); delay(18); digitalWrite(dht11_pin, HIGH); delayMicroseconds(1); pinMode(dht11_pin, INPUT); delayMicroseconds(40); if (digitalRead(dht11_pin)) { Serial.println("!Temperatursensor dht11 startet nicht"); delay(1000); return; } delayMicroseconds(80); if (!digitalRead(dht11_pin)) { Serial.println("!Temperatursensor dht11 startet immer noch nicht"); return; } delayMicroseconds(80); for (i=0; i<5; i++) { dht11_dat[i] = read_dht11_dat(); } pinMode(dht11_pin, OUTPUT); digitalWrite(dht11_pin, HIGH); byte dht11_check_sum = dht11_dat[0]+dht11_dat[2];// check check_sum if(dht11_dat[4]!= dht11_check_sum) { Serial.println("!DHT11 Checksummen-Error"); } //Daten an die serielle Schnittstelle ausgeben //********************************************* //Feuchtigkeit und Temperatur //Feuchtigkeit Serial.print(dht11_dat[0], DEC); Serial.print("%"); Serial.print(tab); //Temperatur Serial.print(dht11_dat[2], DEC); Serial.print(tab); //********************************************* //Bodenfeuchtigkeit //bei 5V 0 an Luft, 800 im Wasser //bei 3.3V bis ca 485 float soilHumidity = analogRead(2); soilHumidity = constrain(map(soilHumidity, 0, 800, 0, 100), 0, 100); Serial.print(soilHumidity); Serial.print("%"); Serial.print(tab); //********************************************* //Licht //Wert von ca. 0 bis 1023 //Gesteckt 5 V - 1. Bein - 2. Bein 10 KOhm Widerstand //und Arduino-Pin - Ausgang Widerstand GND float lightLevel = analogRead(lightPin); //Mittel aus 3 Messungen delay(10); lightLevel = lightLevel + analogRead(lightPin); delay(10); lightLevel = lightLevel + analogRead(lightPin); lightLevel = map(lightLevel/3, 0, 1023, 0, 100); Serial.print(lightLevel); Serial.print("%"); Serial.print(tab); //********************************************* //Bodenfeuchtigkeit Eigenbau //bei 5V 0 an Luft, 800 im Wasser //bei 3.3V bis ca 485 float soilHumidity2 = analogRead(3); Serial.print(soilHumidity2); //Zeilenende Serial.println(""); delay(myDelay-500); //nächster Durchgang }
CSharp
Das CSharp-Programm ist ein reines Konsolenprogramm, das allerdings endlos läuft.
using System; using System.IO; using System.IO.Ports; using System.Configuration; using System.Collections.Specialized; using ArduinoReadFromSerial.Properties; namespace test { class Program { static void Main(string[] args) { Settings settings = new Settings(); SerialPort port = new SerialPort(settings.port, 9600); port.Open(); port.WriteLine(settings.delayInSek.ToString()); String filePathAndName = settings.exportpfad; String ext = ".csv"; String filedate = String.Format("{0:yyyyMMdd_HHmmss}", DateTime.Now); String fileAll = filePathAndName + "_" + filedate + ext; TextWriter tw = new StreamWriter(fileAll, false); Console.WriteLine(fileAll); String tab = char.ConvertFromUtf32(9); String header = "Datum" + tab + "Luftfeuchtigkeit in Prozent" + tab + "Temperatur in Grad Celsius" + tab + "Bodenfeuchtigkeit" + tab + "Licht in Prozent" + tab + "Bodenfeuchtigkeit Eigenbau"; Console.WriteLine(header); tw.WriteLine(header); // close the stream tw.Close(); while (true) { tw = new StreamWriter(fileAll, true); String myVar = port.ReadLine(); //Warnhinweise beginnen mit ! -> nicht in Datei schreiben if (!myVar.StartsWith("!")) { //Hier noch Datum und Zeit vorne dran hängen DateTime now = DateTime.Now; myVar = String.Format("{0:dd.MM.yyyy HH:mm:ss}", now) + tab + myVar; tw.Write(myVar); tw.Close(); } Console.WriteLine(myVar); } } } }
Und so sehen die produzierten Daten aus:
Datum Luftfeuchtigkeit in Prozent Temperatur in Grad Celsius Bodenfeuchtigkeit Licht in Prozent Bodenfeuchtigkeit Eigenbau 25.11.2012 23:16:00 37% 25 100.00% 73.00% 1019.00 25.11.2012 23:17:00 37% 25 100.00% 72.00% 1023.00 25.11.2012 23:18:00 37% 25 100.00% 73.00% 1022.00 25.11.2012 23:19:00 37% 25 100.00% 74.00% 1021.00 25.11.2012 23:20:00 37% 25 100.00% 73.00% 1021.00 25.11.2012 23:21:00 37% 25 100.00% 73.00% 1021.00 25.11.2012 23:22:00 37% 25 100.00% 73.00% 1021.00
Tipps zum CSharp-Projekt
Das Programm entwickelt habe ich auf meinem Notebook. Visual Studio Express auf meinem Netbook zu betreiben, würde mich ziemlich sicher auf die Palme bringen, weil das Kistchen zu wenig Ressourcen für den flüssigen Betrieb einer Entwicklungsumgebung hat. Also stand ich ganz rasch vor dem Problem, dass ich meine Konsolenapplikation konfigurierbar machen musste, so dass die Einstellungen für Dateipfad und seriellen Port aus einer änderbaren Konfigurationsdatei stammen. In einem Konsolenprojekt unter Visual Express Studio 2012 erstellt man diese, indem man im Projektmappen-Explorer Properties direkt unterhalb des Projektnamens doppelklickt. Unter „Einstellungen“ habe ich die folgenden Werte erfasst:
- exportpfad: C:\meinpfad\meinedatei (ohne Datei-Endung, da noch Datum und Uhrzeit angehängt wird)
- port: COM9 (wechselt je nach Computer)
- delayInSek: 60 (so kann man die Frequenz, mit der Daten gesammelt werden, individuell einstellen)
Um die Applikation auf anderen Rechner installieren zu können, geht man unter Erstellen – MeinProjekt veröffentlichen.
Da die Konsolen-Applikation in ein absolut obskures Verzeichnis installiert wird, und ich nicht jedesmal danach suchen wollte, um meine Einstellungen in der Config-Datei für das Netbook zu ändern, kam ich auf die Idee, die Konfigurationsdatei noch vor der Installation zu ändern, denn vom Installationsverzeichnis weiss ich genau, wo es liegt. Doch ich hatte die Rechnung ohne Microsoft gemacht: Die Installation wurde aus Sicherheitsgründen abgebrochen, weil der Hash-Code der von mir manipulierten Konfigurationsdatei nicht mehr mit jenem des Projektes übereinstimmte!
Demo
Mit einer bestimmten Zahl an Sensoren wird die Schaltung ziemlich unübersichtlich. Oben links sieht man Bodenfeuchtigkeitssensor, bzw. jener Teil davon, der nicht im Boden steckt. In der linken oberen Ecke des Steckbretts steht der Photosensor. Und das blaue Teil im unteren Teil des Steckbretts ist der kombinierte Feuchtigkeits- und Temperatursensor. Das orange Kabel führt zu einem nicht sichtbaren und auf dem Bestückungsplan nicht erwähnten selbst gebastelten Feuchtigkeitssensor: Ein Stück Styropor mit zwei verkabelten Nägeln. Am Arduino hängt übrigens auch schon die SD Card, auch wenn sie im Moment noch absolut funktionslos ist.
Und wozu das Ganze? Am Schluss kann man die Daten aus der Textdatei in Excel holen und mit PivotTables auswerten oder als Diagramme darstellen. Für den Lichtverlauf sieht das folgendermassen aus: