//======================================================================
//= 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 <SPI.h> // mindestens ab IDE 1.6.0 erforderlich
#include <SD.h>
#include <Time.h>
#include <OneWire.h>
#include <DallasTemperature.h>
//======================================================================
// 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 =
//======================================================================