Nano + SDlib memoryprobleme

Hallo zusammen,

ich baue mir gerade einen Datenlogger zusammen. Aufgezeichnet werden sollen bis zu 32 Analogwerte die dann entsprechend skaliert werden sollen.

Basis für alles ist ein Nano3.0 und die einschlägig bekannte SD.h Bibliothek.

Leider stoße ich andauernd an die Grenzen des sehr kleinen Memoryspeichers.
Das zwischenspeichern in ein Array aus Stringvariablen funktioniert praktisch gar nicht, das auslesen von Daten auf der SD und sofortiges abspeichern an einen anderen Ort der SD Karte führt auch nicht wirklich zum Erfolg.

Gibt es evtl. alternative Libarys die weniger Memory benötigen? Oder irgendeinen Trick wie ich das in den Griff bekommen kann oder bleibt nur der Weg über ein Board mit größerem Speicher wie dem Mega?

Vielleicht hat ja jemand eine Idee..
Besten Dank!

SdFat ist vielleicht etwas effizienter. Aber die legt auch einen 512 Byte Puffer an. Die Standard SD Lib ist aber auch nur ein Wrapper einer sehr alten SdFat Version.

TinyFat scheint recht minimalistisch zu sein, aber auch da ist ein 512 Byte Puffer drin:
http://henningkarlsen.com/electronics/library.php?id=37

Habe ich aber noch nicht ausprobiert.

Ansonsten solltest du die String Behandlung soweit wie möglich durch Verwendung von F(), PSTR() und/oder PROGMEM optimieren. Ohne das landen alle String Literale im RAM.
Das geht aber nicht mit allen Funktionen. Einer Funktion die einen String im RAM erwartet kannst du keinen String im Flash übergeben.
F() geht nur mit print()/println(). PSTR() geht nur mit den _P Versionen von String Funktionen oder selbst geschriebenen Funktionen die das dann entsprechend verarbeiten.

ALF84:
Oder irgendeinen Trick wie ich das in den Griff bekommen kann

Würdest Du "programmieren lernen" bereits als "Trick" bezeichnen?

Hier im Forum stelle ich immer wieder fest, dass Leute, die niemals irgendwo einen nennenswerten Programmierkurs (z.B. Vorlesung "Einführung in die Programmierung mit ..." an irgendeiner Uni oder sowas) belegt haben, sondern mit "Lerarning-by-Doing" und der Methode "Copy-and-Paste" irgendwelche Spaghetticodemonster von zweifelhafter Qualität nach der Frankenstein-Methode zusammensetzen ("keine Leiche ist zu häßlich, um nicht wiederbelebt zu werden"), sich dann darüber wundern, dass sie irgendwas "nicht in den Griff bekommen".

Ein Dr. Frankenstein hat ja die von ihm geschaffenen Monster bekanntlich auch nie in den Griff bekommen, nicht mal im Film.

Typische Arduino-Boards haben immer mindestens 2 KB RAM, und eine SD-Library belegt davon kaum mehr als 1 KB, so dass nach Abzug einer Reserve von ca. 250 Bytes für den Laufzeit-Stack für jede Art Programm mit Verwendung von SD-Karten stets noch ca. 700 Bytes freier RAM-Speicher zum Verbraten durch das Anwenderprogramm zur Verfügung stehen sollten, während mit der SD-Library gearbeitet wird. Womit man bei sparsamem Umgang eine ganze Menge anfangen kann.

Außer natürlich, man kann selbst überhaupt nicht programmieren und stellt Spaghetticodemonster mit Copy-and-Paste nach der Frankenstein-Methode zusammen...

Serenifly:
SdFat ist vielleicht etwas effizienter. Aber die legt auch einen 512 Byte Puffer an. Die Standard SD Lib ist aber auch nur ein Wrapper einer sehr alten SdFat Version.

TinyFat scheint recht minimalistisch zu sein, aber auch da ist ein 512 Byte Puffer drin:
Electronics - Henning Karlsen

