// // ************************************************************************************* // *** komplett ueberarbeitete Version. *** // *** ACHTUNG: Original-Code stimmte nicht mit Librarie ueberein. DEC = Dezimal *** // *** *** // *** Aufgabe: *** // *** Temperatur-Messung, Luftfeuchte *** // *** Luftdruckmessung, in Verbindung *** // *** der Echtzeit, auf SD-Karte speichern *** // *** Stand: V3 - Interval-Speicherung - 13.03.2016 *** // *** V4 - 1.8"-TFT-Display *** // ************************ DL2EBZ ***************************************************** // *** Anschluesse: // *** DHT22 -Pin = D9 // *** DS3231-SDA = A4 // *** -SCL = A5 // *** BMP085-SDA = A4 // *** -SCL = A5 // *** SD-Card mit Zugriff auf den SPI-Bus // *** MOSI - pin 11 / 51 // *** MISO - pin 12 / 50 // *** CLK - pin 13 / 52 // *** CS - pin 4 / 53, bzw. SS // ************************************************ // *** offene Punkte: // *** a) SD-Karte einbinden --> OK, 02.03.2016 // *** b) Speicher-Zyklus einstellen --> OK, 12.03.2016 // *** c) Wenn-Dann-Auswahl beim Tages-Datum instand setzen --> OK, 13.03.2016 // *** d) Zeit= jede Sekunde, Sensorik= alle 30 Sekunden, Speichern= alle 10 Minuten // *** ===> allerdings nur ein einziges Mal !!!! Nicht eine ganze Minute lang jede Sekunde --> OK, 13.03.2016 // *** ==> Erweiterung um die sekundengenasue Ausgabe nach Volker d.V., --> OK, 14.03.2016 // *** // *** e) Temperatur auch vom DS3231 auslesen (muss ueber die Kontrolle der vorhandenen I2C-Devices aufzufinden sein) // *** f) ... auch in Fahrenheit --> f = c*9. / 5. + 32 // *** g) Monats-Namen ausgeben (muss eine vorhandene Funktion in der TIME.h sein) --> monthShortStr(now.month()) --> OK, 02.03.2016 // *** h) Moeglichkeit einer Zeit-Korrektur schaffen --> 19.01.2016 // *** i) SD-Card einbinden --> schreiben, auslesen, sowie löschen // *** j) Darstellung einer Balkengrafik für Barometer-Verlauf // *** k) Anzeige auf TFT-Display, statt LCD-Display, Version-4 (Sensorik_SD_TFT.ino) --> OK, 19.03.2016 // *** l) Das Datums muß noch vor einer jeden Aenderung 'uebermalt werden' !! // *** // *** DHT22 OK, BMP085 OK, DS3231 OK, SD-Kartenleser OK // *** // *** SA und SU bezogen auf Standort berechnen // *** MA und MU bezogen auf Standort berechnen // *** Tiedenkalender für z.B. Borkum berechnen // *** Kompass-Kurs für Sonnenuntergang berechnen // *** // ************************************************ // #include // Hier wird die Library des DS1307 genutzt !! #include // #include // I2C-Bibliothek einbinden #include "RTClib.h" // RTC-Bibliothek einbinden #include #include // SD-Karte einbinden RTC_DS1307 RTC; // RTC Modul // ************** L C D ********************* // #include // LCD-Bibliothek einbinden // LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Auswahl der Anschluesse fuer das 2x 16-LCD-Paneel // LiquidCrystal lcd(2, 3, 4, 5, 6, 7); // Auswahl der Anschluesse fuer das 4x 20-LCD-Paneel // ************** TFT ************************ #include // Core graphics library #include // Hardware-specific library #define TFT_CS 10 #define TFT_RST 9 // man kann auch den Arduino reset nutzen. In dem Fall lautet die Zeile #define pin 0! #define TFT_DC 8 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); #define TFT_SCLK 52 // MEGA --> UNO 13 #define TFT_MOSI 51 // MEGA --> UNO 11 // *** alternative Schreibweise : Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST); // Adafruit_ST7735 tft = Adafruit_ST7735(10, 8, 52, 51, 9); // ************* Sensorik ********************** #include "DHT.h" // DHT-Bibliothek #include // BMP085-Bibliothek #define DHTPIN 7 #define DHTTYPE DHT22 //DHT11, DHT21, DHT22 DHT dht(DHTPIN, DHTTYPE); // Temp-Feuchte-Sensor Adafruit_BMP085 bmp; // Luftdruck-Sensor // ************** SD-Karte ********************* File dataFile; // diese Variable soll spaeter die Daten aufnehmen String dataString = ""; // fuer die Aufnahme sämtlicher Sensor-Daten #define SD_CS 53 // CS = Chip Select / beim UNO Pin 10 File logDat; // diese Variable 'logDat' im Format 'File' nimmt spaeter den Dateinamen und die Befehle auf int go = 0; // SD-Schreib-Freigabe float h; // Luftfeuchte DHT22 float t; // Temperatur DHT22 float t2; // Temperatur BMP085 float Druck; // Baro BMP085 static long md = 7; // default-Wert, der nie erreicht wird, um so mindestens einenDurchlauf zu gewaehrleisten static long tag; // *********************************************************************************************************** // *************************** S E T U P ********************************************************************* void setup() { Serial.begin(9600); // Serielle Ausgabe starten Wire.begin(); // Initialisiere I2C RTC.begin(); // Initialisiere RTC // lcd.begin(16, 2); // Start der Bibliothek 2x 16 // lcd.begin(20, 4); // Start der Bibliothek 4x 20 tft.initR(INITR_BLACKTAB); // initialize a ST7735S chip, black tab tft.fillScreen(ST7735_BLACK); Serial.println("Initialized"); // *** Kurzer Funktions-Test *** uint16_t time = millis(); tft.fillScreen(ST7735_BLACK); time = millis() - time; Serial.println(time, DEC); // delay(200); // large block of text // testdrawtext("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a tortor imperdiet posuere. ", ST7735_WHITE); // delay(3000); // ** erste Nennung den Bildschirm zu loeschen, schwarzer Hintergrund, rote Schrift (bis auf Widerruf) und text-Groesse tft.setTextWrap(false); tft.fillScreen(ST7735_BLACK); tft.setTextColor(ST7735_RED); tft.setTextSize(2); // (Werte von 1-4) tft.setRotation (45); // 90° gedrehte Darstellung // **** Pruefen ob RTC läuft ************* if (! RTC.isrunning()) { DateTime now = RTC.now(); DateTime compiled = DateTime(__DATE__, __TIME__); if (now.unixtime() < compiled.unixtime()) { Serial.println("RTC ist aelter als das Compile-Datum ! Updaten !"); RTC.adjust(DateTime(__DATE__, __TIME__)); // Hier wird die RTC auf die Zeit gesetzt, zu der dieser Sketch kompiliert wurde Serial.println("Starte Datum und Zeit"); // Anfangstext auf seriellem Monitor und LCD ausgeben } else Serial.println("Echtzeituhr laeuft bereits und ist aktuell."); } dht.begin(); // Start des Temp-Sensors // lcd.clear(); // Leert die Display-Zeilen // **** Pruefen ob BMP085 laeuft ************* Serial.begin(9600); if (!bmp.begin()) { Serial.println("Kein BMP085 gefunden ... I2C Adresse und Verkabelung pruefen !"); while (1) {} } // ******* SD-Karte einbinden und pruefen ************ while (!Serial) { // Hier wird gewartet bis der 'Serielle Monitor' gestartet wurde } Serial.print("Initialisiere SD-Card... "); // Zugriffs-Versuch auf die SD-Karte pinMode (53, OUTPUT); // fuer MEGA2560 // pinMode (10, OUTPUT); // fuer UNO R3 if (!SD.begin(SD_CS)) // Alternative: if (!SD.begin(4)) { Serial.println(" SD-Fehler !"); return; } Serial.println("SD-OK!"); Serial.println(); } // Setup // **** Wochentag ermitteln **** Das muss ausserhalb von SETUP oder LOOP passieren ! **** String get_day_of_week(uint8_t dow) { String dows = " "; switch (dow) { case 0: dows = "Sonntag"; break; case 1: dows = " Montag"; break; case 2: dows = "Diensta"; break; case 3: dows = "Mittwoc"; break; case 4: dows = "Donners"; break; case 5: dows = "Freitag"; break; case 6: dows = "Samstag"; break; } return dows; } void testdrawtext(char *text, uint16_t color) { tft.setCursor(0, 0); tft.setTextColor(color); tft.setTextWrap(false); tft.print(text); } // *********************************************************************************************************** // **************** L O O P ****************************************************************************** long currentTime = millis(); // http://www.vielsichtig.de/index.php?id=120 void loop() { // tft.setTextWrap(false); tft.fillScreen(ST7735_BLACK); // -------- Code-Erweiterung von Volker v.d.V. ------------------------ static long sav_sec; // letzte Sekunde merken DateTime now = RTC.now(); if (now.second() == sav_sec) { return; // Loop bis zur neuen Sekunde } else { sav_sec = now.second(); // aktuelle Sekunde merken // -------------------------------------------------------------------- Serial.print(now.year(), DEC); Serial.print('/'); Serial.print(now.month(), DEC); Serial.print('/'); Serial.print(now.day(), DEC); Serial.print(' '); Serial.print(now.hour(), DEC); Serial.print(':'); Serial.print(now.minute(), DEC); Serial.print(':'); Serial.print(now.second(), DEC); Serial.println(); /* // ******* Spielerei **** Serial.print(" since midnight 1/1/1970 = "); Serial.print(now.unixtime()); Serial.print("s = "); Serial.print(now.unixtime() / 86400L); Serial.println("d"); */ // ***** D a t u m ***** // ********************* Serial.print(" Das TagesDatum = "); Serial.print(now.day()); Serial.print(" Die variable tag ="); Serial.println(tag); if (now.day() == tag) // wenn das Tages-Datum gleich dem gemerkten TagesDatum ist, dann ... { // ... nichts machen } else // ... sonst hier weiter machen (also bei Datums-Wechsel) { // ***** Tag ***** if (now.day() < 10)Serial.print(0); // Wenn Wert kleiner 10, dann setzte noch eine '0' davor Serial.print("now.day= "); Serial.print(now.day(), DEC); Serial.print("."); tft.fillRect(1, 5, 140, 15, 0x0000); // (links,oben,rechts,unten, Farbe) male den Bereich für des Wochentages schwarz aus if (now.day(), DEC >= 10) // bei einstelligen Datum eine '0' vorausstellen { tft.setCursor(8, 10); tft.setTextColor(ST7735_RED); tft.setTextSize(2); tft.print(now.day()); tft.setCursor(18, 10); tft.println(" . "); // Position und Inhalt } else { tft.setCursor(13, 10); tft.setTextColor(ST7735_RED); tft.setTextSize(2); tft.print(now.day()); tft.setCursor(18, 10); tft.println(" . "); // Position und Inhalt } tag = now.day(); // die gemerkte Minute erhält den Wert der aktuellen Stunde // ***** Monat., Kurzform ***** Serial.print(now.month(), DEC); Serial.print("."); Serial.print(now.year(), DEC); tft.setCursor(48, 10); tft.setTextColor(ST7735_YELLOW); tft.setTextSize(2); tft.print(monthShortStr(now.month())); tft.setCursor(69, 10); tft.println(" . "); // Monat in klein und gelb // ***** Jahr ***** tft.setCursor(99, 10); tft.println(now.year()); // Jahreszahl in klein und gelb // ***** Wochentag *** // Serial.print("Wochentag = "); Serial.println(weekday()); // Wochentag der UNIX-Zeit von 1970 Serial.print(" "); Serial.print(get_day_of_week(now.dayOfTheWeek())); Serial.print(", "); tft.fillRect(8, 31, 49, 35, 0x0000); // (links,oben,rechts,unten, Farbe) male den Bereich für des Wochentages schwarz aus tft.setCursor(8, 33); tft.setTextColor(ST7735_GREEN); tft.setTextSize(1); tft.println(get_day_of_week(now.dayOfTheWeek())); // Tagesname in klein und gruen } // else tag <> now.day // ***** Stunde *** if (now.hour() < 10)Serial.print(0); // Wenn Wert kleiner 10, dann setzte noch eine '0' davor Serial.print(" Es ist "); Serial.print(now.hour(), DEC); Serial.print(":"); static long mh; if (now.hour() == mh) // wenn die aktuelle Minute gleich der gemerkten Minute ist, dann ... { // ... nichts machen } else // ... sonst hier weiter machen { if (now.hour() < 10) { tft.fillRect(60, 25, 79, 35, 0x0000); // male den Bereich für die Stunde schwarz aus tft.setCursor(60, 30); tft.setTextColor(ST7735_BLUE); tft.setTextSize(2); tft.print("0"); tft.setCursor(72, 30); tft.print(now.hour(), DEC); tft.setCursor(80, 30); tft.print(":"); // Stunde in klein und blau } else { tft.fillRect(60, 25, 79, 35, 0x0000); // male den Bereich für die Stunde schwarz aus tft.setCursor(60, 30); tft.setTextColor(ST7735_BLUE); tft.setTextSize(2); tft.print(now.hour(), DEC); tft.setCursor(80, 30); tft.print(":"); } mh = now.hour(); // die gemerkte Minute erhält den Wert der aktuellen Stunde } // *** Minute *** Serial.print(0); Serial.print(now.minute(), DEC); Serial.print(":"); // Wenn Wert kleiner 10, dann setzte noch eine '0' davor static long mm; if (now.minute() == mm) // wenn die aktuelle Minute gleich der gemerkten Minute ist, dann ... { // ... nichts machen } else // ... sonst hier weiter machen { if (now.minute() < 10) { tft.fillRect(90, 25, 109, 35, 0x0000); // male den Bereich für die Minute schwarz aus tft.setTextColor(ST7735_BLUE); tft.setTextSize(2); tft.setCursor(90, 30); tft.print("0"); tft.setCursor(102, 30); tft.print(now.minute(), DEC); tft.setCursor(110, 30); tft.print(":"); } else { tft.fillRect(90, 25, 109, 35, 0x0000); // male den Bereich für die Minute schwarz aus tft.setTextColor(ST7735_BLUE); tft.setTextSize(2); tft.setCursor(90, 30); tft.print(now.minute(), DEC); tft.setCursor(110, 30); tft.print(":"); } mm = now.minute(); // die gemerkte Minute erhält den Wert der aktuellen Minute } // *** Sekunde *** Serial.print(0); Serial.println(now.second(), DEC); tft.fillRect(120, 25, 140, 35, 0x0000); if (now.second() < 10) { tft.setTextColor(ST7735_BLUE); tft.setTextSize(2); tft.setCursor(120, 30); tft.print("0"); tft.setCursor(132, 30); tft.print(now.second(), DEC); // Wenn Wert kleiner 10, dann setzte noch eine '0' davor } else { tft.setTextColor(ST7735_BLUE); tft.setTextSize(2); tft.setCursor(120, 30); tft.print(now.second(), DEC); } Serial.println(); } // *********** Sensorik **** alle 30 Sekunden ****************** tft.setTextColor(ST7735_WHITE); tft.setCursor(5, 70); tft.setTextSize(1); tft.print("Thermo = "); tft.setCursor(5, 90); tft.print(" Hygro = "); tft.setCursor(5, 110); tft.print(" Baro = "); if (now.second() == 30 || now.second() == 0) { h = (dht.readHumidity() + 30); // Luftfeuchte auslesen, sowie mit einem Korrektur-Wert addieren t = dht.readTemperature(); // Temperatur auslesen if (isnan(t) || isnan(h)) // Pruefen ob eine gueltige Zahl zurueckgegeben wird. Wenn NaN (not a number) zurueckgegeben wird, dann Fehler ausgeben. { // Serial.println("DHT22 konnte nicht ausgelesen werden"); } else { Serial.print("Luftfeuchte = "); Serial.print(h); Serial.println(" %\t"); Serial.print("Temperatur DHT22 = "); Serial.print(t); Serial.println(" C"); static long mte; // Anzeige der Temperatur if (t == mte) // wenn die aktuelle Temperatur gleich der gemerkten Temperatur ist, dann ... { // ... nichts machen } else // ... sonst hier weiter machen { tft.fillRect(70, 60, 150, 70, 0x0000); // male den Bereich für die Temp schwarz aus tft.setTextSize(2); tft.setCursor(70, 65); tft.print(t); mte = t; // die gemerkte Temperatur erhält den Wert der aktuellen Temperatur } // else Temperatur static long mhu; // Anzeige der Luftfeuchte if (h == mhu) // wenn die aktuelle Feuchte gleich der gemerkten Feuchte ist, dann ... { // ... nichts machen } else // ... sonst hier weiter machen { tft.fillRect(70, 80, 150, 90, 0x0000); // male den Bereich für die Feuchte schwarz aus tft.setTextSize(2); tft.setCursor(70, 85); tft.print(h); mhu = h; // die gemerkte Feuchte erhält den Wert der aktuellen Feuchte } // else Luftfeuchte } // else 30-Sekunden-Interval // ****** Luftdruck ********************************* Serial.print("Temperatur BMP085 = "); t2 = bmp.readTemperature(); // Serial.print(bmp.readTemperature()); Serial.print(t2); Serial.println(" C"); Serial.print("durchschnittl. Temperatur aus beiden Werten = "); float t3 = (t2 + t) / 2; Serial.print(t3); Serial.println(" C"); Serial.print("Luftdruck = "); // Serial.print(bmp.readPressure()); // Serial.println(" Pa"); float d = bmp.readPressure(); Druck = (d + 2000) / 100; // Korrektur auf den hiesigen, aktuellen Wert Serial.print(Druck); Serial.println(" hPa"); static long mdr; // Anzeige des Luftdruck if (Druck == mdr) // wenn die aktuelle Minute gleich der gemerkten Minute ist, dann ... { // ... nichts machen } else // ... sonst hier weiter machen { tft.fillRect(70, 100, 150, 110, 0x0000); // male den Bereich für die Minute schwarz aus tft.setTextSize(2); tft.setCursor(70, 105); tft.print(Druck); mdr = Druck; // die gemerkte Minute erhält den Wert der aktuellen Minute } // Die kalkulierte Hoehe resultiert aus dem vorgegebenen Standard-Druck // von 1013.25 millibar = 101.325 Pascal. Die Korrektur erfolgt in Zeile 127. // Heiligenhaus liegt auf einer Hoehe von 170mtr (bzw. von 140 bis 190mtr). Serial.print("Hoehe = "); Serial.print(bmp.readAltitude()); Serial.println(" meter"); Serial.print("Luftdruck auf Seehoehe (errechnet) = "); Serial.print(bmp.readSealevelPressure()); Serial.println(" Pa"); // Man kann eine noch genauere Hoehen-Anzeige bekommen wenn man den aktuellen Luftdruck auf See-Höhe kennt, welche // mit dem Wetter variieren kann. Wenn es 1.015 millibar sind, dann ist es das selbe wie 101.500 Pascals. Serial.print("Tatsaechliche Hoehe = "); Serial.print(bmp.readAltitude(102240)); // Korrekturwert fuer Hlgh. in Klammern Serial.println(" meter"); // ****** Umrechnung auf Fahrenheit ******* /* float c = RTC.temperature() / 4.; float f = c * 9. / 5. + 32.; Serial << F(" ") << c << F(" C ") << f << F(" F"); */ } // Ende wenn Sekunde = 30, oder 59 else { // nichts tun } // wenn nicht 30 Sekunden, oder 59 Sekunden // ******* Aufbau des Daten-String ******** dataString = ""; dataString += (t); dataString += " C; "; dataString += (h); dataString += " %; "; dataString += (Druck); dataString += " hPa; "; dataString += (t2); dataString += " C;"; dataString += (" "); dataString += (now.day()); dataString += ("-"); dataString += (monthShortStr(now.month())); dataString += ("-"); dataString += (now.year()); dataString += (", "); dataString += (now.hour()); dataString += (":"); dataString += (now.minute()); dataString += (":"); dataString += (now.second()); Serial.println(dataString); // Serial.println(); // Serial.println(monthShortStr(now.month())); // Das hier ist der Monatsname als Kurz-Name, z.B. 'Feb' // *** FLAG zum Schreiben setzen ****** // *** ... und in einem Zeit-Rhytmus von 10 Minuten --> wenn m=09 or m=19 or m=29 or ... and s=00 // *** ... Da jedoch eine Messmoeglichkeit nicht immer genau um xy:19:59 zur Verfuegung stehen wird (Programmdurchlauf), oder gerade dann // der Wert 'nan' ist, muß auch da zudem noch solange der Schreib-Kanal geöffnet bleiben, bis es zu einer Speicherung gekommen // ist - und dann 'weiter wie geplant' = xy:29:59 ! if ((now.minute() == 9 && now.second() == 59) || (now.minute() == 19 && now.second() == 59) || (now.minute() == 29 && now.second() == 59) || (now.minute() == 39 && now.second() == 59) || (now.minute() == 49 && now.second() == 59) || (now.minute() == 59 && now.second() == 59)) { go = 1; // ... DANN go=1 --- Variable zur Freigabe des SPEICHER-Vorgangs auf die SD-Karte. Funktion ist geprueft ! } // if else { // nichts tun } // *** Speichern der Sensor-Daten ***** // ************************************ // *** Speicherung auf der SD-Karte *** // *** MOSI - pin 11 / 51 oder SPI Header // *** MISO - pin 12 / 50 oder SPI Header // *** CLK - pin 13 / 52 oder SPI Header // *** CS - pin 4 oder 10 beim UNO; 4 oder 53 beim MEGA, bzw. SS // *** hier werden die Kriterien ueberprueft, welche erfuellt sein muessen, um einen Schreib-Vorgang zu starten **** if (now.year() < 2016) { // *** 3. ... und wenn eine korrekt Zeit vorhanden ist Serial.println(); Serial.println(" RTC nicht vorhanden. "); // .. sonst nichts machen } else { // ... ansonsten weitermachen Serial.print(" RTC vorhanden - das akt. Jahr = "); Serial.println(now.year()); if (isnan(t) || isnan(h)) // *** 2. ... und keine Sensor-Werte fehlen --> wenn (hygro) oder (temp) = 'NAN' { Serial.println(" Keine Sensordaten "); // ... nichts machen } else { Serial.println(" Sensordaten komplett "); // ... ansonsten weitermachen if ( go < 1) { // *** 1. grundsaetzliche Speicherfreigabe gemaess Intervall-Vorgabe Serial.print(" Keine Freigabe ! go= "); Serial.println(go); // ... nichts machen } else { Serial.print("XY:"); Serial.print(minute()); Serial.print(":"); Serial.print(second()); Serial.print(" - go= "); Serial.print(go); Serial.print(" - year= "); Serial.print(now.year()); Serial.println(" - Sensordaten verfuegbar "); logDat = SD.open("senslog.txt", FILE_WRITE); // Oeffenen der Datei 'senslog.txt' zum nachfolgenden SCHREIBEN Serial.println("senslog.txt zum Schreiben oeffnen."); if (logDat) { logDat.println(dataString); // Beschreiben der soeben erstellten Datei logDat.close(); // Datei nach dem Schreiben wieder schliessen go = 0; // Intervall-Freigabe zuruecksetzen Serial.print("SD-Speicherung erfolgreich. "); Serial.print("Die Variable go wird zurueckgesetzt auf den Wert: "); Serial.println(go); } else { Serial.println("Fehler beim Oeffnen der Datei Senslog.txt !"); // Hinweis fuer den Fall, dass sich die Datei nicht mehr oeffnen laesst } // Ende Schreibfehler } // Ende GPS-Signal ? } // Ende else isnan } // Ende Speicher-Intervall } // LOOP