SD.open(variable_x, modus), variable_x dynamisch füllen

Guten Tag zusammen,

hab jetzt schon jede idee durch, die mir eingefallen ist und die ich ergoogelt habe, jedoch komme ich auf keinen grünen Zweig.

Evtl könnt ihr mir erklären warum und einen Lösungsvorschlag bieten.
Kenntnisse in Sachen Arduino: Anfänger mit Grundkenntnissen

Fall:

Ich versuche mir einen Datenlogger für 2 - n Tempertaturwerte aufzubauen. Ausgabe über LCD und Aufzeichnung auf SD-Karte mit Timestamp von einem TinyRTC.

LCD Angeschlossen und Funktionstüchtig? - Check!
TinyRTC Angeschlossen und Funktionstüchtig? - Check!
SD-Modul Angeschlossen und Funktionstüchtig? - Check!
Differenzverstärker für PT100 Aufgebaut, Angeschlossen und Funktionstüchtig? - Check!

Was heißt: Ich kann Daten ans Display senden und bekomme sie auch korrekt Angezeigt.
Kann Datum und Uhrzeit auslesen und zb. ans Display senden.
Kann ebenso auch schon zb. den ausgelesen Temperaturwert zb ans Display senden und in eine Datei schreiben.

Nun könnte man zurecht Fragen: Was willste dann hier? Läuft doch alles?

Nicht ganz: Ich möchte das Programm gerne so Aufbauen, dass man alle wichtigen Parameter, die man später auch gerne mal ändern möchte, Global (also über void setup() ) einstellbar machen.
Ebenso soll der Dateiname im späteren Betrieb mit dem aktuellen Datum versehen werden.
Z.B.:

datalog_25.08.2013.txt

Demnach geht es in diesem Konkreten um den Befehlt "SD.open".

Es gibt noch an anderer Stelle Probleme, doch in diesem Thread würde ich mich gerne auf diese Baustelle konzentrieren.

Der Befehl verlangt laut "SD.h" als erstes ein char und anschließend eine Angabe über den Modus.

SD.open(char *filename, uint8_t mode = FILE_READ)

Da ich anscheinend das mit den Variablentypen und "Sternchen" noch nicht so ganz verstanden habe stehe ich nun vor einem Problem.
Ich fange mal klein an:

Schreibe ich den Code so:

#include <Wire.h>
#include "RTClib.h"
#include <LiquidCrystal.h>
#include <SD.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;


const int chipSelect = 10;

File dataFile;


// Globale Einstellungen

 char logfile[] = "datalog.txt";




RTC_DS1307 rtc;
LiquidCrystal lcd (9, 8, 5, 4, 3, 2);


////Variablen für Temperaturerfassung anlegen
int temp_pin = A0;            //Analogen Eingangs-PIN mappen
int temp_raw = 0;             //Variable für den rohen Eingangswert (0-1023)
int temp_value = 0;           //Variable für den umgerechneten Rohdatenwert in °C



// #################################
// #################################



void setup () 
{

  //Librarys initialisieren
  Serial.begin(9600);
  Wire.begin();
  rtc.begin();
  lcd.begin(20,4);          //LCD Zeichen und Zeilen einstellen

  
 
  //Pinmodes Definieren

  pinMode(10, OUTPUT);        //'ChipSelect' PIN für die SD-Karte
  pinMode(temp_pin, INPUT);    //Analoger Eingangspin für den Differenzverstärker


  //SD-Karte initialisierung und Debugging-infos

  Serial.println("Initializing SD card...");
  lcd.setCursor(0,0);
  lcd.print("Init SD card");
  delay(500);

  


//Bereitschaft der SD-Karte prüfen
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");

    lcd.setCursor(0,1);
    lcd.print("Karte defekt oder");
    lcd.setCursor(0,2);
    lcd.print("nicht vorhanden");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");
  lcd.setCursor(0,1);
  lcd.print("Success!");
  delay(1000);

  
  
//Bereitschaft der RealTimeClock prüfen
  Serial.println("Checking RTC");
  lcd.setCursor(0,2);
  lcd.print("Checking RTC");
  delay(800);



    if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    lcd.setCursor(0,3);
    lcd.print("Failed!");
    // following line sets the RTC to the date & time this sketch was compiled
    //RTC.adjust(DateTime(__DATE__, __TIME__));
    while (1);
  }

  Serial.println("RTC is running.");
  lcd.setCursor(0,3);
  lcd.print("RTC ready.");
  delay(800);
  lcd.clear();
  delay(800);


  Serial.println("Checking Logfile");
  lcd.setCursor(0,0);
  lcd.print("Checking Logfile");
  delay(1000);