Habe ich aber noch nicht ausprobiert.

Ansonsten solltest du die String Behandlung soweit wie möglich durch Verwendung von F(), PSTR() und/oder PROGMEM optimieren. Ohne das landen alle String Literale im RAM.
Das geht aber nicht mit allen Funktionen. Einer Funktion die einen String im RAM erwartet kannst du keinen String im Flash übergeben.
F() geht nur mit print()/println(). PSTR() geht nur mit den _P Versionen von String Funktionen oder selbst geschriebenen Funktionen die das dann entsprechend verarbeiten.

Das ist ein guter Hinweis das schaue ich mir mal an!

@jurs Ich hoffe du verlangst jetzt nicht auch noch von mir dass ich Dich ernst nehme?!

Ist das dein Code?

Da fällt dann schon sofort auf, dass du bei Serial nicht das F() Makro verwendest. Richtig:

Serial.println(F("Blah blah. Romane schreiben ohne dass RAM belegt wird"));

Die String Klasse sollte man auch rausschmeißen. Die ist nie nötig und verschwendet nur Speicher (vor allem da sie fast nie korrekt verwendet wird). Wenn du meinst, dass dein Speicher so schon knapp ist, wieso pfuscht du dann noch mit dynamischem Speicher herum?

Ja so hatte ich angefangen, in der Zwischenzeit ist noch etwas dazugekommen. Das ist aber wahrscheinlich auch nicht besser, sonst würde es ja funktionieren.

Das ist mein erstes Projekt, die F Funktion etc. kannte ich bisher nicht. Ich lese mich mal ein.

#include <MemoryFree.h>
#include "Wire.h"
#include <SD.h>
#include "math.h"
#include "stdlib.h"

//**************************************constant
#define DS1307_ADDRESS 0x68
#define DS3231_SECONDS 0x00
#define DS3231_MINUTES 0x01
#define DS3231_HOURS   0x02
#define DS3231_DAY     0x03
#define DS3231_DATE    0x04
#define DS3231_MONTH   0x05
#define DS3231_YEAR    0x06

#define NUMBER_OF_MEASUREMENTS 10


const int ANALOGPIN0 = A0;
const int PIN_MUX_S0 = 2;
const int PIN_MUX_S1 = 3;
const int PIN_MUX_S2 = 4;
const int PIN_MUX_S3 = 5;
const int PIN_MUX_E1  = 6;
const int PIN_MUX_E2  = 7;

File myFile;

//**************************************global vars
double measurement_value[NUMBER_OF_MEASUREMENTS];
int measurement_raw_data[NUMBER_OF_MEASUREMENTS];


//**********************************************************************************************
//**************************************  setup  ***********************************************
//**********************************************************************************************
void setup(){
  pinMode(10, OUTPUT); //needet for sd lib
  pinMode(PIN_MUX_S0, OUTPUT);  
  pinMode(PIN_MUX_S1, OUTPUT);  
  pinMode(PIN_MUX_S2, OUTPUT);  
  pinMode(PIN_MUX_S3, OUTPUT);  
  pinMode(PIN_MUX_E1 , OUTPUT);  
  pinMode(PIN_MUX_E2 , OUTPUT);  

    Serial.begin(9600);
    Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
   
  if (!SD.begin(10)) 
  {
    Serial.println("initialization failed!");
    return;
  }
    Serial.println("initialization done.");

  Wire.begin();
  
}

//**********************************************************************************************
//**************************************  main  ***********************************************
//**********************************************************************************************
	 
void loop()
{
    get_analog_data();
    calculate_measurement();
    save_measurement();
    delay(4000);
}



//**********************************************************************************************
//**************************************functions***********************************************
//**********************************************************************************************

//**************************************clock
 byte bcdToDec(byte val){
 // Convert binary coded decimal to normal decimal numbers
  return ( (val/16*10) + (val%16) );
}
	 
