//====================================================================== //= Beschreibung = //====================================================================== /* Projekt: Temperaturlogger * * Ziele: * Sensoren: Dallas DS18B20 * RTC: Version 1.0 zunächst noch mit Softwareuhr * Datenspeicher: SD-Karte * IP: 65 * Konfiguration: über Konfigurationsdatei auf der SD-Karte * Einstellung Uhr: Nach einem Reset (Startwert aus Konfigurationsdatei) * Versorgung: Batterie (4 Stck. NimH.Akkus AA) * * SD-Karte an SPI-Bus: * MOSI - Pin 11 MasterOutSlaveIn * MISO - Pin 12 MasterInSlaveOut * CLK - Pin 13 Clock * CS - PIN 4 (beim Ethernet-Shield) * * Step 1: * Diverse Werte aus einer config-Datei auf der SD Karte auslesen. * Unter Verwendung der parseInt() Methode. * Siehe hierzu: * Kochbuch Seite 105, 113 * oder auch: http//:arduino.cc/en/Tutorial/ReadASCIIString * * Step 2: * Schreiben auf die SD-Karte in eine csv-Datei * Achtung: der Dateiname darf 8 Zeichen nicht übersteigen * siehe: http://arduino.cc/en/Reference/SD * "The library supports FAT16 and FAT32 file systems on standard * SD cards and SDHC cards. It uses short 8.3 names for files." * * Step 3: * Anbindung der Sensoren * - Test mit zwei Sensoren: erfolgreich * * Step 4: * Feinabstimmung * - Von der Zykluszeit die Programmlaufzeit abziehen (höhere Präzision) * - Messdauer in Sekunden einstellbar: erledigt * - Auflösung einstellbar: erledigt * - Test mit drei Sensoren: erfolgreich * * Step 5: * Optimierungen * - Tabellenkopf erzeugen (zur Identifikation der Sensoren): * erledigt * - Startverzögerungsoption einbauen: * erledigt * - F("") Option der aktuellen IDE verwenden um RAM zu sparen: * erledigt * - Umstellung auf sdfat wegen "file.rename" Möglichkeit: * * - file = SD.open("test.txt", O_CREAT | O_WRITE); testen: * * - Strombedarf reduzieren * * Step 6: * Umstellung auf Nano mit SD-Card Modul * - Aufbau auf Steckbrett: erledigt * - SD-Modul via SPI-Bus anbinden: erledigt * - Strombedarf aus Akkublock messen * - Schaltplan erstellen: * * Step 7: * Dokumentation * * * Step 8: * Anpassung an die neue IDE mind. ab 1.6.0 * u. A. neue Time.h Library * * Version: 13_Final * */ //====================================================================== //= Bibliotheken einbinden = //====================================================================== #include // mindestens ab IDE 1.6.0 erforderlich #include #include #include #include //====================================================================== // variables created by the build process when compiling the sketch = //====================================================================== extern int __bss_end; // notwendig für die Berechnung extern void *__brkval; // des Speicherverbrauchs (RAM) //====================================================================== //= diverse Konstanten = //====================================================================== #define ONE_WIRE_BUS 7 // Hier ist der Sensorbus angeschl. #define chipSelect 4 // beim Ethernet Shield CS = pin 4 #define hardwareSS 10 // hardware SS pin //====================================================================== //= diverse Deklarationen = //====================================================================== // für die SD-Karte File myFile; // Objekte vom Typ File erzeugen char zeichen; // das zuletzt gelesene Zeichen char dateiName[20]; // Dateiname der csv-Datei char buffer[20]; // Puffer zur int-Umwandlung // allgemeine Daten unsigned long zyklusZeit; // Messzyklus in Millisekunden unsigned long initUnixZeit; // Initialisierung der Uhr unsigned long verzoegMessung; // Verzögerung der Messung unsigned long startMessung; // Start der Messung unsigned long endeMessung; // Ende der Messung unsigned long dauerMessung; // Messdauer in Sekunden int genauigkeitMessung; // Auflösung der Messung long dauer; // Laufzeit von loop // für die Temperatursensoren OneWire oneWire(ONE_WIRE_BUS); // Objekte vom Typ oneWire erzeugen DallasTemperature sensors(&oneWire); // Anbindung an die Dallas-Lib DeviceAddress tempDeviceAddress; // Adressspeicher int numberOfDevices; // Anzahl gefundener Sensoren //====================================================================== //= Setup-Funktion = //====================================================================== void setup() { // einen Kanal zur seriellen Kommunikation öffnen------------------ Serial.begin(9600); // SD-Karte initialisieren----------------------------------------- Serial.print(F("Initialisierung der SD Karte...")); pinMode(hardwareSS, OUTPUT); // erfolgreich? if (!SD.begin(chipSelect)) { Serial.println(F(" fehlgeschlagen!")); return; } Serial.println(F(" erledigt.")); // Konfigurationsdatei zum lesen öffnen---------------------------- myFile = SD.open("config.txt"); if (myFile) { Serial.println(F("Einlesen der Startwerte:")); // Die Konfigurationswerte einlesen genauigkeitMessung = myFile.parseInt(); initUnixZeit = myFile.parseInt(); zyklusZeit = myFile.parseInt(); verzoegMessung = myFile.parseInt(); dauerMessung = myFile.parseInt(); // Datenkanal wieder schließen myFile.close(); // Zeit (UTC) laut config.txt setzen setTime(initUnixZeit); // Info zur Signalauflösung zum Terminal Serial.println(""); Serial.print(F("Messgenauigkeit = ")); Serial.print(genauigkeitMessung); Serial.println(" Bit"); // Info zur Zeitinitialisierung zum Terminal Serial.println(""); Serial.print(F("Startwert der Uhr = ")); Serial.print(initUnixZeit); Serial.println(F(" im Unixzeitformat")); // Info über den Messzyklus zum Terminal Serial.println(""); Serial.print(F("Messzyklus = ")); Serial.print(zyklusZeit); Serial.println(F(" Millisekunden")); // Info zur Verzögerung zum Terminal Serial.println(""); Serial.print(F("Startverzoegerung = ")); Serial.print(verzoegMessung); Serial.println(F(" Sekunden")); // Info zur Messdauer zum Terminal Serial.println(""); Serial.print(F("Messdauer = ")); Serial.print(dauerMessung); Serial.println(F(" Sekunden")); Serial.println(""); // Startzeit der Messung berechnen startMessung = initUnixZeit + verzoegMessung; // Zeit für Ende der Messung berechnen endeMessung = startMessung + dauerMessung; // den Namen der Ausgabedatei erzeugen erzeugeDateiNamen(); // Info zum Dateinamen zum Terminal Serial.print(F("Dateiname = ")); Serial.println(dateiName); Serial.println(""); // Wenn es die Datei schon gibt, dann löschen !!!!!!!!! // hier muss noch optimiert werden. Löschen ist nicht // wirklich gut. Datenverlust droht bei unbedachten RESETS !!! if (SD.exists(dateiName)) { SD.remove(dateiName); Serial.println(F("Altdatei geloescht.")); Serial.println(""); } } else { // Wenn das Öffnen der Konfigurationsdatei gescheitert ist Serial.println(F("Fehler beim Öffnen der Konfigurationsdatei!")); } // Temperatursensoren --------------------------------------------- // Die Dallas Temperature IC Control Library starten sensors.begin(); // Anzahl der Sensoren am Bus ermitteln numberOfDevices = sensors.getDeviceCount(); // Anzahl der Sensoren am Bus am Terminal ausgeben Serial.println(F("Sensorsuche...")); Serial.print(F("gefunden: ")); Serial.print(numberOfDevices, DEC); Serial.println(F(" Sensoren.")); // Spannungsversorgungsart am terminal anzeigen Serial.print(F("Parasitaere Versorgung ist: ")); if (sensors.isParasitePowerMode()) Serial.println(F("aktiv")); else Serial.println(F("inaktiv")); // Adressen aller Sensoren am Terminal anzeigen und in Tabellenkopf schreiben myFile = SD.open(dateiName, FILE_WRITE); // für den Tabellenkopf if (myFile) { // die Datendatei öffnen myFile.print(F("Datum")); // Spalte 1 myFile.print(";"); // Spaltentrenner myFile.print(F("Uhrzeit")); // Spalte 2 for (int i = 0; i < numberOfDevices; i++) { // Sensoradressen durchl. // Adresssuche und Ausgabe zum Terminal und in die Datei if (sensors.getAddress(tempDeviceAddress, i)) { Serial.print(F("\nSensor ")); Serial.print(i, DEC); Serial.print(F(" mit der Adresse: ")); printAddress(tempDeviceAddress); // Terminal und csv-Datei Serial.println(); // Die Messwertauflösung auf genauigkeitMessung bit setzen // Bei jedem Dallas/Maxim Sensor kann, unabhängig von einander, // die Auflösung eingestellt werden. // Bei diesem Projekt werden alle auf die gleiche Auflösung // parametriert. sensors.setResolution(tempDeviceAddress, genauigkeitMessung); // Genauigkeit aller Sensoren am terminal anzeigen Serial.print(F("Die Aktuelle Genauigkeit ist eingestellt auf: ")); Serial.print(sensors.getResolution(tempDeviceAddress), DEC); Serial.println(); } else { Serial.print(F("Geisterbauteil gefunden bei ")); Serial.print(i, DEC); Serial.print(F(" die Adresse kann nicht ermittelt werden. Aufbau kontrollieren!")); } } myFile.println(""); // Zeilenvorschub für den Tabellenkopf // die csv-Datei wieder schließen myFile.close(); } else { // Wenn das Öffnen der csv-Datei gescheitert ist Serial.println(F("Fehler beim schreiben in die CSV-Datei!")); } Serial.println(F("===================================================")); Serial.println(""); }// Ende: Setup-Funktion //====================================================================== //= diverse Funktionen = //====================================================================== void erzeugeDateiNamen() { // den Namen der Ausgabedatei erzeugen // ACHTUNG: Namenslänge der Datei maximal 8.3 !!! strcpy(dateiName, ""); // alten Namen löschen itoa(year(), buffer, 10); // Jahr strcat(dateiName, buffer); itoa(month(), buffer, 10); // Monat strcat(dateiName, buffer); itoa(day(), buffer, 10); // Tag strcat(dateiName, buffer); strcat(dateiName, ".csv"); // .csv }// Ende: erzeugeDateiNamen void printDigits(int digits) { // Zusatzfunktion zur Uhrzeitanzeige // Doppelpunkt und führende 0 ausgeben Serial.print(":"); if (digits < 10) Serial.print('0'); Serial.print(digits); }// Ende: printDigits void printDigitsOnSD(int digits) { // Zusatzfunktion zur Uhrzeitausgabe in die csv-datei // Doppelpunkt und führende 0 ausgeben myFile.print(":"); if (digits < 10) myFile.print('0'); myFile.print(digits); }// Ende: printDigitsOnSD void printAddress(DeviceAddress deviceAddress) { // Adresse eines Temperatursensors ausgeben (Terminal und Datei) myFile.print(";"); // Spaltentrenner for (uint8_t i = 0; i < 8; i++) { if (deviceAddress[i] < 16) Serial.print("0"); Serial.print(deviceAddress[i], HEX); // Terminal myFile.print(deviceAddress[i], HEX); // Tabellenkopf } }// Ende: printAddress int memoryFree() { // function to return the amount of free RAM int freeValue; if ((int)__brkval == 0) freeValue = ((int)&freeValue) - ((int)&__bss_end); else freeValue = ((int)&freeValue) - ((int)__brkval); return freeValue; }// Ende: memoryFree //====================================================================== //= Loop-Funktion = //====================================================================== void loop() { if (now() >= startMessung) { // bei Freigabe der Messung // Datenausgabe in die csv-Datei auf der SD-Karte------------------ // die csv-Datei zur Datenaufnahme öffnen und beschreiben myFile = SD.open(dateiName, FILE_WRITE); // wenn erfolgreich, dann Start der Ausgabe------------------------ if (myFile) { myFile.print(day()); // Tag myFile.print("."); myFile.print(month()); // Monat myFile.print("."); myFile.print(year()); // Jahr myFile.print(";"); // neue Spalte---------------------- myFile.print(hour()); // Stunde printDigitsOnSD(minute());// Minute printDigitsOnSD(second());// Sekunde // jetzt kommen endlich die Daten // Aufruf von: sensors.requestTemperatures() um eine Antwort von // allen Geräten am Bus zu erhalten. // Hierdurch werden die Temperaturen bereitgestellt. sensors.requestTemperatures(); // Alle Geräte durchlaufen und Messwerte ausgeben for (int i = 0; i < numberOfDevices; i++) { // den Bus nach Adressen absuchen if (sensors.getAddress(tempDeviceAddress, i)) { // Die Antwort kommt fast unmittelbar. Sofortige Datenausgabe float tempC = sensors.getTempC(tempDeviceAddress); myFile.print(";"); // neue Spalte-------------- myFile.print(tempC); // Messwert vom angesprochenen Sensor } //else ghost device! Check your power requirements and cabling } // Ende von for myFile.println(""); // Zeilenvorschub am Ende der Tabellenzeile // die csv-Datei wieder schließen myFile.close(); } // Ende von if myFile else { // Wenn das Öffnen der csv-Datei scheitert Serial.println(F("Fehler beim schreiben in die CSV-Datei!")); } // Ende von else } // Ende von if freigabe // Messzeitkorrektur und Terminaltestausgaben // Zykluszeit des Programms bestimmen dauer = millis() - dauer; // mit korrigiertem Wert auf den nächsten Messzyklus warten delay(zyklusZeit - dauer); // neuen Zählerstand der Millisekunden merken dauer = millis(); // Kontrolle ob das Messzeitende erreicht ist while (now() > endeMessung) { } // am Ende der Messung, ab in die Dauerschleife }// Ende: Loop-Funktion //====================================================================== //= Ende = //======================================================================