Öffnen von Datei

Hallo Zusammen,

ich versuche gerade mit meinem fehlenden Wissen das Rückgrat für einen Datenlogger zu schreiben
(ok, zusammen zu kopieren;-)).

Der Gedanke ist, über die Datumsangabe soll pro Tag (z. Zt. testweise im Minutentakt) eine Datei geöffnet werden und zeilenweise mit Inhalt gefüllt werden.
Wenn ich das richtig verstanden habe braucht SD.open(char;…) die Angabe des Dateinamens als char,
was ich auch mit dname.c_str() versucht habe, aber augenscheinlich wird keine Datei erstellt bzw. geöffnet.

Warum?
Auch eine komplexere Umwandlung des Strings dname in char hat nicht geklappt.

Für den einen oder anderen kleinen Tipp wäre ich dankbar.

#include <Wire.h>
#include <DS3231.h>

#include <SPI.h>
#include <SD.h>

File myFile;

unsigned long previousMillis = 0; // speichert wie viele Sekunden seit derletzten Änderung vergangen sind
unsigned long interval = 1000;    // Interval zwischen zwei Aenderungen


DS3231 clock;
RTCDateTime dt;
String dname;
String name1;


void setup()
{
//  pinMode(ledPin, OUTPUT);      // Setzt den ledPin (Pin 13) als Ausgang
Serial.begin(9600);
clock.begin();
SD.begin(53);  
}

 

void loop() {
  // Code der immer laufen soll kommt hier hin.





  if (millis() - previousMillis > interval) {
    previousMillis = millis();   // aktuelle Zeit abspeichern

// Code der in Abstaenden laufen soll kommt hier hin.

 
dt = clock.getDateTime();

name1 = (clock.dateFormat("YmdHi", dt));
dname = name1 + ".txt";
Serial.println(dname);
myFile = SD.open(dname.c_str(), FILE_WRITE);

//hier scheint ein Problem zu sein, es wird keine Datei angelegt bzw. geöffnet!

delay(300);
if (myFile) {
    Serial.print("Writing...");
    myFile.println(clock.dateFormat("d F Y H:i:s",  dt));
    // close the file:
    myFile.close();
    Serial.println("done.");


     
Serial.println("Wiederholung!");


   }
   
  }
}

z. Zt. testweise im Minutentakt

Du hast mit "intervall = 1000" einen 1-Sekunden-Takt eingestellt.
Vielleicht verhaspelt er sich an dieser Stelle?

Ich habe es auf 10 Sekunden eingestellt,

geht aber auch nicht.

Folgende Codeänderung funktioniert auch nicht (obwohl alles sauber kompiliert):

dname = name1 + ".txt";
//Serial.println(dname);
//myFile = SD.open(dname.c_str(), FILE_WRITE);
char fileNameCharArray[dname.length()+1];
dname.toCharArray(fileNameCharArray, sizeof(fileNameCharArray));
if(!SD.exists(fileNameCharArray))
{

myFile = SD.open(fileNameCharArray, FILE_WRITE);

Serial.println(fileNameCharArray);

dt = clock.getDateTime();
name1 = (clock.dateFormat("YmdHi", dt));

Ist mir zu mühsam, zu suchen/raten was da passiert, zumal du einfach nur schauen musst, was auf Serial angezeigt wird.

Dass der Wert von name1 höchstens 8 Buchstaben haben darf, weisst du ?

Das mit der Dateinamenlänge von 8 Stellen hats gebracht.
Die Verlängerung um Stunden und Minuten war Mist;-)
Danke für den Hinweis Michael!

Für die die es interessiert hier mal der (vorläufige) funktionierende Code.

Heute Nacht werde ich mal das Ding laufen lassen um zu schauen,
ob tatsächlich bei neuem Datum eine neue Datei angelegt wird
(Wovon ich allerdings ausgehe).

Somit hätte man 2 unterschiedliche Zeitschleifen.
Einmal wird für jeden Tag eine neue Datei angelegt,
was interessant ist, falls mal Schreibfehler auftreten, damit wenigstens abgeschlossene Tage ausgewertet werden können und nicht alle Daten verloren sind.

