Zeilenumbruch findet irgendwann nicht mehr statt

Hallo Zusammen!

Ich habe folgendes Problem:
Ich habe eine kleine Schaltung gebaut mit der man den den Lade- und Entladestrom sowie die Spannung eines Akkus über den Arduino erfassen kann. Die Daten sollen dann in einem bestimmten Zeitintervall (aktuell 5 Sekunden) auf eine SD-Karte geschrieben werden.
Soweit läuft das ganze auch. Allerdings findet nach einiger Zeit kein Zeilenumbruch mehr statt. Sowohl bei der Ausgabe auf dem seriellen Monitor, als auch bei der .csv Datei auf der SD-Karte. Das ganze schaut dann so aus, wie auf dem Bild im Anhang.

Woran kann das liegen?
Hab schon ein wenig gesucht, aber bisher nichts dazu im Netz gefunden…

Viele Grüße und schon mal vielen Dank im voraus!
Christopher

Der Fehler steckt in Zeile 42 deines Programms.

chrisn93:
Hab schon ein wenig gesucht, aber bisher nichts dazu im Netz gefunden…

Combie ist heute wieder gut drauf. Manchmal ist er auch ein bisschen knapp und wenig hilfreich.

Zeig’ am besten mal Deinen Sketch. Füge ihn so in Deine Nachricht ein, dass er scrollbar ist (fasse ihn in passende Tags). Wenn Du den „richtigen“ Editor mit Symbolleiste (und </>-Knopf, ganz links) benutzen möchtest, klick unten bei „Quick Reply“ auf „Preview“.

Gruß

Gregor

Bitteschön: :slight_smile:

#include <Keyboard.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"

#define ECHO_TO_SERIAL 1  // Daten auf seriellem Monitor ausgeben (0 = nein, 1 = ja)


#define runEvery(t) for (static uint32_t _lasttime;\
                        (uint32_t)((uint32_t)millis() - _lasttime) >= (t);\
                         _lasttime += (t))

                         
const int chipSelect = 10;           // Ethernetshield: Pin4; Adafruit SD Shield & Modules: Pin 10

RTC_DS1307 RTC;
RTC_DS1307 rtc;

float  Messintervall = 5000;         // festlegen des Messintervalls in ms

//Variablen Spannungsmessung
float  U;                            // Variable für die Batteriespannung
float  Ubatmax = 28.8;               // Maximale Batteriespannung
float  Usptmax = 4.999;              // Wert am Spannungsteiler bei Ubatmax
float  TV =  Ubatmax / Usptmax;      // Teilerverhältnis Spannungsteiler
float  UmessV;                       // Messwert am Spannungsteiler
float  UmessVd;                      // Messwert am Spannungsteiler digital (Bitwert) 


//Variablen Strommessung
float  I;                            // Variable für den Strom
float  Umax = 5.01;                 // Ausgangsspannung am 5V Ausgang des Arduinos (messen!)
float  Ubit = Umax / 1023;           // Schrittweite pro Bit real
float  Ubitt = 1023 / 5;             // Schrittweite pro Bit theoretisch (Sollwert)
float  E = 0.1;                      // Empfindlichkeit des ACS 712 in V/A
float  Uoffset;                      // Offsetspannung des ACS 712
float  Uoffsetbit;                   // Offsetspannung des ACS 712 digital (Bitwert)
int    Uoffsetbitint;                // Offsetspannung des ACS 712 ohne Nachkommastellen
float  Vcc;                          // Versorgungsspannung des ACS 712
float  UmessA;                       // Messwert am Ausgang des ACS 712                          
double UmessAa;                      // Analogwert von UmessA

float   As;                          // Variable für AmpereSekunden
float   Ws;                          // Variable für WattSekunden
double  Ah;                          // Variable für AmpereStunden
double  Wh;                          // Variable für WattStunden

