Parsing data from .txt file on an SD card.

Hello,

I am currently working on a project which involves reading data stored on an SD card. I'm trying to use some of the program from example 5 that is listed on the Serial Input Basics tutorial here: Serial Input Basics - updated - Introductory Tutorials - Arduino Forum.

I'm trying to make a program which parses through the data stored on the card and will save the user ID and Passwords to an array.

In order to do this however I first need to feed all of the data stored on the SD card and place it through the function which parses data. But I'm stuck on this step.

I'm assuming that you have to put some sort of data into the array called "receivedChars", but I'm kind of stuck on how to go about doing it from data stored on a .txt file on an SD card.

I've tried creating a string and saving the data from the .txt file into the string, but that didn't work. So

So far, the code that I have is:

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

// declare files to read/write from
File userData;

const int chipSelect = 7;

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

void setup() {
  Serial.begin(9600);
  pinMode(chipSelect, OUTPUT);

  // Initialize the SD Card
  if (SD.begin()) {
    Serial.println("SD card is ready to use.");
  }
  else {
    Serial.println("SD card initialization failed");
    return;
  }

  userData = SD.open("userinfo.txt");
  if (userData) {
    Serial.println(" ");
    // Reading the whole file
    while (userData.available()) {
      Serial.write(userData.read());
    }
    userData.close();
  }
  else {
    Serial.print("error opening ");
    Serial.println(userData);
  }

}

void loop() {

  int stringLen = userDataString.length();

  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    parseData();
    showParsedData();
    newData = false;
  }
}

//============

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//============

void parseData() {      // split the data into its parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ",");     // get the first part - the string
  strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  integerFromPC = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
  Serial.print("Message ");
  Serial.println(messageFromPC);
  Serial.print("Integer ");
  Serial.println(integerFromPC);
  Serial.print("Float ");
  Serial.println(floatFromPC);
}

My file stored on the SD card is called userinfo.txt and looks like this:

user<Username>
userid<1,2,3,4>
password<4,5,6,7>

Thank you!

i'm having a hard time following your code.

of course you have a "chicken and egg" problem where I believe you want to read info from the SD card, but need to write info there first.

seems that you need to process several types of commands from the Serial interface. One might be to read info from the SD card. another is to read info entered thru the Serial interface and write it to the SD card. Don't forget a command to enable debugging.

strings of chars from both the Serial interface and SD card need to be processed. (I don't understand how to sent control characters (e.g. \n, NULL) thru the Arduino IDE serial monitor, but perhaps a period "." would suffice).

i think the format of you user info should be a single comma separated string: "123,password,username". don't understand your comments about arrays.

i suggest you work on receiving command strings thru the Serial interface. as suggested the string can be period terminated, but a command can be a single letter (e.g. 'r' for read SC, 'n' for new user info).

new user info might be "n123,mypassword,username.". starting at the 2nd character, separate the ID, password and username. Then work on code to write that to the SD card.

the string written to the SD card could be terminated with a NULL.

Thank you,

I'm writing this code as a test before incorporating it into a much larger code that I'm working on. I have arrays in that code which store the username and password as four character arrays.

I don't need to write to the SD card with this code, just read from it. I created the file on my computer and saved it to the card from there.

it's not clear to what this problem is. i don't think you're at the point of reading a string from the SD code, even though there is code to do so. I think you're trying to process a string from the Serial interface. But it's not clear if you the code has a problem reading a complete string or using strtok to separate it into sub-strings.

i love using strtok, but am not sure what this particular issue is

Is there data for multiple users in that file? If so, use the csv format mentioned above. If not, you can use the existing format (or change to csv).

The first step in both scenarios is to read a line. You can use readBytesUntil for that or use same approach as in Robin's tutorial (recvWithEndMarker; change the Serial to userData)

You will need to indicate what the line ending in the file is; usually "\r\n" (DOS/Windows) or "\n" (Linux); not sure about the Mac (I think it's nowadays "\n", it used to be "\r"). If you don't know, you can use below code to test

void setup() {
  Serial.begin(9600);
  pinMode(chipSelect, OUTPUT);

  // Initialize the SD Card
  if (SD.begin())
  {
    Serial.println("SD card is ready to use.");
  }
  else
  {
    Serial.println("SD card initialization failed");
    return;
  }

  userData = SD.open("userinfo.txt");
  if (userData)
  {
    Serial.println(" ");
    // Reading the whole file
    while (userData.available())
    {
      char ch = userData.read();
      if (ch < 0x10)
      {
        Serial.print("0");
      }
      Serial.print(userData.read(), HEX);
      Serial.print(" ");
    }

    userData.close();
  }
  else {
    Serial.print("error opening ");
    Serial.println(userData);
  }
}

void loop()
{
}

The output will look like (your example)

75 73 65 72 3C 55 73 65 72 6E 61 6D 65 3E 0D 0A 75 73 65 72 69 64 3C 31 2C 32 2C 33 2C 34 3E 0D 0A 70 61 73 73 77 6F 72 64 3C 34 2C 35 2C 36 2C 37 3E

If you see the sequence 0D 0A (as above), your line ending is "\r\n", if you only see 0D, it's "\r" and if you only see 0A the line ending is "\n".

Now you can read a line till the line ending; the below caters for "\r\n" or "\n". In case of the former, the "\r" can be stripped off later.

// line buffer for 80 characters
char line[81];

void setup() {
  Serial.begin(9600);
  pinMode(chipSelect, OUTPUT);

  // Initialize the SD Card
  if (SD.begin()) {
    Serial.println("SD card is ready to use.");
  }
  else {
    Serial.println("SD card initialization failed");
    return;
  }

  userData = SD.open("userinfo.txt");
  if (userData)
  {
    while (userData)
    {
      Serial.println(" ");
      // clear the linebuffer
      memset(line, '\0', sizeof(line));
      // read a line fromfile
      userData.readBytesUntil('\n', line, sizeof(line) - 1);
      // and  print it
      Serial.println(line);
    }
    userData.close();
  }
  else {
    Serial.print("error opening ");
    Serial.println(userData);
  }

}

Now you can parse the line.

Code compiles but not tested.