int clock_get_Date(int i){

  // Set registerpointer to first register
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  delay(100);
  	 
  Wire.requestFrom(DS1307_ADDRESS, 7);
  
  int second = bcdToDec(Wire.read());        
  int minute = bcdToDec(Wire.read());
  int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
  int weekday = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
  int monthday = bcdToDec(Wire.read());
  int month = bcdToDec(Wire.read());
  int year = bcdToDec(Wire.read());
  
  switch(i)
  {
    case 0: return second; 
    break;
    case 1: return minute; 
    break;
    case 2: return hour; 
    break;
    case 3: return weekday; 
    break;
    case 4: return monthday; 
    break;
    case 5: return month; 
    break;
    case 6: return year; 
    break;
    default:
    break;
  } 
}

void clock_set_Date(int clockregister, byte value){

  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(clockregister);
  Wire.write(value);
  Wire.endTransmission();
}  

//**************************************sd functions


void save_measurement(void)
{
  Serial.println("-->Function:save_measurement");
  
  if (SD.exists("Messw.csv")) 
  {
    write_dataset();
  }
  else 
  {
    create_file();
    write_dataset();
  }
}
//*********************************************************************************write dataset on sd
void write_dataset(void)
{
  Serial.println("-->Function:write_dataset");
  if(SD.exists("Messw.csv"))
  {    
    myFile = SD.open("Messw.csv", FILE_WRITE);
    
    if (myFile) 
    {
      Serial.print("Writing to Messw.csv...");
      
      //write Timestamp
      myFile.print(clock_get_Date(DS3231_DATE));
      myFile.print(".");
      myFile.print(clock_get_Date(DS3231_MONTH));
      myFile.print(".");
      myFile.print(clock_get_Date(DS3231_YEAR));
      myFile.print(";");
      
      myFile.print(clock_get_Date(DS3231_HOURS));
      myFile.print(":");
      myFile.print(clock_get_Date(DS3231_MINUTES));
      myFile.print(":");
      myFile.print(clock_get_Date(DS3231_SECONDS));
      myFile.print(";");
    
      //write Measurements
      int i;
      for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
      {
        myFile.print(measurement_value[i]);
        myFile.print(";");
      }
      myFile.print("\n");
    
      // close the file:
      myFile.close();
    
      Serial.println("done.");
    }
    else 
    {
      Serial.println("error opening Messw.csv");
      print_free_memory();
    }
  }
}

und der rest…

//*********************************************************************************create file for dataset
void create_file(void)
{
  Serial.println("-->Function:create_file");

  //prepare column deklarations for first line (get from config sd)
  String column_name[NUMBER_OF_MEASUREMENTS];
  int i;
  for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
  {
    column_name[i] = get_config_string(i,1);  
  }
  
  //create file
  myFile = SD.open("Messw.csv", FILE_WRITE);
  if (myFile) 
  {
    Serial.println("created new File 'Messw.csv'");  
    myFile.print("Date");
    myFile.print(";");
    myFile.print("Time");
    myFile.print(";");
    
    for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
    {
      myFile.print(column_name[i]);
      myFile.print(";");
      
    }
    myFile.print("\n");  //end of line
    myFile.close();
  }
  else
  {
    Serial.println("could not create 'Messw.csv'");
  }

}


//*********************************************************************************get data saved on sd to calculate some measurements
int get_config_value(int measurand_nb, int config_line_offset)
{
  Serial.println("-->Function:get_config_value");
  
  int i,cnt_char,cnt_line,return_value;
  int config_line_data;   //line where we can find data of measurand
  char c;
  char chararray[20];
  
  config_line_data = config_line_offset + measurand_nb;

  myFile = SD.open("config.txt");
  if (myFile) 
  {
  //Serial.println("config.txt opened successfull");
    
    cnt_line=0;
    while (myFile.available()) // read from the file until there's nothing else in it:
    {
       cnt_char=0;  

       do
       {          
          c = myFile.read();     //write Chars to Array
          chararray[cnt_char] = c;  
          cnt_char++;            //count every char
        }while(c != '\n');      //till end of line

        cnt_line++;            //count line
        
        cnt_char=cnt_char-3;    //char counter ->decrement 3 ( \n + cycle after detection because of do-while)
        
        if(cnt_line == config_line_data)  //calculate measurand only for the right line
        {
          return_value =  atol(&chararray[0]); //atol gives back intvalue starting on adress of array0
        }
    }
    myFile.close();  // close the file:
  } 
  else 
  {
    Serial.println("error opening config.txt L276");     // if the file didn't open, print an error:
    print_free_memory();
  }
  return return_value;    
  }