//String Heading = ("Datum,Zeit,Spannung,Strom,Amperesekunden,Wattsekunden,Amperestunden,Wattstunden");  //Spaltenüberschriften
String Heading = ("Datum,Zeit[h:min:s],Spannung[V],Strom[A],Ladung [As],Energie [Ws],Ladung [Ah],Energie [Wh]");  //Spaltenüberschriften
int    HeadingState = 0;


File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);


  while(1);
}
void setup() {
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

// folgende Zeile setzt die RTC auf das aktuelle Datum und die aktuelle Zeit:
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");



  Wire.begin();  
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }

}

void loop() {

  DateTime now = RTC.now();

  uint32_t m = millis();

   
// Spannungsmessung
  UmessV = analogRead(A1);           // Einlesen Wert des Spannungsteilers an Pin A1
  UmessVd = UmessV*Ubit;             // Umrechnen Bitwert in Spannung
  U = TV * UmessVd;                  // Umrechnen Spannung am Spannungsteiler in Realwert
  

// Strommessung
  Vcc = Umax;                            
  Uoffset = (Vcc*0.5);                   // Offset Spannung ACS 712 = 0,5xVcc  
  UmessA = analogRead(A0);               // Einlesen Wert des ACS 712 an Pin A0
  Uoffsetbit = Uoffset*Ubitt;            // Umrechnen Offsetspannung in Bitwert
  Uoffsetbitint = (int)Uoffsetbit;       
  
  if (UmessA >= (Uoffsetbitint-2) and UmessA <= (Uoffsetbitint+2))
  {
    I = 0;
  }
  else
  { 
    UmessAa = UmessA*Ubit;
    I = (UmessAa - Uoffset)*10;
  }


//Berechnung As, Ws, Ah und Wh
As = I * (Messintervall/1000);
Ws = As * U;

  runEvery(Messintervall) {
    Ah += As / 3600;
    Wh += Ws / 3600;
  }



// Schreiben auf SD-Karte
String FileName = "";                              // Dateinamen festlegen
  FileName += String(now.day());
  FileName += String(now.month());
  FileName += String(now.year());
  FileName += ".csv";

  if (now.hour() == 0 && now.minute() == 0 && now.second() == 0) // neuer Tag -> neue Überschrift der Spalten
   { HeadingState = 0;
   Ah = 0;                            // Rücksetzen der Ah
   Wh = 0;                            // Rücksetzen der Wh
   }
    
  runEvery(Messintervall) {
    logfile = SD.open(FileName, FILE_WRITE);
      if (logfile) {
      if (HeadingState == 0) {        // Anweisung, dass Überschrift nur 1x geschrieben wird
        logfile.println(Heading);
        HeadingState = 1;
      }

  logfile.print(now.day(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.year(), DEC);
  logfile.print(", ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print(",");
  logfile.print(U);
  logfile.print(",");
  logfile.print(I);
  logfile.print(",");
  logfile.print(As);
  logfile.print(",");
  logfile.print(Ws);
  logfile.print(",");
  logfile.print(Ah);
  logfile.print(",");
  logfile.print(Wh);
  logfile.println();
    logfile.close();
 
  }
      
else {                                               // error, falls Datei nicht geöffnet werden kann
      Serial.println("error opening Textfile");
     }

      
//Ausgabe auf Seriellem Monitor
#if ECHO_TO_SERIAL

  //Serial.print(now.unixtime());       // seconds since 1/1/1970
  //Serial.print(", ");
  //Serial.print('"');

  Serial.print("Datum: ");
  Serial.print(now.day(), DEC);
  Serial.print(".");
  Serial.print(now.month(), DEC);
  Serial.print(".");
  Serial.print(now.year(), DEC);
  Serial.print("   ");
  Serial.print("Uhrzeit: ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.println(now.second(), DEC);

  Serial.print("Messwert Digital [Bit]: ");Serial.println(UmessVd);   
  Serial.print("Messwert Spannung [V]: ");Serial.println(UmessV); 
  Serial.print("Spannung [V]: ");Serial.print(U);Serial.println(" Volt");
  
  Serial.print("Uoffset [V]: ");Serial.println(Uoffset); 
  Serial.print("Messwert Digital [Bit]: ");Serial.println(UmessA); 
  Serial.print("Uoffsetbit [Bit]: ");Serial.println(Uoffsetbitint); 
  Serial.print("Messwert Analog [V]: ");Serial.print(UmessAa);Serial.println(" Volt");
  Serial.print("Strom [A]: ");Serial.print(I);Serial.println(" Ampere");
  Serial.print("Ladung [As]: ");Serial.println(As);
  Serial.print("Energie [Ws]: ");Serial.println(Ws);
  Serial.print("Ladung [Ah]: ");Serial.println(Ah);
  Serial.print("Energie [Wh]: ");Serial.println(Wh);
  Serial.println("__________________________");
  
#endif // ECHO_TO_SERIAL
  }

}

Viele Grüße,
Christopher

Du verschwendest sehr, sehr viel RAM. Durch die Romane die du schreibst und dann dynamisch durch die unnötige Verwendung der String Klasse. Das kann mit der Zeit Probleme geben

Erst mal verwende überall wo du String Literale mit print()/println() hast das F()-Makro:

Serial.println(F("String im Flash"));

Wenn du ein einzelnes Zeichen schreiben willst, verwende ':' statt ":"

Meine Arduino IDE sagt dazu:

Globale Variablen verwenden 1.632 Bytes (79%) des dynamischen Speichers, 416 Bytes für lokale Variablen verbleiben. Das Maximum sind 2.048 Bytes.
Wenig Arbeitsspeicher verfügbar, es können Stabilitätsprobleme auftreten.

Und so geschieht es bei dir.

Abhilfe 1:
Nutze das F() Macro!
Ersetze: Serial.print("Uoffset [V]: ");
Durch: Serial.print(F("Uoffset [V]: "));
Und das bei allen konstanten Zeichenketten.

Abhilfe 2:
Verzichte auf die String Klasse!
Diese bricht dir, mit ihrer dynamischen Speicherverwaltung, das Genick.
Alternativ: Reserviere ihr im Setup schon genug Speicher.

Abhilfe 3:
Verzichte auf float, da wo es nicht nötig ist.

ich hatte gerade mal etwas Langeweile und habe deinen Sketch nach den Vorschlägen von Combie und Serenifly überarbeitet, bis auf die String Objekte, die kannst du wenn es mit der Zeit Probleme gibt gegen Char Arrays austauschen. Probier’s mal aus.

//#include <Keyboard.h> // wird im Sketch nicht genutzt
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"

#define ECHO_TO_SERIAL 1  // Daten auf seriellem Monitor ausgeben (0 = nein, 1 = ja)


#define runEvery(t) for (static uint32_t _lasttime;\
                        (uint32_t)((uint32_t)millis() - _lasttime) >= (t);\
                         _lasttime += (t))

                         
const int chipSelect = 10;           // Ethernetshield: Pin4; Adafruit SD Shield & Modules: Pin 10

RTC_DS1307 RTC;
RTC_DS1307 rtc;

const uint16_t  Messintervall = 5000;         // festlegen des Messintervalls in ms

//Variablen Spannungsmessung
float  U;                            // Variable für die Batteriespannung
const float  Ubatmax = 28.8;         // Maximale Batteriespannung
const float  Usptmax = 4.999;        // Wert am Spannungsteiler bei Ubatmax
float  TV =  Ubatmax / Usptmax;      // Teilerverhältnis Spannungsteiler
uint16_t  UmessV;                    // Messwert am Spannungsteiler
float  UmessVd;                      // Messwert am Spannungsteiler digital (Bitwert)


//Variablen Strommessung
float  I;                            // Variable für den Strom
const float  Umax = 5.01;                 // Ausgangsspannung am 5V Ausgang des Arduinos (messen!)
float  Ubit = Umax / 1023;           // Schrittweite pro Bit real
float  Ubitt = 1023 / 5;             // Schrittweite pro Bit theoretisch (Sollwert)
float  E = 0.1;                      // Empfindlichkeit des ACS 712 in V/A
float  Uoffset;                      // Offsetspannung des ACS 712
float  Uoffsetbit;                   // Offsetspannung des ACS 712 digital (Bitwert)
int    Uoffsetbitint;                // Offsetspannung des ACS 712 ohne Nachkommastellen
float  Vcc;                          // Versorgungsspannung des ACS 712
uint16_t  UmessA;                    // Messwert am Ausgang des ACS 712                         
double UmessAa;                      // Analogwert von UmessA

float   As;                          // Variable für AmpereSekunden
float   Ws;                          // Variable für WattSekunden
double  Ah;                          // Variable für AmpereStunden
double  Wh;                          // Variable für WattStunden

//String Heading = ("Datum,Zeit,Spannung,Strom,Amperesekunden,Wattsekunden,Amperestunden,Wattstunden");  //Spaltenüberschriften
String Heading = ("Datum,Zeit[h:min:s],Spannung[V],Strom[A],Ladung [As],Energie [Ws],Ladung [Ah],Energie [Wh]");  //Spaltenüberschriften
int    HeadingState = 0;


File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);


  while(1);
}
void setup() {
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

// folgende Zeile setzt die RTC auf das aktuelle Datum und die aktuelle Zeit:
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  Serial.print(F("Initializing SD card..."));

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println(F("Card failed, or not present"));
    // don't do anything more:
    return;
  }
  Serial.println(F("card initialized."));



  Wire.begin(); 
  if (!RTC.begin()) {
    logfile.println(F("RTC failed"));
#if ECHO_TO_SERIAL
    Serial.println(F("RTC failed"));
#endif  //ECHO_TO_SERIAL
  }

}

