Temperaturen mit Uhrzeit auf SD-Karte logen

Hallo Forum,

ich bin hier neu angemeldet aber schon länger am "heimlich" mitlesen.
Seit ein paar Wochen beschäftige ich mich mit dem Arduino.
Meine Kenntnisse rund um das Thema Programmieren gehen gegen null. Aber das soll sich ändern.

Jetzt zu meinem Vorhaben:
Ich möchte im ersten Schritt die Temperaturen von 5 DS18B20 Sensoren auslesen und mit der Uhrzeit auf eine SD-Karte logen.
Neben dem Arduino UNO R3 und den 5 DS18B20 Sensoren verwende ich von Grove das Base Shield V2, das SD-Karten Shield V4 und die Echtzeituhr RTC.

Bisher habe ich erfolgreich den Sensoren Feste Adressen gegeben, die Uhr eingebaut, und die Temperaturen mit der Uhrzeit auf dem Seriellen Monitor ausgegeben. Auch die SD-Karte hat er erkannt.

Ich bekomm aber die Werte nicht auf die SD-Karte.

Den Sketch hab ich aus verschiedenen Teilen aus dem WWW zusammengestellt und angepasst.

Könnt ihr mir helfen das der Uno die Daten auf die SD-Karte schreibt?

Gruß Sascha

// https://www.reichelt.de/magazin/how-to/temperatur-log-arduino-uno-fortsetzung/

#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <DS1307.h>

#define ONE_WIRE_BUS 2

DS1307 clock;

unsigned long WRITE_INTERVAL = 60000; // 1 Minuten

unsigned long lastWrite = WRITE_INTERVAL;


OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);      //Übergabe der OnewWire Referenz zum kommunizieren mit dem Sensor.

int sensorCount;

DeviceAddress Puffer_1 = { 0x28, 0xCE, 0x74, 0x54, 0x0B, 0x00, 0x00, 0xF7 };
DeviceAddress Puffer_2 = { 0x28, 0xE1, 0x39, 0x55, 0x0B, 0x00, 0x00, 0xEF };
DeviceAddress Puffer_3 = { 0x28, 0x36, 0xE5, 0x55, 0x0B, 0x00, 0x00, 0xD1 };
DeviceAddress Puffer_4 = { 0x28, 0xE2, 0x21, 0x55, 0x0B, 0x00, 0x00, 0xF4 };
DeviceAddress Puffer_5 = { 0x28, 0x89, 0xA7, 0x54, 0x0B, 0x00, 0x00, 0x17 };


void setup()
{
  Serial.begin(9600);
  
   clock.begin();                         // Die Verbindung zur Uhr aufbauen
   
//   clock.fillByYMD(2019,11, 05);          // Die Zeit einstellen
//   clock.fillByHMS(20, 19, 30);
//   clock.fillDayOfWeek(TUE);
//   clock.setTime();

    sensors.begin();
    sensors.setResolution(Puffer_1, 9);   //9 - 12 steht für Auflösung
    sensors.setResolution(Puffer_2, 9);   //  9 = 0,5
    sensors.setResolution(Puffer_3, 9);   // 10 = 0,25
    sensors.setResolution(Puffer_4, 9);   // 11 = 0,125
    sensors.setResolution(Puffer_5, 9);   // 12 = 0,05

 int selectedChip = 4;                    // SD Karte initialisieren
 if (!SD.begin(selectedChip)) {
   Serial.println("SD-Karte fehlt");
    } else {
      Serial.println("SD-Card initialisiert.");  
 }
 Serial.print("Die Temperaturen des Pufferspeichers sind: \n\r");
}

void printTime(){
  clock.getTime();                        // Zeit vom Chip abfragen
    Serial.print(clock.hour, DEC);
    Serial.print(":");
    Serial.print(clock.minute, DEC);
    Serial.print(":");
    Serial.print(clock.second, DEC);
    Serial.print(" | ");
    Serial.print(clock.dayOfMonth, DEC);
    Serial.print(".");
    Serial.print(clock.month, DEC);
    Serial.print(".");
    Serial.print(clock.year, DEC);
    Serial.println();

}