Zum Anderen eine Möglichkeit je nach Wunsch die Anzahl der Messintervalle am Tag anzupassen.

Es ist zwar keine “stromsparende” Lösung, aber für den Anfang doch eine funktionierende.

Wer eine bessere Ideen hat, darf sie los werden;-)

#include <Wire.h>
#include <DS3231.h>

#include <SPI.h>
#include <SD.h>

File myFile;

unsigned long previousMillis = 0; // speichert wie viele Sekunden seit derletzten Änderung vergangen sind
unsigned long interval = 10000;    // Interval zwischen zwei Aenderungen


DS3231 clock;
RTCDateTime dt;
String dname;
String name1;


void setup()
{
//  pinMode(ledPin, OUTPUT);      // Setzt den ledPin (Pin 13) als Ausgang
Serial.begin(9600);
clock.begin();
SD.begin(53);  
}

void loop() {
  // Code der immer laufen soll kommt hier hin.

  if (millis() - previousMillis > interval) {
    previousMillis = millis();   // aktuelle Zeit abspeichern

// Code der in Abstaenden laufen soll kommt hier hin.

 
dt = clock.getDateTime();

name1 = (clock.dateFormat("Ymd", dt));
dname = name1 + ".txt";

char fileNameCharArray[dname.length()+1];
dname.toCharArray(fileNameCharArray, sizeof(fileNameCharArray));
//if(!SD.exists(fileNameCharArray))


myFile = SD.open(fileNameCharArray, FILE_WRITE);

Serial.println(fileNameCharArray);
//delay(300);
if (myFile) {
    Serial.print("Writing...");
    myFile.print(clock.dateFormat("d.m.Y",  dt));
    myFile.print(";");
    myFile.print(clock.dateFormat("H:i:s",  dt));
    myFile.print(";");
    myFile.print("Hier fehlt noch was!");

//Sensordaten
    
    myFile.println("");

    // close the file:
    myFile.close();
    Serial.println("done.");

   }
  }
}

Ich verwende den Alarm-Interrupt-Ausgang der RTC, damit kommst Du auf genauere Intervalle als mit der millis-Zählerei. Ist sicher nicht kriegsentscheidend, aber Du hast gefragt :slight_smile:

toCharArray() braucht man nicht wenn man nur lesenden Zugriff auf das interne Array benötigt.

Dafür gibt es c_str():
http://www.arduino.cc/en/Reference/CStr
Damit wird kein zusätzlicher Speicher belegt

Serenifly:
toCharArray() braucht man nicht wenn man nur lesenden Zugriff auf das interne Array benötigt.

Dafür gibt es c_str()

Na ja, Schreiben wird auch gehen, man sollte aber dann schon wissen was man tut.

c_str() liefert einen const Zeiger (d.h. ein Zeiger auf const Daten). Denn kann man zwar umgehen, aber so ganz einfach ist es nicht.

Danke für den Hinweis auf die Alarm-Funktion, die werde ich nach einbinden der Sensoren mal ausprobieren, dann habe ich ein Vergleich für den Stromverbrauch.

"Na ja, Schreiben wird auch gehen, man sollte aber dann schon wissen was man tut."

Das weiß ich im Moment nicht so genau, aber wer nicht wagt der nicht gewinnt;-)

Ich probier einfach mal .c_str().

Ich gebe dir gerne Recht, ich hatte nicht nachgeschaut wie die Definition genau war.

Es ist ausreichend um Flüchtigkeitsfehler aufzudecken und Anfänger abzuhalten.
Am besten kommt man ja sowieso nicht in die Verlegenheit, indem man direkt char Arrays benutzt.

Die String Klasse war nett gemeint, weil sie so einfach zu bedienen ist, hat aber die Plattform verfehlt.
In die Falle 'mein Programm stürzt unvorhersehbar ab' läuft doch fast jeder der sie benutzt.

