The old - read from an SD card question

Hello all
I have an UNO set up, with a TXT file I am reading that contains various text information. Probably about 15 lines.

I don't really understand the in's and out's of strings etc (and I seem to remember strings were bad for Arduinos?). Might be wrong.

My question is, how can I read each line as text that I can reproduce on an LCD screen later on? Bearing in mind these text lines will vary in length (probably up to around 30 character max, but usually a lot shorter).
I have been up all night tinkering... not really got anywhere to be honest. You can laugh at my code.

This is where I left it... it searches for a #, and then uses that as the indication that a line number follows.
Then, it reads the following pair of digits.
Based on that number, it would then retrieve the text if I asked it to.

This is where is all goes a bit wayward. I would have to add another symbol at the end of the sentence to indicate the end of the text. I originally thought of using the next # symbol at the start of the next line. Didn't seem to work that well.

I also am getting madly mixed up with returned values. To identify a # symbol, I am having to search for a returned value of 35. However, when it returns the number values, I have to deduct 48 to get the correct number.

Its a mess! It did start all tidy. Constant tweaking has ruined it.
This was searching for line 3

Ideally, it would read 15 lines and store them in 15 EEPROM addresses, but the UNO is gonna run out of memory before that ever happens.

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

File myFile;

int hash1;
int data1;
int data2;
int data3;


byte i;

//----------------------------------------------------------------------------------------------------