void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == -127.00) {
    Serial.print("Fehler mit Sensor");
  } else {
    Serial.print(tempC);
    Serial.print(" °C  || ");    
    printTime();
    }
}

void writeToSD(String line) {
 File dataFile = SD.open("datalog.csv", FILE_WRITE);
 if (dataFile) {
   dataFile.println(line); // Auf die SD-Karte schreiben
   dataFile.close();
   Serial.println(line); // Zusätzlich auf serielle Schnittstelle schreiben zum Debuggen
 }
 else {
   Serial.println("Fehler beim Öffnen der Datei");
 }
}


void loop()
{ 
  sensors.requestTemperatures();
  
  Serial.print("Temperatur Puffer 1 ist: ");
  printTemperature(Puffer_1);
  Serial.print("\n\r");
  Serial.print("Temperatur Puffer 2 ist: ");
  printTemperature(Puffer_2);
  Serial.print("\n\r");
  Serial.print("Temperatur Puffer 3 ist: ");
  printTemperature(Puffer_3);
  Serial.print("\n\r");
  Serial.print("Temperatur Puffer 4 ist: ");
  printTemperature(Puffer_4);
  Serial.print("\n\r");
  Serial.print("Temperatur Puffer 5 ist: ");
  printTemperature(Puffer_5);
  Serial.print("\n\r\n\r");

delay(5000);
 
}

Hast Du mal nur das SD-Shield gesteckt und das Beispielsketch für SD getestet?

Gruß Tommy

Grüß dich Tommy.

Nein, natürlich noch nicht. Ich werd mal den den original Sketch von Reichelt testen.
Einen DHT 22 hab ich noch.
Ich bekomm aber beim kompilieren schon ein Fehler angezeigt. Hab schon verschieden Dinge probiert.

Wie gesagt ich werd erst mal den original Sketch testen und mich melden.

Gruß

Kompilerfehler solltest Du nicht geheim halten :wink:

Gruß Tommy

Hi

Hast Du keine Eigenen? :wink:

Dann: Wenn's 'trotzdem läuft' wird's nur eine Warnung gewesen sein.
Weiter werden die Meldungen ausführlicher, wenn man Diese in den Einstellungen anklickt - wird aber ebenfalls als Warnung ausgegeben, daß man Da doch noch mehr sehen könnte, wenn man denn wöllte.

MfG

postmaster-ino:
Hast Du keine Eigenen? :wink:

Evtl. zu wenige? :wink:

Gruß Tommy

Danke für die Tipps.
Heut und morgen werd ich nicht mehr dazu kommen. Melde mich zum Wochenende wieder.

MfG

Hallo,

schau Dir das Beispiel Datalogger mal genau an. Da werden 3 Analogwerte in einer Zeile auf die SD geschrieben. Also fast das was Du willst.

Du hast eine function verwendet "writeToSD(parameter)" diese erwartet einen Parameter in der runden Klammer. Der Parameter muss von Typ String sein. Dieser String besteht aus einer Zeile mit den Zeichen die Du auf die SD Karte schreiben willst. Und dann musst Du die Function auch noch aufrufen damit sie auch bearbeitet wird. Vermutlich war das auch die Fehlermeldung. :slight_smile:

Hilfe zu Strings gibt's unter Hilfe Reference. Besser wäre es noch mit einer C-Zeichenketten zu arbeiten. Dazu ein link

Heinz

derby_grillt:
ich bin hier neu angemeldet aber schon länger am "heimlich" mitlesen.

Ach, Du warst das!

derby_grillt:
Meine Kenntnisse rund um das Thema Programmieren gehen gegen null. Aber das soll sich ändern.

Dann solltest Du Dir vom Start weg angewöhnen, Deinen Code so zu gestalten, dass er leicht verdaulich ist. Wobei ich „gestalten“ durchaus wörtlich meine: Einheitliche Einrückungen/Abstände/Klammerung usw. Was mir zu „hübschem“ Code eingefallen ist, habe ich hier ins Netz gekippt.

