Search code examples
c++csvarduino

Storing the last value of a file from SD card using arduino


I am making a device that moves back and fourth and needs to store its last position so that upon power up, the last stored value can be grabbed from the last line of the file on an SD card, and it can resume operation. This file will then be destroyed and re-written. For this particular application homing and other methods can not be used because it must start in the spot it last was. Due to position tracking via encoder, there is no positional memory otherwise.The file is setup to be a single data column seperated by commas.

Currently I am successfully writing to the SD card as position changes, and reading the entire file to be printed on the Serial monitor. However, I really only need the last value. The length of the file will always be different do to system operation.

I have read a lot of different solutions but none of them seem to work for my application.

I can read the entire file using:

void read_file() {
  // open the file for reading:
  myFile = SD.open("test8.txt");
  if (myFile) {
    Serial.println("test8.txt:");
    // read from the file until there's nothing else in it:
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      String a = "";
      for (int i = 0; i < 9; ++i)
      {
        int j;
        char temp = myFile.read();
        if (temp != ',' && temp != '\r')
        { //a=temp;
          a += temp;
        }
        else if (temp == ',' || temp == '\r') {
          j = a.toInt();
          // Serial.println(a);
          Serial.println(j);
          break;
        }
      }
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test8.txt");
  }
}

This gives me a stream of the values separated by 0 like this:

20050
0
20071
0
20092
0
20113
0
20133
0

Ideally I just need 20133 to be grabbed and stored as an int.

I have also tried:

void read_file_3() {
  // open the file for reading:
  myFile = SD.open("test8.txt");
  if (myFile) {
    Serial.println("test8.txt:");
    // read from the file until there's nothing else in it:
    Serial.println(myFile.seek(myFile.size()));
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

This only returns "1", which does not make any sense to me.

Update: I have found a sketch that does what I want, however it is very slow due to the use of string class. Per post #6 here: https://forum.arduino.cc/index.php?topic=379209.0

This does grab the last stored value, however it takes quite awhile as the file gets bigger, and may blow up memory.

How could this be done without the string class?

void read_file() {
  // open the file for reading:
  myFile = SD.open("test8.txt");
  if (myFile) {
    while (myFile.available())
    {
      String  line_str = myFile.readStringUntil(',');  // string lavue reading from the stream - from , to , (coma to comma)
      int line = line_str.toInt();
      if (line != 0) // checking for the last NON-Zero value
      {
        line2 = line; // this really does the trick
      }
//      Serial.print(line2);
//      delay(100);
    }
    Serial.print("Last line = ");
    Serial.print(line2);
    // close the file:
    myFile.close();
    //    SD.remove("test3.txt");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

Any help would be greatly appreciated!


Solution

  • seek returns true if it succesffuly goes to that position and false if it does not find anything there, like for instance if the file isn't that big. It does not give you the value at that position. That's why you see a 1, seek is returning true that it was able to go to the position (myFile.size()) and that's what you're printing.

    Beyond that, you don't want to go to the end of the file, that would be after your number. You want to go to a position 5 characters before the end of the file if your number is 5 digits long.

    Either way, once you seek that position, then you still need to use read just like you did in your first code to actually read the number. seek doesn't do that, it just takes you to that position in the file.

    EDIT: Since you edited the post, I'll edit the answer to go along. You're going backwards. You had it right the first time. Use the same read method you started with, just seek the end of the file before you start reading so you don't have to read all the way through. You almost had it. The only thing you did wrong the first time was printing what you got back from seek instead of seeking the right position and then reading the file.

    That thing you looked up with the String class is going backward from where you were. Forget you ever saw that. It's doing the same thing you were already doing in the first place only it's also wasting a lot of memory and code space in the process.

    Use your original code and just add a seek to skip to the end of the file.

    This assumes that it's always a 5 digit number. If not then you may need a little bit of tweaking:

    void read_file() {
      // open the file for reading:
      myFile = SD.open("test8.txt");
      if (myFile) {
        Serial.println("test8.txt:");
    
        /// ADDED THIS ONE LINE TO SKIP MOST OF THE FILE************
        myFile.seek(myFile.size() - 5);
    
    
        // read from the file until there's nothing else in it:
        // read from the file until there's nothing else in it:
        while (myFile.available()) {
          String a = "";
          for (int i = 0; i < 9; ++i)
          {
            int j;
            char temp = myFile.read();
            if (temp != ',' && temp != '\r')
            { //a=temp;
              a += temp;
            }
            else if (temp == ',' || temp == '\r') {
              j = a.toInt();
              // Serial.println(a);
              Serial.println(j);
              break;
            }
          }
        }
        // close the file:
        myFile.close();
      } else {
        // if the file didn't open, print an error:
        Serial.println("error opening test8.txt");
      }
    }
    

    See, all I've done is take your original function and add a line to seek the end to it.