//**********************************************************

String get_config_string(int measurand_nb, int config_line_offset)
{
  Serial.println("-->Function:get_config_value");

  String return_value;
  int i,cnt_char,cnt_line;
  int config_line_data;   //line where we can find data of measurand
  char c;
  char chararray[10];
  
  config_line_data = config_line_offset + measurand_nb;

  myFile = SD.open("config.txt");
  if (myFile) 
  {
    cnt_line=0;
    while (myFile.available()) // read from the file until there's nothing else in it:
    {
       cnt_char=0;  

       do
       {          
          c = myFile.read();     //write Chars to Array
          chararray[cnt_char] = c;  
          cnt_char++;            //count every char
        }while(c != '\n');      //till end of line

        cnt_line++;            //count line
        
        cnt_char=cnt_char-2;    //char counter ->decrement 3 ( \n + cycle after detection because of do-while)
        
        if(cnt_line == config_line_data)  //calculate measurand only for the right line
        {
          for(i=0;i<cnt_char;i++)
          {
            return_value += String(chararray[i]);
            Serial.println(chararray[i]);
          }
        }
    }
    myFile.close();  // close the file:
  } 
  else 
  {
    Serial.println("error opening config.txt L325");     // if the file didn't open, print an error:
    print_free_memory();
  }
  return return_value;    
  }

//**************************************other
void calculate_measurement(void)
{
  int i;
  Serial.println("-->Function:calculate_measurement");

  for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
  {
    if(get_config_value(i,91) == 1)  //If digital value
    {
      if(measurement_raw_data[i] > get_config_value(i,121)) //If raw data digital bigger than configurable threshold
      {
        measurement_value[i] = get_config_value(i,151);     //then write configurable defaultvalue 
      }
      else
      {
        measurement_value[i] = 0;                          //otherwise 0
      }
    }
    else    //if analog value
    {
      measurement_value[i] =  get_config_value(i,31) * measurement_raw_data[i] + get_config_value(i,61);  //calculate unit y=mx+b
    }
  }
}

void get_analog_data(void)
{
  Serial.println("-->Function:get_analog_data");
  int i;
  
  for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
  {
    measurement_raw_data[i] = get_analog_value(i);
  }
}

int get_analog_value(int channel)
{
  set_analogchannel(channel);    //set mux channel
  return analogRead(ANALOGPIN0);  //read analogvalue
}

int set_analogchannel(int channel)
{
  int bin[4];
  int i;

  for(i=0;i<4;i++)
  {
    bin[i] = 0;
  }
  
  if(channel < 16)                     //first 74HC4067
  {
    digitalWrite(PIN_MUX_E1, LOW); //active LOW!!
    digitalWrite(PIN_MUX_E2, HIGH);

    for(i=0;channel>0;i++)
    {
      bin[i] = channel%2;
      channel = channel/2;
    }
  }
  else                               //second 74HC4067
  {
    digitalWrite(PIN_MUX_E1, HIGH);
    digitalWrite(PIN_MUX_E2, LOW);

    channel = channel -16;
    
    for(i=0;channel>0;i++)
    {
      bin[i] = channel%2;
      channel = channel/2;
    }
  }

  if(bin[0] == 1){digitalWrite(PIN_MUX_S0, HIGH);}else{digitalWrite(PIN_MUX_S0, LOW);}
  if(bin[1] == 1){digitalWrite(PIN_MUX_S1, HIGH);}else{digitalWrite(PIN_MUX_S1, LOW);}
  if(bin[2] == 1){digitalWrite(PIN_MUX_S2, HIGH);}else{digitalWrite(PIN_MUX_S2, LOW);}
  if(bin[3] == 1){digitalWrite(PIN_MUX_S3, HIGH);}else{digitalWrite(PIN_MUX_S3, LOW);}
}