derby_grillt:
... Ich bekomm aber die Werte nicht auf die SD-Karte. ...

Mir ist auf die Schnelle nur aufgefallen, dass es wohl keine Zeile im Code gibt, in der eine Instanz „SD“ erzeugt wird. Wird das in SD.h erledigt?

Welche Fehlermeldung oder Warnung bekommst Du denn?

Gruß

Gregor

Tommy56:
Hast Du mal nur das SD-Shield gesteckt und das Beispielsketch für SD getestet?

Gruß Tommy

Hallo Tommy,

Ich habe jetzt das Beispiel SD-Card CardInfo und Datalogger getestet.
CardInfo erkennt die SD-Card und gibt mir die Infos wie Größe, Formatierung usw. zurück.
Datalogger schreibt auf die SD-Karte in eine .txt Datei dien ich dann am PC öffnen kann.

Kompilerfehler setzt er keinen mehr. Kann sein das ich mir irgendwo was hingeschrieben hatte es aber nicht gespeichert habe.

Gruß Sascha

Rentner:
Hallo,

schau Dir das Beispiel Datalogger mal genau an. Da werden 3 Analogwerte in einer Zeile auf die SD geschrieben. Also fast das was Du willst.

Du hast eine function verwendet "writeToSD(parameter)" diese erwartet einen Parameter in der runden Klammer. Der Parameter muss von Typ String sein. Dieser String besteht aus einer Zeile mit den Zeichen die Du auf die SD Karte schreiben willst. Und dann musst Du die Function auch noch aufrufen damit sie auch bearbeitet wird. Vermutlich war das auch die Fehlermeldung. :slight_smile:

Hilfe zu Strings gibt's unter Hilfe Reference. Besser wäre es noch mit einer C-Zeichenketten zu arbeiten. Dazu ein link

Heinz

Hallo Heinz,

ich denke bei den String liegt mein Fehler. Da werde ich zumindest mal ansetzen. Bei dem Thema Strings muss ich mich aber erst rein arbeiten. Danke für den Link.

Gruß Sascha

Hi

Uno und String ist keine tolle Kombination.
Bei jeder Veränderung am String (mit großem S) wird für den neuen String ein neuer Speicherplatz gesucht und beansprucht.
Packst Du 10x ein einzelnes Leerzeichen dran, wird 10x eine neue Lücke im Speicher gesucht, Die mindestens diese Größe hat (ok, Hörensagen, aber immerhin).
Deine 5 Sensoren können je maximal drei Stellen vor dem Komma haben, ein Komma und drei Stellen hinter dem Komma.
Macht 7 Stellen pro Sensor, ohne Trennzeichen.
Mit Trennzeichen 5x7+4 = 39 Zeichen.
Wenn Du nun ein char-Array mit 40 Zeichen Platz erstellst, bist Du für Alles, was die Sensoren so anzeigen können, gewappnet - das 40.te Zeichen ist /0 und schließt den char-string (mit kleinem s) ab.
Den kannst Du ebenfalls auf die SD-Karte schieben.

MfG

Hy postmaster-ino-,

danke für den Tipp.
Wenn ich das Datum sowie die Uhrzeit mit dabei haben möchte und die Möglichkeit für jeden Sensor ein Vorzeichen zu nutzen komme ich auf 62 Zeichen.
Also heißt es jetzt Strings (mit S und s) und Arrays pauken.

Bis später.

Achso, mit folgendem Code schreibt der Uno alle 6 Sekunden das Datum und die Uhrzeit getrennt durch ein | auf die Karte. Die kann ich mir dann in Excel anzeigen lassen und bearbeiten.

@gregorss: Ja das war wohl ich. ;D
Ich habe versucht den Code gleich etwas auf zu räumen.

#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <DS1307.h>

#define ONE_WIRE_BUS 2


DS1307 clock;