dataFile = SD.open(logfile, FILE_WRITE);
  
  if (! dataFile) {
    Serial.println("Failed!");
    lcd.setCursor(0,1);
    lcd.print("Failed!");
    delay(800);
    // Wait forever since we cant write data
    while (1) ;
  }


   Serial.println("Logfile ready.");
   lcd.setCursor(0,1);
   lcd.print("Logfile ready.");
   delay(800);
  
  Serial.println("Start logging");
  lcd.setCursor(0,2);
  lcd.print("Start logging to");
  lcd.setCursor(0,3);
  lcd.print(logfile);
  delay(800);

}


//########################################
//########################################

void loop () {

// Rohdaten Temperatur vom Differenzverstärker auslesen - PIN A0
  temp_raw = analogRead(temp_pin);


// Rohdaten Temperatur auf 0-100°C mappen
  temp_value = map (temp_raw, 0, 1023, 0, 100);


  //Temp in die Logfile, Serial Monitor und LCD Schreiben
  Serial.println(temp_value);           //Temperatur auf Serialmonitor ausgeben
  lcd.clear();
  lcd.print(temp_value);                //Temperatur auf LC-Dispaly ausgeben
  dataFile.println(temp_value);         //Logfile öffnen und log_string schreiben
  dataFile.flush();                     //Logfile speichern und schließen


  delay(5000);

}

mit char logfile[] = "datalog.txt";

dann klappt damit die übergabe von "datalog.txt" an "SD.open" einwandfrei.
Damit wäre das "Problem" der globalen Einstellungen ganz oben im Code ja eigentlich kein Problem.
Schwierig wirds jetzt, wenn ich den Namen aus mehreren Variablen zusammen setzen will:

Mal ganz einfach nur Dateinamen und Endung verbinden:

char file[] = "datalog";
char endung[] = ".txt";
char logfile[] = file + endung;

Alleine schon das verursacht beim Kompilieren

error: invalid operands of types 'char [8]' and 'char [5]' to binary 'operator+'

Man kann auf chars kein "+" operator verwenden, OK. Aber wie verbinde ich die denn dann?

Wenn ichs so schreibe:

char file = 'datalog';
char endung = '.txt';
char logfile = file + endung;

gibt mir "Serial.println(logfile);" auch nur Müll heraus und beim Kompilieren mit SD.open() gibts dann folgendes vor den Latz: " error: invalid conversion from 'char' to 'char*'"

Ich komm da nicht weiter.

Gruß

char* ist ein Pointer auf char. Strings in C sind Null-terminierte Arrays aus char. Array Variablen in C sind Pointer auf das erste Element. char* ist daher ein String. Die Variable "file" ist ein char* der auf 'd' zeigt.

char file[] = "datalog";
char endung[] = ".txt";
char logfile[] = file + endung;

Die + Notation funktioniert mit C-Strings nicht. Das geht nur mit Objekten (wo im Hintergrund i.d.R. ein neues Objekt erstellt wird). Dafür gibt es in C diese Funktion:
http://www.cplusplus.com/reference/cstring/strcat/

Du musst dabei aufpassen, dass dein Ziel-Array groß genug ist! Also logfile muss mindestens groß genug für file + endung + 1 mal Null sein. Also 7 + 4 + 1 = 12. Wenn du da Dateinamen mit variabler Länge hast, kannst du auch 16 oder 20 nehmen.

Am besten erstmal ein Array deklarieren ohne es zu Initialisieren:
char logfile[12];

Dann mit strcpy() file nach logfile kopieren:
http://www.cplusplus.com/reference/cstring/strcpy/

Und dann endung an logfile mit strcat anhängen.

Oder du machst gleich file groß genug, dass du die Endung daran anhängen kannst. z.B.

char file[12];
strcpy(file, "datalog");

Dann musst du nur noch einmal strcat(file, endung); machen

Hallo,

also, ich habe nun folgendes:

char file[] = "datalog";               //char array? (string) anlegen und mit "datalog" füllen
char endung[] = ".txt";              //char array? (string) anlegen und mit ".txt" füllen
char logfile[12];                       //leeres char array? (string) mit platz für 7 + 4 +1mal Null = 12 anlegen

void setup() 
{
strcpy(logfile, file);                   //mit strcpy den Inhalt von "file" in das leere Array "logfile" kopieren
strcacat(logfile, endung);           //mit strcat den Inhalt von "endung" an das Array "logfile" anhängen
Serial.println(logfile);                // Array "logfile" über SerialMonitor ausgeben

}

Scheint soweit zu funktionieren. Danke! :slight_smile:

Ich Spiel mal ein bisschen und melde mich wieder

:slight_smile:

Du kannst wie gesagt in strcat und strcpy auch Literale verwenden.