Genauso ist in den Beispielen die Anwendung des F() Makros eher selten (falls überhaupt vorhanden),
und Anfängerfalle 2 'Ich habe keinen Speicher mehr (und es u.U. noch nicht einmal bemerkt)' vorbereitet.

Der Stromverbrauch ist der gleiche für String wie für char Array, für millis() wie für irgendwelche Alarm-Funktionen der RTC. und bei Betrieb über ein Steckernetzteil vernachlässigbar.

Welche Library genau verbirgt sich hinter #include <DS3231.h> ?
Liefert deren Methode DS3231::DateFormat() tatsächlich ein String-Objekt und braucht die eigene Zeit als Parameter? Etwas verdächtig, für meinen Geschmack …

Die Library und der Code kommen von hier:

/*
  DS3231: Real-Time Clock. Date Format
  Read more: www.jarzebski.pl/arduino/komponenty/zegar-czasu-rzeczywistego-rtc-ds3231.html
  GIT: https://github.com/jarzebski/Arduino-DS3231
  Web: http://www.jarzebski.pl
  (c) 2014 by Korneliusz Jarzebski
*/

#include <Wire.h>
#include <DS3231.h>

DS3231 clock;
RTCDateTime dt;

void setup()
{
  Serial.begin(9600);

  // Initialize DS3231
  Serial.println("Initialize DS3231");;
  clock.begin();
}
String dname;
String name1;

void loop()
{
  dt = clock.getDateTime();

  Serial.print("Long number format:          ");
  Serial.println(clock.dateFormat("d-m-Y H:i:s", dt));
  
  Serial.print("Anderes Format:          ");
  name1 = (clock.dateFormat("Ymd", dt));
  dname = name1 + ".txt";
  Serial.println(dname);
//  Serial.println(clock.dateFormat("YmdHis", dt) + ".txt");
  
  Serial.print("Long format with month name: ");
  Serial.println(clock.dateFormat("d F Y H:i:s",  dt));

  Serial.print("Short format witch 12h mode: ");
  Serial.println(clock.dateFormat("jS M y, h:ia", dt));

  Serial.print("Today is:                    ");
  Serial.print(clock.dateFormat("l, z", dt));
  Serial.println(" days of the year.");

  Serial.print("Actual month has:            ");
  Serial.print(clock.dateFormat("t", dt));
  Serial.println(" days.");

  Serial.print("Unixtime:                    ");
  Serial.println(clock.dateFormat("U", dt));

  Serial.println();

  delay(1000);
}

Ich habe ihn verwendet, damit ich den Monat und Tag auf einfache Weise 2stellig bekomme (das sortiert schöner in der Dateiansicht;-)

Der Versuch Strom zu sparen, ist im Moment für mich sowieso nachrangig.
Ggf. versuche ich - wenn ich mehr über diese kleinen elektronischen Dinger gelernt habe - das Ganze mal auf einem Tennsy 3.2 mit snooze Library und Alarm-Weck-Funktion über RTC aufzubauen.
Die Stromersparnis wird dann sicherlich stark von der Messfrequenz abhängen,
ggf. stellt sich die Frage ob der dazu notwendig Boot-Vorgang nicht mehr Strom benötigt, als das Ding einfach laufen zu lassen.

Aber das ist Zukunftsmusik.

Der Controller ist die Nacht über durchgelaufen und “pünktlich” zum Tageswechsel wurde eine neue Datei angelegt. Es funktioniert also im Moment alles wie es soll.

Die nächsten Schritte werden die Einbindung eines Dallas one wire Temperaturfühlers und eine Spannungsmessung für ein 12 V Akku werden.

Hab mir die mal angesehen: dateFormat erzeugt kein String-Objekt, aber gibt einen Zeiger auf einen lokalen Puffer zurück, der allerdings reichlich (255) bemessen ist.

Da man nie weiss, wie lange dieser Puffer nach Aufruf von dateFormat gültig bleibt (formal ist das Verhalten undefiniert), solltest du folgenden Vorschlag vorsichtig verwenden:

char* name = dateFormat("Ymd",dt); // liefert 8 Zeichen "YYYYMMDD" und reichlich Platz danach.
strcat (name, ".txt");
myFile = SD.open(name, FILE_WRITE);

Besser du verwendest statt dateFormat einen eigenen passenden Puffer und die Original-Funktion:

char name[13] = "yyyymmdd.txt";
sprintf(name,"%04d%02d%02d", dt.year, dt.month, dt.day);
name[8] = '.' ; // Endekennung reparieren 
Serial.println(name);
myFile = SD.open(name, FILE_WRITE);

Das spart zwar keinen Strom, aber Speicher. Der ist bei Verwendung einer SD-Karte mit Uno nicht üppig. Mit einem Teensy3.2 natürlich kein Thema mehr.

Danke für den Tipp.
Ich werde mir mal das Ganze genauer anschauen
(Programmieren ist doch ein wenig mehr als Code über Libraries zusammen zu stricken;-))

P. S.: Mein "Uno" ist ein "Mega", ganz so knapp mit Recourcen sollte es also nicht sein.

@Michael

Ich habe Deinen Vorschlag (teilweise) erfolgreich umgesetzt:

char dname[13] = "yyyymmdd.txt";
sprintf(dname,"%04d%02d%02d", dt.year, dt.month, dt.day);
dname[8] = '.' ; // Endekennung reparieren
Serial.println(dname);
myFile = SD.open(dname, FILE_WRITE);

char datum[11] = "dd.mm.yyyy";
sprintf(datum, "%02d%03d%05d", dt.day, dt.month, dt.year);
datum[2] = '.' ;
datum[5] = '.' ;

char zeit[9] = "hh:mm:ss";
sprintf(zeit, "%02%03%03", dt.hour, dt.minute, dt.second);
//zeit[2] = ':' ;
//zeit[5] = ':' ;


Serial.println(datum);
Serial.println(dt.hour);
Serial.println(dt.minute);
Serial.println(dt.second);


Serial.println(zeit);
//Temperatursensor -> todo

//Lichtsensor -> todo

//Luftdrucksensor -> todo

//Luftfeuchtigkeitsensor -> todo

//Hier schreiben wir in die Logdatei

//myFile = SD.open(dname.c_str(), FILE_WRITE);
if (myFile) {
    Serial.print("Writing...");
    myFile.print(datum);
    myFile.print(";");
    myFile.print(clock.dateFormat("H:i:s",  dt));
    myFile.print(";");
    myFile.print("Hier fehlt noch was!");

//Sensordaten

Das mit dem Dateinamen und dem Datum in der Datei klappt.
Aber das mit der Uhrzeit bekomme ich nicht hin.

Das ist die Ausgabe von Serial.println:

...schnipp...
0160425.txt
25.04.2016
12
40
16

Writing...done.
20160425.txt
25.04.2016
12
40
21

Writing...done.

...schnapp...

Wie man sieht sind die Stunden, Minuten, Sekunden da, aber das char array ist leer.
In Ermangelung einer Fehlermeldung stehe ich auf dem Schlauch (mal wieder;-))
Irgendwelche Ideen?

Versuch mal

sprintf(zeit, "%02d%03d%03d", dt.hour, dt.minute, dt.second);

Du hattest den Feldtyp beim sprintf vergessen. :wink:

P.S. warum sollen Minuten und Sekunden dreistellig werden?

Schau dir die printf() Doku an. Dein Format-String stimmt nicht

%02d bedeutet eine Mindestbreite von zwei Zeichen mit führenden Nullen in Dezimal

@Whandall
Hühnerk...e!
Du hast recht, jetzt klappt es:

Writing...done.
20160425.txt
25.04.2016
13
13
2
13:13:02
Writing...done.
20160425.txt
25.04.2016
13
13
7
13:13:07
Writing...done.

Kaum macht man es richtig, schon klappt es;-))

@Serenifly

Die übrigen (0)Stellen werden vom ":" überschrieben.