void print_free_memory(void)
{
  //Test free memory
  Serial.print("freeMemory()=");
  Serial.println(freeMemory());
}

Wenn du mal überall wo du print() mit String Literalen verwendest (auch bei File) das F() Makro verwendest wirst du schon merken, dass du plötzlich ein paar Hundert Byte mehr frei hast.

Liegt an der Harvard Architektur des AVRs. Normale Funktionen und Assembler Befehle können nur ins RAM greifen. Deshalb werden alle Strings am Anfang vom Flash ins RAM kopiert. Man kann Strings auch im Flash lassen, aber dann muss man einige Umwege machen um darauf zuzugreifen.
Das ist ein Krampf, aber es hat sich leider so etabliert. Und zukünftigen Compiler Versionen wird es hoffentlich etwas einfacher (da muss man nur beim deklarieren sagen, dass die Daten im Flash sind, aber der Compiler übernimmt dann automatisch den Zugriff).

Einen Trick um den Speicherverbrauch beim Öffnen von Dateien zu minimieren ist das:

char stringBuffer[13];
#define P(str) strcpy_P(stringBuffer, PSTR(str))

P() kann man dann wie F() verwenden, aber auch für Funktionen die einen RAM String wollen wie open(). Das Makro kopiert dann einfach den String aus dem Flash in den Puffer und gibt einen Zeiger auf den Puffer zurück.

Das kostet natürlich erst mal 13 Byte + etwas Flash für strcpy(). Aber wenn man das mehrmals verwendet, braucht man nicht jedes mal RAM für den Dateinamen.

Oder noch ne Kleinigkeit:
Statt

myFile.print(";");

Das:

myFile.print(';');

Belegt kein RAM und weniger Flash als F(";")

Besten Dank!!!
Die F-Funktion haut es raus, jetzt habe ich Speicher ohne Ende und kann ohne Probleme alle Messwerte verarbeiten. Trotzdem werde ich mich mit dem Thema jetzt mal auseinandersetzen. Den String habe ich auch gleich wieder rausgeschmissen. Umso erstaunlicher, dass mir bei der Relevanz der F-Funktion selbige noch nicht über den Weg gelaufen ist. Ich gucke mal dass ich mir ein Buch besorge wo man sowas nachlesen kann. Habe schon einige überflogen doch leider richten sich viele Arduino-Bücher an "Anfänger" mit den entsprechenden Projekten -LED blinken -LCD mit Hello World etc.
Daumen hoch für die gute Erklärung!

Ja, es gibt ein paar Dinge die nicht gut dokumentiert sind. Dieser Bereich gehört dazu. Man findet Anleitungen dazu, aber auf der normalen Arduino Webseite wird das nicht genug betont. Und auch die Doku zu PROGMEM ist veraltet.

F() ist genau genommen keine Funktion, sondern ein Makro. Das deklariert einen String mit dem PROGMEM Attribut und castet ihn per C++ reinterpret_cast auf einem Zeiger auf eine Hilfsklasse namens __FlashstringHelper. Das wird gemacht, weil ein normaler PROGMEM String lediglich ein const char* ist. Also nicht von einem String im RAM zu unterscheiden ist was den Datentyp betrifft. Den __FlashstringHelper* dagegen kann man von einem const char* unterscheiden und kann so verschiedene Funktionen gleichen Namens schreiben (Überladung). Die Version die den F() String als Parameter hat, macht dann einen Cast auf const char* und behandelt diesen intern wie normal.

Sieht so aus:

#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

jetzt habe ich Speicher ohne Ende

Schön für dich 8) 700 Byte "zum Verbraten", wie jurs richtig bemerkt hat.