Das hier funktioniert genauso:
strcat(logfile, ".txt");

Fr33man:

datalog_25.08.2013.txt

Schon falsch, weil als FAT-Dateiname viel zu lang.
Auf einem stinksimplen FAT-Dateisystem kann man keine "lange Dateinamen" verwenden.

Die FAT-Namenskonvention nennt sich "8.3", und das heißt:

  • 8 Zeichen Dateiname
  • ein Punkt
  • drei Zeichen als Dateiendung
    = 12 Zeichen insgesamt.

Ob die FAT-Library innerhalb der 12 Zeichen auch abweichende Dateinamen zuläßt, z.B. Dateinamen mit einem Punkt am Anfang oder irgendwo in der Mitte des Dateinamens, mehrere Punkte im Dateinamen, bestimmte Sonderzeichen im Dateinamen, ist abhängig von der Implementierung (also ggf. selbst ausprobieren).

Was für Datenlogger geht und üblich ist, sind Dateinamen nach dem Schema:
yyyymmdd.txt
wie Du sie z.B. mit Hilfe dieser von mir geschriebenen Funktion bilden kannst:

char* formatFilename(int day, int month, int year, char* ext)
{
  static char filename[13];
  snprintf(filename,sizeof(filename),"%04d%02d%02d.%s",year,month,day,ext);
  return filename;
}

Es wird gleich ein Char-Pointer (char*) zurückgeliefert, so dass der Rückgabewert der Funktion als Parameter für SD.open() verwendet werden kann.

Bildung eines Zufallsdateinamens z.B.:

  int day=random(1,31);
  int month=random(1,13);
  int year=random(2010,2014);
  char* filename=formatFilename(day,month,year,"CSV");
  Serial.println(filename);

Die Benennung von Logger-Dateien nach dem Muster "yyyymmdd.txt" hat sich übrigens deshalb durchgesetzt, weil die ganz einfachen FAT-Libraries für Mikrocontroller keine Verwaltung von Zeit- und Datumsstempeln für Dateien unterstützen, also Dinge wie "Zeit der Dateierstellung", "Zeit des letzten Zugriffs" und "Zeit des letzten Schreibvorgangs" mit typischen Mikrocontroller SD-Libraries NICHT zur Verfügung stehen. Und diese Art der Benennung hat den Vorteil: Eine alphabetische Sortierung der Dateinamen entspricht bei so einer Namenskonvention exakt einer Sortierung nach Datum, so dass sich die Dateinamen auch beim Auslesen auf einem anderen System (z.B. einem PC) trotz des fehlenden Datumsstempels der Dateien leicht chronologisch sortieren lassen, einfach durch alphabetische Sortierung.

jurs:
Schon falsch, weil als FAT-Dateiname viel zu lang.
Auf einem stinksimplen FAT-Dateisystem kann man keine "lange Dateinamen" verwenden.

Die FAT-Namenskonvention nennt sich "8.3", und das heißt:

  • 8 Zeichen Dateiname
  • ein Punkt
  • drei Zeichen als Dateiendung
    = 12 Zeichen insgesamt.

Ja, das habe ich auch noch bemerkt.. kleiner Edit in meinem ersten Post.
Aber danke nochmal für den Hinweis :slight_smile:

Serenifly:
:slight_smile:

Du kannst wie gesagt in strcat und strcpy auch Literale verwenden.

Das hier funktioniert genauso:
strcat(logfile, ".txt");

Danke! Habe ich beim spielen gerade mitbekommen :smiley:

Und nochmal ein großes Danke an jurs.

Ohne es zu wollen, hast du mir auch bei einem anderen Problem sehr weitergeholfen :smiley:

Formatierung von Datum und Uhrzeit. Die RTC-Library gibt nämlcih keine führenden Nullen (führende 0 - für google ;)) mit aus. Und ich wusste nicht, wie ich jetzt einen "int" und einen "char" in einer neuen Variable zusammenführen kann.. Dann kamst du daher :slight_smile:

Deine Funktion leicht geändert:

char* formatDate_Time(int day, int month, int year, int hour, int minute, int second)
{
  static char Date_Time[22];
  snprintf(Date_Time,sizeof(Date_Time),"%04d.%02d.%02d - %02d:%02d:%02d",year,month,day,hour,minute,second);
  return Date_Time;
}

Aufruf mit:

int day=now.day();
  int month=now.month();
  int year=now.year();
  int hour=now.hour();
  int minute=now.minute();
  int second=now.second();
  char* Date_Time=formatDate_Time(day,month,year,hour,minute,second);
  Serial.println(Date_Time);

getestet und läuft. Zumindest bis zum nächsten Problem ^^

Danke Leute!