unsigned long WRITE_INTERVAL = 6000;         // 6 Sekunden
unsigned long lastWrite = WRITE_INTERVAL;


OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);          //Übergabe der OnewWire Referenz zum kommunizieren mit dem Sensor.


int sensorCount;                              // Sensoranzahl ???


DeviceAddress Puffer_1 = { 0x28, 0xCE, 0x74, 0x54, 0x0B, 0x00, 0x00, 0xF7 };
DeviceAddress Puffer_2 = { 0x28, 0xE1, 0x39, 0x55, 0x0B, 0x00, 0x00, 0xEF };
DeviceAddress Puffer_3 = { 0x28, 0x36, 0xE5, 0x55, 0x0B, 0x00, 0x00, 0xD1 };
DeviceAddress Puffer_4 = { 0x28, 0xE2, 0x21, 0x55, 0x0B, 0x00, 0x00, 0xF4 };
DeviceAddress Puffer_5 = { 0x28, 0x89, 0xA7, 0x54, 0x0B, 0x00, 0x00, 0x17 };


void setup()
  {
    Serial.begin(9600);
    
    clock.begin();                            // Die Verbindung zur Uhr aufbauen
     
  //   clock.fillByYMD(2019,11, 05);          // Nur zum Stellen der Echtzeituhr
  //   clock.fillByHMS(20, 19, 30);
  //   clock.fillDayOfWeek(TUE);
  //   clock.setTime();
  
    sensors.begin();
    sensors.setResolution(Puffer_1, 9);       //  9 - 12 steht für Auflösung
    sensors.setResolution(Puffer_2, 9);       //  9 = 0,5
    sensors.setResolution(Puffer_3, 9);       // 10 = 0,25
    sensors.setResolution(Puffer_4, 9);       // 11 = 0,125
    sensors.setResolution(Puffer_5, 9);       // 12 = 0,05
  
    int selectedChip = 4;                     // SD Karte initialisieren
      if (!SD.begin(selectedChip))
        {
         Serial.println("SD-Karte fehlt");
        }
      else
        {
         Serial.println("SD-Card initialisiert.");  
        }
         Serial.print("Die Temperaturen des Pufferspeichers sind: \n\r");
  }


void writeToSD(String line)
  {
    File dataFile = SD.open("datalog.csv", FILE_WRITE);
    if (dataFile)
      {
       dataFile.println(line); // Auf die SD-Karte schreiben
       dataFile.close();
       Serial.println(line); // Zusätzlich auf serielle Schnittstelle schreiben zum Debuggen
      }
    else
      {
       Serial.println("Fehler beim Öffnen der Datei");
      }
  }


void loop()
  {
    if (millis() - lastWrite > WRITE_INTERVAL)
      {
       String line = String(getTime()); // + "|" + String (Puffer_1);
       writeToSD(line);
       lastWrite = millis();
      }
  }   
      
String getTime()
  {
   clock.getTime(); // Zeit vom Chip abfragen
   String t = String(clock.dayOfMonth, DEC);
   t += String(".");
   t += String(clock.month);
   t += String(".");
   t += String(clock.year);
   t += String(" | ");
   t += String(clock.hour);
   t += ":";
   t += String(clock.minute);
   t += ":";
   t += String(clock.second);
   return t;
  }

@postmaster-ino,

ich komm nicht weiter. Das mit dem Array hab ich ja soweit verstanden, aber ich hab keinen Plan wie ich die Sensorwerte darin einbau, bzw. sie auf die SD-Karte bekomme.

Gruß Sascha

Schau mal, ob Dir das weiter hilft.

Gruß Tommy

Hi

Selber habe ich eine eigene Funktion, Die mir die Werte auf die SD-Karte schreibt.
Dafür ist auf der SD-Karte ein File zum Schreiben geöffnet/erstellt.