ALF84:
@jurs Ich hoffe du verlangst jetzt nicht auch noch von mir dass ich Dich ernst nehme?!

Nein, du brauchst jurs nicht ernst zu nehmen, er nimmt dich ja auch nicht ernst.
War schon etwas ruppig, und das Frankenstein-Beispiel ist sicher zu blumig um hilfreich zu sein. Hat jurs evtl. Spass gemacht, das so zu formulieren.
Erfordert von dir einige Größe zu überlegen, ob was an der Antwort richtig oder bedenkenswert war.

Willkommen im Forum und Viel Erfolg mit dem Datenlogger!
Hast du schon raus warum ( oder welche ) deiner SD Karten gehen?
Im anderen Thread schreibst du dass 2 von 4 funktionieren ?
Was für einen SD-Adapter verwendest du ?

Ja genau, 700 Bytes sind auch ungefähr das was mir die Funktion aus der Memoryfree lib angezeigt wurde. Leider war ich mit meiner Antwort dass alles funktioniert etwas schnell aber ich weiß ja jetzt wonach ich gucken muss.

michael_x:
Nein, du brauchst jurs nicht ernst zu nehmen, er nimmt dich ja auch nicht ernst.
War schon etwas ruppig, und das Frankenstein-Beispiel ist sicher zu blumig um hilfreich zu sein. Hat jurs evtl. Spass gemacht, das so zu formulieren.
Erfordert von dir einige Größe zu überlegen, ob was an der Antwort richtig oder bedenkenswert war.

Vielleicht habe ich es auch missinterpretiert. Für mich kam das anders rüber. Ist aber jetzt auch kein Thema was mich länger beschäftigt. Ich merke nur an, dass hier sicherlich einige unterwegs sind die nicht Informatik studiert haben. Manche schneiden auch Haare, das kann jurs z.B. nicht.

Vielen Dank! Noch ist es nicht fertig aber was nicht ist kann ja noch werden. Habe mich bisher noch nicht weiter damit beschäftigt habe ja jetzt eine gefunden die funktioniert. Vielleicht gucke ich mir das später nochmal an. Als SD-Adapter verwende ich nen billigen Chinakracher für 1,50.. sowas hier: http://www.ebay.de/itm/SD-Card-Module-Slot-Socket-Reader-For-Arduino-ARM-MCU-NEW/371177132029?_trksid=p2045573.c100033.m2042&_trkparms=aid%3D111001%26algo%3DREC.SEED%26ao%3D1%26asc%3D20131017132637%26meid%3D3b38fa365783448485efbfb84d50c4f6%26pid%3D100033%26rk%3D3%26rkt%3D4%26sd%3D221534455907

Immerhin schreibt der Händler, dass du Spannungsteiler brauchst !

Jetzt muss ich aber doch nochmal fragen.
Nochmal kurz zu dem was ich vor habe:
Ich möchte eine .txt Datei auslesen in der Konfigurationswerte gespeichert sind damit ich nicht jedes mal wieder den Sketch anpassen muss wenn sich irgendwas ändert. Unter anderem befinden sich in dieser Datei auch die Bezeichnungen für meine Messwerte. Diese möchte ich in meiner log-csv-datei als Spaltennamen eintragen.
Problem ist nach wie vor, dass ich wenn ich zuviele Werte einlese meinen Speicher vollschreibe.
PROGMEM kann hier also kein Ansatz sein, da ich den FLASH zur Laufzeit nicht ändern kann -richtig?
Meine Frage wäre jetzt wo mein Problem eigentlich entsteht. Durch die Strings die ich verwende? Wenn ja, wieviel Speicher belegen die eigentlich? Das konnte ich bisher irgendwie nicht herausfinden, aber im grundegenommen ist es doch auch nichts anderes als ein array aus chars, d.h. ich hätte bei 30 messwerten 30* 12chars = 360bytes Speicher reserviert. Das ist zwar nicht schön aber die alternative wäre dann ja nur die bytes aus einer datei auszulesen -datei schließen- nächste öffnen- schließen usw…
Wobei wenn ich mal so drüber nachdenke, zu den 360bytes kommt ja auch noch der rest… Also reduziert sich meine Frage auf: Gibt es eine Alternative zum einzelnen auslesen und umkopieren?