void loop() {

  DateTime now = RTC.now();

  uint32_t m = millis();

   
// Spannungsmessung
  UmessV = analogRead(A1);           // Einlesen Wert des Spannungsteilers an Pin A1
  UmessVd = UmessV*Ubit;             // Umrechnen Bitwert in Spannung
  U = TV * UmessVd;                  // Umrechnen Spannung am Spannungsteiler in Realwert
 

// Strommessung
  Vcc = Umax;                           
  Uoffset = (Vcc*0.5);                   // Offset Spannung ACS 712 = 0,5xVcc 
  UmessA = analogRead(A0);               // Einlesen Wert des ACS 712 an Pin A0
  Uoffsetbit = Uoffset*Ubitt;            // Umrechnen Offsetspannung in Bitwert
  Uoffsetbitint = (int)Uoffsetbit;       
 
  if (UmessA >= (Uoffsetbitint-2) and UmessA <= (Uoffsetbitint+2))
  {
    I = 0;
  }
  else
  {
    UmessAa = UmessA*Ubit;
    I = (UmessAa - Uoffset)*10;
  }


//Berechnung As, Ws, Ah und Wh
As = I * (Messintervall/1000);
Ws = As * U;

  runEvery(Messintervall) {
    Ah += As / 3600;
    Wh += Ws / 3600;
  }



// Schreiben auf SD-Karte
String FileName = "";                              // Dateinamen festlegen
  FileName += String(now.day());
  FileName += String(now.month());
  FileName += String(now.year());
  FileName += ".csv";

  if (now.hour() == 0 && now.minute() == 0 && now.second() == 0) // neuer Tag -> neue Überschrift der Spalten
   { HeadingState = 0;
   Ah = 0;                            // Rücksetzen der Ah
   Wh = 0;                            // Rücksetzen der Wh
   }
   
  runEvery(Messintervall) {
    logfile = SD.open(FileName, FILE_WRITE);
      if (logfile) {
      if (HeadingState == 0) {        // Anweisung, dass Überschrift nur 1x geschrieben wird
        logfile.println(Heading);
        HeadingState = 1;
      }

  logfile.print(now.day(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.year(), DEC);
  logfile.print(", ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print(",");
  logfile.print(U);
  logfile.print(",");
  logfile.print(I);
  logfile.print(",");
  logfile.print(As);
  logfile.print(",");
  logfile.print(Ws);
  logfile.print(",");
  logfile.print(Ah);
  logfile.print(",");
  logfile.print(Wh);
  logfile.println();
    logfile.close();
 
  }
     
else {                                               // error, falls Datei nicht geöffnet werden kann
      Serial.println(F("error opening Textfile"));
     }

     
//Ausgabe auf Seriellem Monitor
#if ECHO_TO_SERIAL

  //Serial.print(now.unixtime());       // seconds since 1/1/1970
  //Serial.print(", ");
  //Serial.print('"');

  Serial.print(F("Datum: "));
  Serial.print(now.day(), DEC);
  Serial.print(F("."));
  Serial.print(now.month(), DEC);
  Serial.print(F("."));
  Serial.print(now.year(), DEC);
  Serial.print(F("   "));
  Serial.print(F("Uhrzeit: "));
  Serial.print(now.hour(), DEC);
  Serial.print(F(":"));
  Serial.print(now.minute(), DEC);
  Serial.print(F(":"));
  Serial.println(now.second(), DEC);

  Serial.print(F("Messwert Digital [Bit]: "));Serial.println(UmessVd);   
  Serial.print(F("Messwert Spannung [V]: "));Serial.println(UmessV);
  Serial.print(F("Spannung [V]: "));Serial.print(U);Serial.println(F(" Volt"));
 
  Serial.print(F("Uoffset [V]: "));Serial.println(Uoffset);
  Serial.print(F("Messwert Digital [Bit]: "));Serial.println(UmessA);
  Serial.print(F("Uoffsetbit [Bit]: "));Serial.println(Uoffsetbitint);
  Serial.print(F("Messwert Analog [V]: "));Serial.print(UmessAa);Serial.println(F(" Volt"));
  Serial.print(F("Strom [A]: "));Serial.print(I);Serial.println(F(" Ampere"));
  Serial.print(F("Ladung [As]: "));Serial.println(As);
  Serial.print(F("Energie [Ws]: "));Serial.println(Ws);
  Serial.print(F("Ladung [Ah]: "));Serial.println(Ah);
  Serial.print(F("Energie [Wh]: "));Serial.println(Wh);
  Serial.println(F("__________________________"));
 
#endif // ECHO_TO_SERIAL
  }

}

Ein einzelnes Zeichen schreibt man besser in einfachen Anführungszeichen. Dann ist es automatisch eine Konstante die kein RAM belegt. Es gibt keinen Grund da ein zwei Byte String-Literal draus zu machen.

RTC_DS1307 RTC;
RTC_DS1307 rtc;

:o 2 Instanzen? :o

Da würde ich gerne mal den Sinn erfahren!

Hallo Zusammen,

entschuldigt die späte Antwort!
Die überarbeitete Version des Sketches von @ardubu hat tatsächlich schon ausgereicht, damit das ganze absolut stabil läuft. Vielen Dank dafür! :slight_smile:

combie:
:o 2 Instanzen? :o

Da würde ich gerne mal den Sinn erfahren!

Sollte eigentlich nur einmal auftauchen, war unbeabsichtigt :smiley:

Du hattest vorgeschlagen, ich solle auf die String Klasse verzichten.
Wie könnte ich das bewerkstelligen?

Viele Grüße,
Christopher

Du hattest vorgeschlagen, ich solle auf die String Klasse verzichten.
Wie könnte ich das bewerkstelligen?

void add2( char* p, byte val) {
  // val ( 0 .. 99 ) wird als Dezimalzahl nach p und p+1 geschrieben.
  *p++ = (val/10) + '0';
  *p      = (val%10) + '0';
} 
...

char filename[13] ; // 8.3 mit Terminator
char* cp = filename;

add2(cp, now.day());
add2(cp+2, now.month());
add2(cp+4, now.year()/100);
add2(cp+6, now.year()%100);
strcpy(cp+8, ".csv");  // liefert z.B. "31122016.csv" wenn year() eine vierstellige Jahreszahl liefert
Serial.println(filename);