void savesensorwerte(void) {
  static byte lastwrite = 0;
  //prüft die Uhrzeit, zur vollen Minute werden alle Sensor-Werte auf die SD-Karte gespeichert


  DateTime now = rtc.now();
  if (now.minute() != lastwrite) {
    //zur 'neuen' Minute Daten speichern
    lastwrite = now.minute();
    if (now.day()<10) logFile.print('0');
    logFile.print(now.day(), DEC);
    logFile.print('/');
    if (now.month()<10) logFile.print('0');
    logFile.print(now.month(), DEC);
    logFile.print('/');
    logFile.print(now.year(), DEC);
    logFile.print(", ");
    logFile.print(now.hour(), DEC);
    logFile.print(':');
    if (now.minute()<10) logFile.print('0');
    logFile.print(now.minute(), DEC);
//    logFile.print(':');
//    logFile.print(now.second(), DEC);
    for (byte sensornummer=0;sensornummer<sensoranzahl;sensornummer++){
      logFile.print (", ");
      logFile.print(tempsensor[sensornummer].raw.rawword/16.0,2);
    }

    logFile.println();
    logFile.flush();
    Serial.print(F("Logfile:"));
    if (now.day()<10) Serial.print('0');    
    Serial.print(now.day(), DEC);
    Serial.print('/');
    if (now.month()<10) Serial.print('0');    
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.year(), DEC);
    Serial.print(F(", "));
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    if (now.minute()<10) Serial.print('0');    
    Serial.print(now.minute(), DEC);
    for (byte sensornummer=0;sensornummer<sensoranzahl;sensornummer++){
      Serial.print (F(", "));
      Serial.print(tempsensor[sensornummer].raw.rawword/16.0,2);
    }
    Serial.println();
  }
}

Das ergibt dann

15:35:14.061 -> Logfile:10/11/2019, 15:38, 0.00, 26.44, 25.00, 26.00, 5.94, 9.63, 35.81, 33.13, 35.13, 30.44, 35.19, 35.63, 43.94, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 57.00, 0.00, 0.00, 14.63, 0.00

Meine Sensorwerte stehen in einem Array aus struct's, Die ich in der FOR-Schleife auslese und zur SD-karte schicke (incl. trennendem Komma - gibt ein .CSV File - Komma separierte Variablen, kann Exel direkt importieren - bei OpenOffice habe ich hier noch meine liebe Not, daß die .-Werte als Kommazahlen interpretiert werden ... denke ich - andere Geschichte!)

//Sensoren
typedef struct {
  byte sensornummer;          //Nummer des Sensor (externe Nummer)
  union {
    int16_t rawword;          //raw-Wert
    byte rawbyte[2];          //raw-Bytes
  } raw;
} Tsensor;
Tsensor tempsensor[] = {
  {0x00,   0},   //Dummy - hier landen alle Sensoren, Die keine externe ID haben - sollte Keiner sein
  {0x01,   0},    //TH-Modul 1
  {0x02,   0},    //TH-Modul 2
  {0x03,   0},    //TH-Modul 3
  {0x04,   0},    //Außentemperatur Thermische Solar T4
  {0xFF,   0}, //TO92-Gehäuse, wurde 0xFF, werden von BEIDEN Sensor-Knoten hier gespeichert
  {0x06,   0},      //vor WT Außen
  {0x07,   0},      //hinter WT Außen
  {0x08,   0},      //hinter Schieber WT, vor WP KL/Speicher
  {0x09,   0},      //hinter WP KL/Speicher Trinkwasser
  {0x10,   0},     //vor WP GR
  {0x11,   0},     //hinter WP GR/vor TH
  {0x12,   0},     //hinter WP KL, vor Speicher Trinkwasser
  {0x13,   0},     //vor WT INNEN-Seite
  {0x14,   0},     //Dummy hinter WT INNEN-Seite, vor Puffer
  {0x15,   0},     //Dummy Puffer oben - Entnahme Entladen
  {0x16,   0},     //Dummy Puffer Mitte oben
  {0x17,   0},     //Dummy Puffer Mitte unten
  {0x18,   0},     //Dummy Puffer unten - Entnahme Laden
  {0x19,   0},     //Dummy hinter Puffer / vor Öl-Kessel
  {0x20,   0},     //Dummy hinter Öl-Kessel vor Heizkörper
  {0x21,   0},     //Dummy hinter Heizkörper
  {0x22,   0},     //Dummy Entnahme HW Trinkwasser (Heiß-Wasser)
  {0x23,   0},     //Dummy Temperatur Brauchwasser (Thermo-Mischer, nix Strom)
  {0x24,   0},     //Dummy Speicherfühler
  {0x25,   0},     //Dummy hinter WT Innen
  {0x26,   0},     //Speicher RL-Fühler/Heizwasser Austritt - bei stehendem Heizwasser obere Speicher-Temperatur
  {0x46,   0},     //unbekannt :) taucht auf, kA, wo der Sensor sitzt
  {0x05,   0},     //sollten wir nicht haben
};
const byte sensoranzahl = sizeof(tempsensor) / sizeof(tempsensor[0]);

