Datei von SD-Card lesen!?

Hi, mein Name ist Ronald und ich bin nicht nur hier neu sondern auch in der Welt des Arduino! ;)

Nachdem ich mich nun schon ein paar Tage mit den Basic's der Arduinoprogrammierung rumgeschlagen habe komme ich nicht weiter.

Für ein Schulprojekt sollen innerhalb eines Zeitrahmens (Startzeit - Stopzeit) aller x Minuten (Intervall) für die Dauer von x Minuten (Dauer) Lampen eingeschaltet werden. Das ganze aber für bis zu 8 Kanälen.

Die Schaltzeiten etc. sollen aus einer Datei von einer SD-Karte eingelen werden und in ein Array 8x4 gespeichert werden.

Die Datei sollte ungefähr so aussehen:

#Startzeit;Endezeit;Intervall;Dauer
830;1600;25;5
830;1630;40;15
900;1700;10;5
1030;1700;20;5
0;0;0;0
0;0;0;0
0;0;0;0
0;0;0;0

Am Beispiel "830;1600;25;5" es soll zwischen 8,30Uhr und 16,00Uhr aller 25 Minuten 5 Minuten eingeschaltet werden. Zeilen mit 0,0,0,0 Kanal nicht aktiv!

Das Format für die Zeiten ohne Komma habe ich bewusst so gewählt da ich die Uhrzeit in "Minuten nach Mitternacht" umrechne und diese so leichter weiterverarbeiten kann. Das habe ich soweit auch schon alles getestet nur mit dem Einlesen der Datei habe ich keinen "Schimmer"! Für einen Schubs in die richtige Richtung und ein bisschen Hilfe wäre ich Euch dankbar! ;)

Welche SD Lib sollte ich verwenden? SD oder SdFat? Hat jemand ein gut dokumentierte Beispiel für die von mir benötigten Funktionen?

Grüße, Ronald

Es geht beides, aber SdFat hat die praktische fgets() Funktion mit der man eine Zeile auf einmal in einen Puffer einlesen kann.

Ich mache es wie folgt, aber das ist bewusst etwas weiter ausgeholt. Es geht auch einfacher.

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;
}

Man übergibt den Dateinamen, den Puffer für die Zeile, die Größe des Puffers und die gewünschte Zeilen-Nummer. Die Zeilen-Nummer fängt bei 1 an! Nicht bei 0. Das kann man aber auch ändern wenn man "count" mit 0 initialisiert.

Die Funktion liefert false zurück wenn man am Ende der Datei ist. Also z.B. Zeile 10 auslesen will und die Datei nur 5 Zeilen hat.

Verwendung dann z.B. so:

const int STRING_BUFFER_SIZE = 21;
char stringBuffer[STRING_BUFFER_SIZE];

SdFat sd;
SdFile file;

...

readLineFromSD("test.txt", stringBuffer, sizeof(stringBuffer), 1)

int value1 = atoi(strtok(stringBuffer, ";"));
int value2 = atoi(strtok(NULL, ";"));
int value3 = atoi(strtok(NULL, ";"));
int value4 = atoi(strtok(NULL, ";"));

Der Rückgabe-Wert wird hier nicht abgefragt und man verlässt sich einfach darauf dass es geklappt hat

Das kann man dann auch in einer Schleife machen. Oder das Parsen in eine Funktion auslagern und die Daten in ein struct schreiben.

Ich würde dir hier auch ein Array aus structs für die Daten empfehlen. Das ist wesentlich angenehmer als ein zwei-dimensionales Array

Hi, und vielen Dank für Deine schnelle Hilfe!
Ich habe das jetzt mal wie folgt umgesetzt und siehe da soweit funktioniert es sogar! ;D
(Auch wenn ich es noch nicht zu 100% verstehe, muss halt noch ein bisschen lernen.)

#include <SdFat.h>

// SD chip select pin
const uint8_t chipSelect = 10; // 4 Ethernet shield

const int STRING_BUFFER_SIZE = 21;
char stringBuffer[STRING_BUFFER_SIZE];

SdFat sd;
SdFile file;

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;
}


void setup() {
  // put your setup code here, to run once:

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  //  while (!Serial) {
  //    ; // wait for serial port to connect. Needed for Leonardo only
  // }

  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();

  for (int i = 1; i < 9; i++)
  {
    readLineFromSD("data.txt", stringBuffer, sizeof(stringBuffer), i);

    int value1 = atoi(strtok(stringBuffer, ";"));
    Serial.print("  Start: "); Serial.print(value1);
    int value2 = atoi(strtok(NULL, ";"));
    Serial.print("  Stop: "); Serial.print(value2);
    int value3 = atoi(strtok(NULL, ";"));
    Serial.print("  Intervall: "); Serial.print(value3);
    int value4 = atoi(strtok(NULL, ";"));
    Serial.print("  Dauer: "); Serial.println(value4);
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

Alle meine Daten werden sauber ausgelesen und dargestellt!
Jetzt muss ich sehen wie ich das am besten in Array(s) packe und dann entsprechend weiterverarbeite!
Grüße,
Ronald

struct Data_t { int start; int stop; int interval; int duration; };
Data_t data[8];

Dann kann man z.B. das machen:

void setup()
{
  for(int i = 0; i < sizeof(data) / sizeof(Data_t); i++)
  {
    if(readLineFromSD("data.txt", stringBuffer, sizeof(stringBuffer), i + 1))
       parseData(&data[i]);
  }
}

void parseData(struct Data_t* data)
{ 
  data->start = atoi(strtok(stringBuffer, ";"));
  data->stop = atoi(strtok(NULL, ";"));
  data->interval = atoi(strtok(NULL, ";"));
  data->duration = atoi(strtok(NULL, ";"));
}

Eventuell kann man bei parseData noch den Puffer als Parameter übergeben. Ist aber hier nicht unbedingt nötig.

Also ich habe es auch hinbekommen, aber...

Was Du hier so hinzauberst ist ja um Welten eleganter! :o ???

Respekt und Danke, ich habe aus Deinen Antworten mega-viel lernen können!

Werde mal versuchen das ganze auf Deine Variante umzustellen, das ist ja nur halb so viel Code wie ich produziert habe!

Grüße und Danke, Ronald