void create_file(void)
{
  Serial.println(F("-->Function:create_file"));
 
 //prepare ROW Deklarations for first line (get from config sd)
// FLASH_STRING(column_name, NUMBER_OF_MEASUREMENTS);
  String column_name[10];
  int i;
  for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
  {
    column_name[i] = get_config_string(i,1);  
  }
  //create file
  myFile = SD.open("Messw.csv", FILE_WRITE);
  if (myFile) 
  {
    Serial.println(F("created new File 'Messw.csv'"));  
    myFile.print(F("Date"));
    myFile.print(';');
    myFile.print(F("Time"));
    myFile.print(';');
    
    for(i=0;i<NUMBER_OF_MEASUREMENTS;i++)
    {
      myFile.print(column_name[i]);  //copy column-names from config to first line of dataset
//      myFile.print(get_config_string(i,1));  -->verbraucht offensichtlich zu viel speicher..
      myFile.print(';');
    }
    
    myFile.print("\n");  //end of line
    myFile.close();
  }
  else
  {
    Serial.println(F("could not create 'Messw.csv'"));
  }
}
String get_config_string(int measurand_nb, int config_line_offset)
{
//  Serial.println(F("-->Function:get_config_value"));

  String return_value;
  int i,cnt_char,cnt_line;
  int config_line_data;   //line where we can find data of measurand
  char c;
  char chararray[12];
  
  config_line_data = config_line_offset + measurand_nb;

  myFile = SD.open("config.txt");
  if (myFile) 
  {
    cnt_line=0;
    while (myFile.available()) // read from the file until there's nothing else in it:
    {
       cnt_char=0;  

       do
       {          
          c = myFile.read();     //write Chars to Array
          chararray[cnt_char] = c;  
          cnt_char++;            //count every char
        }while(c != '\n');      //till end of line

        cnt_line++;            //count line
        
        cnt_char=cnt_char-2;    //char counter ->decrement 3 ( \n + cycle after detection because of do-while)
        
        if(cnt_line == config_line_data)  //calculate measurand only for the right line
        {
          for(i=0;i<cnt_char;i++)
          {
            return_value += String(chararray[i]);
          //  Serial.println(chararray[i]);
          }
        }
    }
    myFile.close();  // close the file:
  } 
  else 
  {
    Serial.println(F("error opening config.txt L325"));     // if the file didn't open, print an error:
    print_free_memory();
  }
  return return_value;    
  }

michael_x:
Immerhin schreibt der Händler, dass du Spannungsteiler brauchst !

Ich hab meine Auktion nicht so schnell wiedergefunden aber wenn ich mir das jetzt so angucke deutet auch das andere Schaltbild daraufhin dass da noch ein Spannungsteiler zwischen muss.. Ich würde ja jetzt mit dem Spannungsabfall auf der Leitung argumentieren aber das erspare ich mir an der Stelle :wink:

*zur vervollständigung
http://www.ebay.de/itm/271517827462

Wieso willst du da die Namen der Messwerte rauslesen? Was was ist ergibt sich doch alleine schon aus der Reihenfolge. Also wenn du z.B. sowas hast: "123,456,369" solltest du wissen was welcher Wert bedeutet ohne dass es dabei steht

Man kann dann erst mal die komplette Zeile in ein char Array einlesen und danach mit strtok() splitten und atoi() wandeln. Aber wenn man sich den Speicher für den String sparen will, kann man auch nur die aktuelle Zahl einlesen. Das geht sogar ganz ohne char Array, wobei für negative Zahlen da noch etwas mehr Code braucht.

Das habe ich gerade hier Prinzip-mäßig mit Serial gezeigt:

Mit der SdFat Library (nicht die normale SD Lib!) kann man übrigens ganz einfach eine komplette Zeile auf einmal einlesen. Ich mache das:

bool readLineFromSD(const char* filename, char* buffer, int bufferSize, int line)
{
  if (sd.begin())
  {
    if (file.open(filename, O_READ))
    {
      int count = 1;

      while (file.fgets(buffer, bufferSize) > 0)
      {
        if (count == line)
          break;
        count++;
      }
      file.close();

      if (count != line)
        return false;

      return true;
    }
  }

  return false;
}

Damit übergebe ich die Zeilennummer (von 1 an, aber das kann man auch 0-basierend machen). fgets() liest eine Zeile in das Array. Das mache ich solange entweder die Datei zu Ende ist oder ich die gewünschte Zeile habe.

Dann kann man einfach sowas in der Art machen:

void parseLine(char* buffer, struct Data* data)
{ 
 data->value1 = atoi(strtok(buffer, ","));
 data->value2 = atoi(strtok(NULL, ","));
 data->value3 = atoi(strtok(NULL, ","));
}

Wobei es wie oben gesagt auch Speicher-sparender geht. Aber ich habe das auf dem Mega laufen und da ist noch Code für ein TFT und sonstiges Zeug drin und ich habe nur knapp über 2kB RAM belegt.

Prinzipiell brauchen die Libraries für SD -Dateien erstmal mindestens 512 byte RAM. Daher wird auch meist nur eine einzige offene Datei unterstützt.

Ich würde eine LogDatei schon mit einer passenden csv-Überschrift vorbereiten
(Oder aus dem Flash initialisieren).
Kann mir nicht vorstellen, was man in einer config-Datei stehen haben kann das einem "erspart", den sketch für andere Logdatei-Inhalte anzupassen und neu zu kompilieren.

Selbst wenn man da irgendwie die Namen/Bedeutungen der Werte auslesen will, muss man diese ja nicht dauerhaft auf dem Arduino speichern. Wenn man die wo Anzeigen oder vergleichen will reicht es i.d.R. sie nacheinander einzulesen und nur temporär zu speichern.

Mit anpassen meine ich, dass ich prinzipiell 30 Kanäle zur Verfügung habe die ich alle analog über zwei Multiplexerbausteine auslese. Allerdings bin ich auf der Seite noch nicht so weit. Um einige Messwerte auszulesen muss ich mir noch was basteln, daher kommen die erst nach und nach dazu. Für die Analogwerte gehe ich davon aus, dass alle linear verlaufen und in der configdatei speicher ich dann eben die steigung und die offsetwerte. Es gibt auch "digitale" Werte die ich auslesen möchte, diese lese ich aber ebenfalls analog aus und speicher mir dafür eine schwelle ab wann ich den Messwert als 1 oder eben 0 interpretieren möchte und entspechend in der logdatei wegspeicher.

Die von Serenifly genannten Features (bis Zeilenende lesen, einzelne Zeilen auslesen etc.) kann meine Funktion auch schon, auch negative Werte sind kein Problem. Allerdings ist bei mir viel mehr Code erforderlich und was viel schlimmer ist, ich öffne die config Datei, lese ALLE Zeilen ein die ich benötige und speicher sie zwischen und erst dann öffne ich die logdatei und speichere es dort ab und wie ich das so verstehe liegt ja genau da das Problem.

Also:
Verbesserungspotenzial 1:
-> Einzelne Zeile aus Config einlesen ->Speichern in Log -> Einzelne.. usw..

Verbesserungspotenzial 2:
Weg mit dem StringObject her mit einem CharArray

Das StringObject habe ich sowieso nur verwendet damit ich mir davon ein Array aus mehreren Strings machen kann. Also praktisch ein zweidimensionales CharArray?!
Wieviel Speicher legt der sich denn jetzt eigentlich an für einen String und wieviel für ein Stringarray? Hier auf der Seite steht nur, dass er für ein StringObject mehr benötigt und für ein Chararray pro Zeichen 1Byte + 1Byte zum terminieren

*Edit
Funktioniert!