Du musst 'nur' die Werte, Die Du auf die SD-Karte bannen willst, per .print zum geöffneten File schicken.
Das .flush() sorgt dafür, daß alle offenen Daten jetzt gespeichert werden - andernfalls könnte Es sein, daß der Puffer noch nicht voll ist und die Daten eben erst gespeichert werden, wenn Dieser gefüllt ist (SD-Karten-Lib) - und bei Reset oder Kartenentnahme fehlen Diese dann halt.

Das ist vll. eine frühe Beta, wobei ich hier nur die Daten, Die im CAN-Bus rumspuken, auf die SD-Karte pinne.
Die eigentlichen Empfänger der Daten sind bisher noch still - Das will geändert werden, da ich akut noch in den Keller renne, um zu sehen, ob sich Da 'was tut' ... so viel zum Thema 'Alles vernetzt' :wink:

MfG

PS: Durch das F-Macro ließe sich hier und da noch etwas Speicher sparen (sinnvoll erst ab zwei Zeichen 'am Stück', getestet)

Hier mal eine Variante, wie Du die Werte ins Array bekommst.
Da ich Deine Hardware nicht habe, habe ich mir mit einem Float-Array als Eingang beholfen. Es soll Dir ja auch nur einen möglichen Weg zeigen.

const byte werteAnzahl = 5;
char arr[19+1+werteAnzahl*9] = {0};
float farr[werteAnzahl] = {123.456,234.888,-555.666,456.789,-345.678};
const char datum[] = "09.11.2019 15:45:00";
byte wertIdx = 0;

void setTmst(const char *datum) {
  strcpy(arr,datum);
  wertIdx = 0;  // Zähler zurücksetzen
}

void addWert(float wert) {
  if (wertIdx >= werteAnzahl) return;  // Wenn zu viele Werte: nichts tun
  byte pos = 20 + wertIdx * 9;
  arr[pos-1] = '|';  // vorherigen Wert mit '|' anstelle von '\0' abschließen
  dtostrf(wert,8,3,arr+pos); 
  wertIdx++;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Start");
  setTmst(datum);
  Serial.println(arr);
  for(byte i=0; i< werteAnzahl;i++) {
    addWert(farr[i]);
    Serial.print(arr);
  }
}

void loop() { }

Gruß Tommy

Hallo Tommy,

ich gebe mal wieder, wie ich dene das die Codeteile funktionieren:

const Byte werteAnzahl =  5;

= steht für die 5 Sensoren die ich verwende.

char arr[19+1+werteAnzah*9] = {0};

= 19 kommen durch Datum und Uhrzeit, +1 Platzhalter / Leerzeichen,
*9 = Zeichen je Sensorwert? Vorzeichen + 3 Stellen + . + 3 Stellen + , ?

mit float farr[werteAnzahl] und const char Datum[] gibt's du die Werte vor.

An der Stelle muss ich dann meine Werte von den Sensoren bzw. meine Uhrzeit und Datum von der RTC packen. Richtig?

Gruß Sascha

Richtig

Gruß Tommy