void setup()
{
 
  Serial.begin(9600);                                                                               // Open serial communications and wait for port to open:
  while (!Serial) {
  }

  Serial.print(F("Checking for SD card... "));

  if (!SD.begin(4)) {
    Serial.println(F("No SD card present, using memory stored/default data"));
    return;
  }
  Serial.println(F("Found SD card"));

  delay(80);

  // Open the file for reading:
  myFile = SD.open("SiteData.txt");
  if (myFile) {
    Serial.println(F("SiteData.txt:"));
    Serial.println(F(" "));

    // read from the file until there's nothing else in it:
    while (myFile.available()) {

       //Serial.write(myFile.read());
  
       hash1=myFile.read(); //Serial.print(char(hash1));               //Search for a hash                                                           
                              
       if (hash1==35) {data1=(myFile.read());Serial.print(char(data1));data2=(myFile.read());Serial.print(char(data2));}   //If a hash is found, get the line number of the data
             
       if (((data1-48)*10+(data2-48))==3) {for (i=0;i<=60;i++) {data3=(myFile.read());Serial.print(char(data3)); if (data3==35) {break;}}}
       
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println(F("error opening SiteData.txt"));
  }

void loop() {}

First in your line

if (hash1==35) {data1=(myFile.read());Serial.print(char(data1));data2=(myFile.read());Serial.print(char(data2));} //If a hash is found, get the line number of the data

When Hash is found, you are not reading line numbers, your are simply reading the next 2 characters following the hash.

If you have some confusion concerning the numbers of the characters, you can refer to:

if (hash1==35) {data1=(myFile.read());Serial.print(char(data1));data2=(myFile.read());Serial.print(char(data2));}   //If a hash is found, get the line number of the data

Whether or not the code is doing what is required, the first thing that I would do is to make it readable

if (hash1 == 35)
{
  data1 = myFile.read();  //If a hash is found, get the line number of the data
  Serial.print(char(data1));
  data2 = myFile.read();
  Serial.print(char(data2));
}

Well... like I said... it was readable until I sat up most of the night and tinkered around with it.
I would totally ignore the code in-between opening and closing the SD card file and we start again.

Yes.. it reads the hash, then reads the next two values as they would always be the line number.

Unscramble this line as well

if (((data1-48)*10+(data2-48))==3) {for (i=0;i<=60;i++) {data3=(myFile.read());Serial.print(char(data3)); if (data3==35) {break;}}}

and promise yourself that you will format code properly in future. {} round code blocks, even if the block only has one statement in it, and each { and } on its own line.

if (((data1 - 48) * 10 +( data2 - 48)) == 3)
{
  for (i = 0; i <= 60; i++)
  {
    data3 = (myFile.read());
    Serial.print(char(data3));
    if (data3 == 35)
    {
      break;
    }
  }
}

OK... LIKE I SAID... Its a mess. What was written at 4.34am isn't going to make sense. Sorry if I don't write perfectly pretty code all the time ::slight_smile:

This is where I am now... and it works well so far.
Its sending each line to the EEPROM if an SD card is present. Then I can easily read it back.
I have had to add a hash to the end of each line to allow it to find the end of the sentence. If there any way of getting it to ignore the spaces at the end of a row of text? I am assuming it jumps to the next line when it sees more than one space?
It would be nice if it simply read the entire row of 30 characters, spaces included. Then I could do away with the hash at the end.

void setup()
{
 
  Serial.begin(9600);                                                                               // Open serial communications and wait for port to open:
  while (!Serial) {
  }

  Serial.print(F("Checking for SD card... "));

  if (!SD.begin(4)) {
    Serial.println(F("No SD card present, using memory stored data"));
    return;
  }
  Serial.println(F("Found SD card"));

  delay(80);

  // Open the file for reading:
  myFile = SD.open("SiteData.txt");
  if (myFile) {
    Serial.println(F("SiteData.txt:"));
    Serial.println(F(" "));

    // read from the file until there's nothing else in it:
    while (myFile.available()) {

       hash1=myFile.read();                              //Search for a hash
         if (hash1==35) {                                //If a hash is found, get the line number of the data
             Serial.print(char(hash1));                  //Display the hash+line number
             data1=(myFile.read()-48);             
             data2=(myFile.read()-48);
                      
             data3=((data1*10)+data2);                   //Calculate which line number we are reading
             Serial.print(data3);
             
             EEPROM.write((data3*30)-30,data3);          //Store the line number at its own line number address *30 then -30 (because there is no zero line number)
             
             for (i=0;i<=12;i++) {                       //Skip the next 12 spaces/letters as they are the reference text.  Just print them to the serial monitor
                 Serial.print(char(myFile.read()));
                 }   
                    
             for (i=0;i<=30;i++) {                       //Read the next 30 letters or numbers and store them incrementally from the known line number start point
                 data1=(myFile.read());               
                 if (data1==35) {break;}                 //If it finds another hash, then exit 
                 Serial.print(char(data1));          
                 EEPROM.write(((data3*30)-30)+i,data1);     
             }
             Serial.print('\n');                         //New line         
         }
         
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println(F("error opening SiteData.txt"));
  }

Then I could do away with the hash at the end.

You would be better off using a different character for the start and end markers. Take a look at Serial input basics - updated Don't be put off by the title. The techniques described are equally applicable to reading from an SD card.

Right then. Problem solved I think

My txt file has 16 numbered rows. Starting with #1. #2. #3 etc.
The next 12 letters after that describe what that line should contain.
Then there are up to 30 characters of data.

My code looks through the file, finds a hash and prints the line number and description (but does not store them). Then stores the following >30 characters in EEPROM.
Each EEPROM start position is derived from the line number.
If it encounters the hash of the next line before the full 30 characters, it grabs that read position, knocks it back one place and allows the routine to continue.
The step back of one place then allows it to find the hash again that it just used as the end of the previous data line.

void setup()
{
 
  Serial.begin(9600);                                                                               // Open serial communications and wait for port to open:
  while (!Serial) {
  }

  Serial.print(F("Checking for SD card... "));
  Serial.println();

  if (SD.begin(4)) {
    
     Serial.println(F("Found SD card"));
     delay(50);

  // Open the file for reading:
  myFile = SD.open("SiteData.txt");
  if (myFile) {
    Serial.println(F("SiteData.txt:"));
    Serial.println(F(" "));

    // read from the file until there's nothing else in it:
    while (myFile.available()) {

       hash1=myFile.read();                               //Search for a hash
         if (hash1==35) {                                 //If a hash is found, get the line number of the data
             Serial.print(char(hash1));                   //Display the hash+line number
             data1=(myFile.read()-48);             
             data2=(myFile.read()-48);
                      
             data3=((data1*10)+data2);                    //Calculate which line number we are reading
             Serial.print(data3);
                       
             EEPROM.write((data3*30)-30,data3);           //Store the line number at its own line number address *30 then -30 (because there is no zero line number)
             
             for (count=0;count<=12;count++) {            //Skip the next 12 spaces/letters as they are the reference text.  Just print them to the serial monitor
                 Serial.print(char(myFile.read()));
                 }   
                    
             for (count=0;count<=30;count++) {            //Read the next 30 letters or numbers and store them incrementally from the known line number start point
                 readposition=(myFile.position());        //Get the current read position
                 data1=(myFile.read());                          
                 if (data1==35) {                         //If it finds another hash at the beginning of the next line, then process ready for next line data   
                  myFile.seek(readposition-1);            //Move the read position back one byte
                  break;                                  //Exit this line read
                 }                  
                 Serial.print(char(data1));          
                 EEPROM.write(((data3*30)-30)+count,data1);     
             }
             if (count==30) {Serial.print('\n');}     //If it doesn't move to the next line due to finding the next hash, then force a new line 
             if (data3>=16) {break;}       //If all 16 files are found, then exit
         }
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println(F("error opening SiteData.txt"));
  }

  }else {Serial.print(F("No SD card present, using memory stored data"));}