SD Float/UL in array

Abend zusammen,

Ich speichere zurzeit 2 messwerte auf einer SD karte in diesem format:

15:26, 38.30
15:36, 38.60
16:1, 38.80

und

15:26, 980
15:36, 960
16:1, 520

habt ihr vllt was passendes auf lager um die werte in ein array zu bekommen ?

x1[0] = 15:26
x2[0] = 38.30

x1[1] = 15:36
x2[1] = 38.60

x1[2] = 16:1
x2[2] = 38.80

bzw

y1[0] = 15:26
y2[0] = 980

y1[1] = 15:36
y2[1] = 960

y1[2] = 16:1
y2[2] = 520

Danke schonmal :)

Ich würde die Daten als struct organisieren (char uhrzeit[6] , Floatwert, UL-Wert) und daraus ein Array bilden. Auf der SD dann jede Arrayposition als Zeile aus den 3 Werten speichern.

Du müsstest natürlich wissen, wieviele Zeilen es werden können (Speicherbedarf) oder als erste Zeile die Anzahl speichern und das Array dynamisch erzeugen. Das dann evtl. als 2. Ausbaustufe.

Gruß Tommy

maximal können es,

sensor 1: 23:59, 99,99 12 zeichen , sensor 2: 23:59, 1023 11 zeichen werden

Die Frage war nach Zeilen, nicht Zeichen. Obwohl Zeichen auch gut sind. Aufbau:

23:59,99.99,1023 wären 16 Nutzzeichen plus '\0' für den Abschluss = 17 Zeichen

Für die Teilung am , schaue mal das Beispiel zu strtok in Zeichenketten in C an.

Zum Schreiben einfach die file.print hintereinander (wie bei Seriall und beim letzten println.

Gruß Tommy

Achso Zeilen werden es 48 sein.

Ich schreibe bereits die werte per File.print in eine Datei... täglich wird ne neue angelegt.

Dateinamen ist das heutigen Datum. 161229.csv

Ich will die Daten aus der csv jetzt auslesen um sie grafisch auf dem TFT darzustellen.

Aber dafür brauche ich sie eben in einem Array... mit 48 Zeilen.

Von mir aus können auch beide Sensorwerte in einer Datei sein. So wie du es oben dargestellt hast.

Das aufteilen nach dem , Macht mir eben die Probleme

Schau Dir mal das Beispiel zum strtok an. Das teilt so auf - erst mal in Teilzeichenketten und dann mit atof (float) bzw. strtoul (UL).

Du musst immer eine Zeile einlesen (bis zum ‘\n’). Etwa so (noch ungetestet):

char inputChar;  // Zeichen zum Einlesen
const byte IN_LEN = 20; // Puffergroesse Eingabe
char inStr[IN_LEN];  // Lesepuffer
#ifdef DEBUG
char inStrDebug[IN_LEN];  // Lesepuffer zum Debuggen merken wegen Stringtokenizer
#endif
const char trenn = ',';
boolean fertig = false; // Eingabe beendet

.....

// true = fertig (LF oder zu lang), false = noch nicht fertig
boolean einlesenZeile() {
static byte i = 0; // Positionsindex in inStr
  // wenn es schon eine vollstaendige Eingabe gab -> zuruecksetzen
  if (fertig) {
    fertig = false;  // neuer Anfang
    inStr[0] = '\0'; // Puffer initialisieren
    i = 0; // Positiojnsindex auf Anfang
  }
  inputChar = datafile.read();
  // Wenn nicht Zeilenende und nicht Puffer voll
  if (inputChar != '\n' && i < IN_LEN -1) {
    // Zeichen in inStr schreiben
    inStr[i] = inputChar;
    inStr[++i] = '\0'; // Abschluss dahinter schreiben, um evtl. Debugausgaben richtig anzuzeigen
    return false;
  }  
  // kopieren wegen strtok_r fuer Debuggen / Antwort
  // strtok_r veraendert das Ursprungsarray
  #ifdef DEBUG
  strcpy(inStrDebug,inStr);
  Serial.println("Fertig");
  #endif
  return true;
}

Was Du mit der Zeit machen willst, weiß ich noch nicht.

Gruß Tommy

mpl1337: Das aufteilen nach dem , Macht mir eben die Probleme

Warum machst Du Dir denn die Probleme und schreibst Deine Werte als "Roman" formatiert in Klartext in die Datei?

Kennst Du keine Dateien "mit Datensätzen fester Länge, in die Du direkt Binärdaten schreiben kannst? Oder wie man neudeutsch sagen würde "data file with fixed length data records"?

Und als einen Datensatz (data record) würdest Du ein Array mit sechs Integer-Werten abspeichern.

Die Uhrzeiten mit den Stunden als hunderter und Minuten als Zehner/Einer und die Gleitkommawerte in hundertsteln als ganze Zahl.

Also Deinen ersten Datenblock:

x1[0] = 15:26 x2[0] = 38.30

x1[1] = 15:36 x2[1] = 38.60

x1[2] = 16:1 x2[2] = 38.80

als Array mit sechs Integer-Werten:

int daten1[6]={1526,3830,1536,3860,1601,3880};

Die gesamte Datei mit 48 "Zeilen" (Datensätzen) wäre dann quasi wie ein zweidimensionales Array mit 48 Zeilen und sechs Spalten zu sehen.

Ein Datensatz hat dann immer exakt 12 Bytes und kann mit einem einzigen Befehl in Datei geschrieben werden und auch mit einem Befehl gelesen werden. Das Lesen von 48 Datensätzen wäre also eine for-Schleife mit 48 Durchläufen (oder wieviel Datensätze eben in der Datei drin sind, könnte ja vielleicht mal vorkommen, dass Du eine Datei mit weniger oder mehr Datensätzen lesen möchtest.

Zum Einlesen eines Datensatzes w#re dann die Syntax zu verwenden, bei der eine Adresse (Pointer) im RAM vorgegeben wird und die Anzahl der Bytes , die aus der DAtei in den Puffer eingelesen werden sollen:

DieseCodezeile würde aus der Datei genau 12 bytes in das Array daten1 einlesen:

file.read(daten1, lsizeof(daten1));

und schon hast Du alle sechs Werte aus einer "Zeile! der Datei im Array stehen zur weiteren Verarbeitung.

Den umständlichen Kram, numerische Werte beim Schreiben in Datei erst in Textform-Prosa umzuwandeln und in Datei zu Schreiben, um dann beim Lesen den 48-Zeilen-Roman wieder als Text einzulesen und dann wieder in numerische Werte zu zerlegen, den kannst Du Dir sparen. Das ist fehlerträchtig und langsam.

Das kommt wohl auch darauf an, wo die Daten her kommen. Binär mit fester Länge ist mit Sicherheit das effektivste Format. Es lässt sich aber schwer händisch erzeugen.

Da fehlen noch infos zum Gesamtablauf.

Gruß Tommy

Die Werte kommen von der Timelib, 1Wire lib, DHT lib und einer von Analogread.

Ich hab mir das ganze einfacher vorgestellt.

Die Uhrzeiten benötige ich für die Y Achse eines Graphes.

In welcher Form willst Du sie denn benutzen? Für eine Achsennutzung wäre eher Minute des Tages (Std*60+Minute) sinnvoll. Da wäre man dann doch eher beim binären Protokoll oder willst Du händisch Sätze erzeugen?

Gruß Tommy

Im grunde Brauch ich die Uhrzeit doch nicht… da ich sowieso nur 48 werte pro 24Stunden habe kenne ich ja meine messpunkte auf ein paar Minuten genau… da kann ich ja anhand des Array indexes die Messwerte an die richtige Stelle platzieren

Jedoch möchte ich die Uhrzeit in der CSV lassen um eine Auswertung per OpenOffice noch hübscher darstellen lassen kann.

Wenn Du csv machen willst, dann fällt der binäre Teil weg

Dann musst Du alles als ASCII schreiben und einlesen.

Schau mal nach, welche Zetformate OO akzeptiert.

Gruß Tommy

mpl1337: Jedoch möchte ich die Uhrzeit in der CSV lassen um eine Auswertung per OpenOffice noch hübscher darstellen lassen kann.

Könntest Du mal so eine *.csv Datei hier anhängen?

Eine mittels OO erzeugte Datei sieht bei mir so aus:

15:26;38,30
15:36;38,60
16:01;38,80

Dezimaltrenner ist das Komma, weshalb als Zelltrenner nur das Semikolon bleibt. Ist das bei Dir anders?

Das kommt darauf an, welche Sprache bei OO angegeben ist. Danach ist der Dezimaltrenner . oder , Um es sauber zu machen, sollte man als Werttrenner ; nehmen und danach , durch . ersetzen, wenn man float braucht. Weil atof den . will.

Gruß Tommy

Ich nehme mal meine Daten aus #12 und speichere die in einem Feld. Da ich kein Zahlenformat für die Uhrzeit kenne, wird 15:26 als 1526 gespeichert. Der Wert 38,30 wird als float 38.30 gespeichert.

#include <SPI.h>
#include <SdFat.h>
#define error(s) sd.errorHalt(F(s))  // store error strings in flash to save RAM
ArduinoOutStream cout(Serial);       // create Serial stream

const uint8_t chipSelect = SS;      // SD chip select pin
SdFat sd;                           // file system object
char fileName[] = "messung.csv";

const byte Anzahl_Werte = 10, Anzahl_Buchstaben = 20;
unsigned int x1[Anzahl_Werte];
float x2[Anzahl_Werte];

// read and print CSV file
void readFile() {
  cout << F("\nSD-Karte einlesen Anfang") << endl;
  byte index = 0;
  ifstream sdin(fileName);  // open input file
  if (!sdin.is_open()) error("open");  // check for open error

  // read until input fails
  while (index < Anzahl_Werte) {
    char c1, c2, c3;
    int w1, w2, w3, w4;
    sdin >> w1 >> c1 >> w2 >> c2 >> w3 >> c3 >> w4;
    if (sdin.fail()) break;      // Ausstieg aus der Schleife
    if (c1 != ':' || c2 != ';' || c3 != ',') {
      error("fehlerhafter Feldtrenner");
    }
    cout << w1 << "  " << w2 << "  " << w3 << "  " << w4 << endl;
    x1[index] = w1 * 100 + w2;
    x2[index] = w3 + w4 / 100.0;
    index++;
    sdin.skipWhite();       // Skip CR/LF.
    if (sdin.fail()) error("bad input");
  }
  cout << F("\nSD-Karte eingelesen und gespeichert") << endl;
  cout << F("\nAusgabe des Feldes") << endl;
  for (byte j = 0; j < Anzahl_Werte; j++) {
    cout << x1[j] << "  " << x2[j] << endl;
  }
  cout << F("\nSD-Karte einlesen Ende") << endl;
}

void setup() {
  Serial.begin(9600);
  cout << F("Programmanfang\n");

  if (!sd.begin()) {
    sd.initErrorHalt();
  }
  cout << F("\nInitialisierung beendet") << endl;
  readFile();
}

void loop() {}

TODO: Warnungen analysieren.

ihr macht mir alles schwerer :confused:

DHTTEMPFILE.print(hour(), DEC);
DHTTEMPFILE.print(":");
DHTTEMPFILE.print(minute(), DEC);
DHTTEMPFILE.print(", ");
DHTTEMPFILE.print((float)DHTTemp);
DHTTEMPFILE.println();
DHTTEMPFILE.close();

um die uhrzeit als binär zu speichern muss ich nur eine zeile entfernen. (DHTTEMPFILE.print(":"):wink:

wie bekomme ich den punkt im (float)DHTTemp durch nen komma ersetzt? bzw wie kann ich aus dem float 2 int machen ala dhttempint1 = 38 und dhttempint2 = 30

DHTTEMPFILE.print(hour(), DEC);
DHTTEMPFILE.print(minute(), DEC);
DHTTEMPFILE.print(";");
DHTTEMPFILE.print(dhttempint1);
DHTTEMPFILE.print(",");
DHTTEMPFILE.print(dhttempint2);

die 2 könnte ich ja dann auch wieder zusammenschreiben

mir wäre der float wert der messwerte lieber.

da openoffice nen CSV importer hat kann ich selbst den spaltentrenner wählen

habs hinbekommen.

hab in der sdfat lib nen beispiel gefunden und das um nen struct Array erweitert

// Functions to read a CSV text file one field at a time.
//
#include <limits.h>
#include <SPI.h>

// next line for SD.h
//#include <SD.h>

// next two lines for SdFat
#include <SdFat.h>
SdFat SD;

#define CS_PIN 53

// example can use comma or semicolon
#define CSV_DELIM ','

File file;

/*
   Read a file one field at a time.

   file - File to read.

   str - Character array for the field.

   size - Size of str array.

   delim - csv delimiter.

   return - negative value for failure.
            delimiter, '\n' or zero(EOF) for success.
*/
int csvReadText(File* file, char* str, size_t size, char delim) {
  char ch;
  int rtn;
  size_t n = 0;
  while (true) {
    // check for EOF
    if (!file->available()) {
      rtn = 0;
      break;
    }
    if (file->read(&ch, 1) != 1) {
      // read error
      rtn = -1;
      break;
    }
    // Delete CR.
    if (ch == '\r') {
      continue;
    }
    if (ch == delim || ch == '\n') {
      rtn = ch;
      break;
    }
    if ((n + 1) >= size) {
      // string too long
      rtn = -2;
      n--;
      break;
    }
    str[n++] = ch;
  }
  str[n] = '\0';
  return rtn;
}
//------------------------------------------------------------------------------
int csvReadInt32(File* file, int32_t* num, char delim) {
  char buf[20];
  char* ptr;
  int rtn = csvReadText(file, buf, sizeof(buf), delim);
  if (rtn < 0) return rtn;
  *num = strtol(buf, &ptr, 10);
  if (buf == ptr) return -3;
  while (isspace(*ptr)) ptr++;
  return *ptr == 0 ? rtn : -4;
}
//------------------------------------------------------------------------------
int csvReadInt16(File* file, int16_t* num, char delim) {
  int32_t tmp;
  int rtn = csvReadInt32(file, &tmp, delim);
  if (rtn < 0) return rtn;
  if (tmp < INT_MIN || tmp > INT_MAX) return -5;
  *num = tmp;
  return rtn;
}
//------------------------------------------------------------------------------
int csvReadUint32(File* file, uint32_t* num, char delim) {
  char buf[20];
  char* ptr;
  int rtn = csvReadText(file, buf, sizeof(buf), delim);
  if (rtn < 0) return rtn;
  *num = strtoul(buf, &ptr, 10);
  if (buf == ptr) return -3;
  while (isspace(*ptr)) ptr++;
  return *ptr == 0 ? rtn : -4;
}
//------------------------------------------------------------------------------
int csvReadUint16(File* file, uint16_t* num, char delim) {
  uint32_t tmp;
  int rtn = csvReadUint32(file, &tmp, delim);
  if (rtn < 0) return rtn;
  if (tmp > UINT_MAX) return -5;
  *num = tmp;
  return rtn;
}
//------------------------------------------------------------------------------
int csvReadDouble(File* file, double* num, char delim) {
  char buf[20];
  char* ptr;
  int rtn = csvReadText(file, buf, sizeof(buf), delim);
  if (rtn < 0) return rtn;
  *num = strtod(buf, &ptr);
  if (buf == ptr) return -3;
  while (isspace(*ptr)) ptr++;
  return *ptr == 0 ? rtn : -4;
}
//------------------------------------------------------------------------------
int csvReadFloat(File* file, float* num, char delim) {
  double tmp;
  int rtn = csvReadDouble(file, &tmp, delim);
  if (rtn < 0)return rtn;
  // could test for too large.
  *num = tmp;
  return rtn;
}


struct Element {
  char uhrzeit[9];
  char datum[9];
  int16_t analog;
  float temp1;
  float temp2;
  float hum;
};

Element messwerte[48];


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

  // Wait for USB Serial
  while (!Serial) {
    yield();
  }
  Serial.println("Type any character to start");
  while (!Serial.available()) {
    yield();
  }
  // Initialize the SD.
  if (!SD.begin(CS_PIN)) {
    Serial.println("begin failed");
    return;
  }
  // Remove existing file.
  SD.remove("READTEST.TXT");

  // Create the file.
  file = SD.open("READTEST.TXT", FILE_WRITE);
  if (!file) {
    Serial.println("open failed");
    return;
  }
  // Write test data.
  file.print(F(
#if CSV_DELIM == ','
               "20:15:23,20.22,55.69,99.99,1023,30.12.16\r\n"
               "17:15:23,20.22,55.69,99.99,1023,30.12.17\r\n"
               "20:15:23,20.22,55.69,99.99,512,30.12.18\r\n"
               "20:15:23,20.22,55.69,99.99,1023,30.12.19\r\n"
#elif CSV_DELIM == ';'
               "36;23.20;20.70;57.60;79.50;01:08:14;23.06.16\r\n"
               "37;23.21;20.71;57.61;79.51;02:08:14;23.07.16\r\n"
#else
#error "Bad CSV_DELIM"
#endif
             ));

  // Rewind the file for read.
  file.seek(0);

  // Read the file and print fields.
  int16_t tcalc;
  float t1, t2, h1;
  int i = 0;
  // Must be dim 9 to allow for zero byte.
  char timeS[9], dateS[9];
  while (file.available()) {
    if (csvReadText(&file, timeS, sizeof(timeS), CSV_DELIM) != CSV_DELIM // 01:08:14
        || csvReadFloat(&file, &t1, CSV_DELIM) != CSV_DELIM     // 20.22
        || csvReadFloat(&file, &t2, CSV_DELIM) != CSV_DELIM     // 55.69
        || csvReadFloat(&file, &h1, CSV_DELIM) != CSV_DELIM     // 99.99
        || csvReadInt16(&file, &tcalc, CSV_DELIM) != CSV_DELIM   // 1023
        || csvReadText(&file, dateS, sizeof(dateS), CSV_DELIM) != '\n') { // 23.06.16
      Serial.println("read error");
      int ch;
      int nr = 0;
      // print part of file after error.
      while ((ch = file.read()) > 0 && nr++ < 100) {
        Serial.write(ch);
      }
      break;
    }

  strncpy(messwerte[i].uhrzeit, timeS, sizeof(messwerte[i].uhrzeit));
  strncpy(messwerte[i].datum, dateS, sizeof(messwerte[i].datum));

  messwerte[i].analog = tcalc;
  messwerte[i].temp1 = t1;
  messwerte[i].temp2 = t2;
  messwerte[i].hum = h1;
    i++;

  }


  file.close();

  for (int i = 0; i < 48; i++)
  {
    Serial.print(messwerte[i].uhrzeit);
    Serial.print(',');
    Serial.print(messwerte[i].datum);
    Serial.print(',');
    Serial.print(messwerte[i].analog);
    Serial.print(',');
    Serial.print(messwerte[i].temp1);
    Serial.print(',');
    Serial.print(messwerte[i].temp2);
    Serial.print(',');
    Serial.print(messwerte[i].hum);
    Serial.println();
  }
  Serial.println("---");


}
//------------------------------------------------------------------------------
void loop() {
}

liefert mir alles

Type any character to start
20:15:23,30.12.16,1023,20.22,55.69,99.99
17:15:23,30.12.17,1023,20.22,55.69,99.99
20:15:23,30.12.18,512,20.22,55.69,99.99
20:15:23,30.12.19,1023,20.22,55.69,99.99
,,0,0.00,0.00,0.00
,,0,0.00,0.00,0.00
,,0,0.00,0.00,0.00
,,0,0.00,0.00,0.00
,,0,0.00,0.00,0.00
,,0,0.00,0.00,0.00

hab gleich alle messwerte in eine datei gepackt und noch datum dazu gehängt

mpl1337: ihr macht mir alles schwerer :/

sorry :(

mpl1337: habs hinbekommen.

um so besser :)

agmue: sorry :( um so besser :)

haha ich dachte das es einfacher ginge :D

mpl1337: haha ich dachte das es einfacher ginge :D

Denkt man dann so :)

Wie schon von anderen vorgeschlagen, würde ich an Deiner Stelle nochmal einen Blick auf das werden, was Du da eigentlich speicherst. Uhrzeit und Datum als Zeichen zu merken, scheint mir wenig sinnvoll. Außerdem braucht Deine Tabelle 32*48 = 1.536 Bytes, wenn ich mich nicht irre. Der UNO hat gerade mal 2.048 Bytes. Bei einer Beschränkung auf Minuten und einer Speicherung mit Ganzzahlen könnte man die Tabelle auf 624 Bytes reduzieren. Ich an Deiner Stelle würde dies zumindest in Erwägung ziehen.