Go Down

Topic: How to get the last counter value from txt file from SD card (Read 6900 times) previous topic - next topic

ch0zen

Hello everyone!

I try to count impulses from water meter.
I also attached an Sd card to Arduino Nano to backup counts.

The text file is like this (4 digits):

5742,
5743,
5744,

The code for writing the counts is:

Code: [Select]

myFile.print(buttonPresses1);
     myFile.println(",");
     // myFile.print(buttonPresses1);       // printing without <CR>


I added this "," comma sign while struggling within, it's not that necessary. I also tried <CR>.

If the Arduino is out of the power, I need to read the LAST count to continue with. 

I tried this code to get the last line:

Code: [Select]


File dataFile = SD.open ("cold.txt", FILE_READ);
if (dataFile)
  {
    while (dataFile.available())
    {
      String  line = dataFile.readStringUntil(',');
      lcd.print(line);
      Serial.print(line);
//      delay(10);
    }
Serial.println("The last line is  ");   
Serial.println(line);
lcd.print("The last line is  ");   
lcd.println(line);   

    dataFile.close();

}
}


The problem is - I can see the whole counts in comport monitor as well as on LCD. But I could not get this LAST digit to work with. I need to setup the initial starting point for the meter after power failure.

Comport Monitor says:
Code: [Select]

Initializing SD-card...
5739
5740
5741
5742
5743
5744
The last line is 



Could anyone help me out with this issue?
Kindly appreciated in advance!
Thank you,
Dmitry

Whandall

You could try to use seekEnd to position to the last number
Code: [Select]
datafile.seekEnd(-7); // 5 chars + cr + lf
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

ch0zen

Sorry, could not compile the sketch

Code: [Select]


SD_card_RW_test:67: error: 'class SDLib::File' has no member named 'seekEnd'

      String line = dataFile.seekEnd(-7);            // 5 chars + cr + lf

                             ^

exit status 1
'class SDLib::File' has no member named 'seekEnd'



Is there a way to use that String, which I get?
Or, the last value of "line" is empty? Why I cannot see it after the "The last line is  "

Whandall

#3
Feb 15, 2016, 11:49 am Last Edit: Feb 15, 2016, 11:54 am by Whandall Reason: added alternative code
The SdFat library I'm using has that functionality, I did not regard it special, sorry.

You should be able to use something like the following for the positioning.
Code: [Select]
dataFile.seek(dataFile.size()-7);
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

ch0zen

Thank you!

I have figured it out, no problem. But still have the same problem.

I installed the SdFat library, have a lot of inclused added. But I use the stanard SD library to work with SD card, I suppose now it conflicts with SdFat. I have got a lot of errors. 


What I could not understand - why the LAST value is <nothing>?
I tried this, reading the string until "," and converting the string into integer.

Code: [Select]

 
String  line_str = dataFile.readStringUntil(',');
          int line = line_str.toInt();
          Serial.print(line);



I tried to remove "\n" in the incoming text file like this:

5739,5740,5741,5742,5743,5744,

The parsing works perfectly, except the last value while I'm out of "while" cycle.

What I am doing wrong?

Thanks

Whandall

The way you are reading (up to a ',' and then checking for available()) always gives you a cr-lf as the last line.

I would position to the interesting 4 characters and read them to a buffer, or convert them directly to an int.
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

ch0zen

Thank you for your help.
I suppose I have found the solution.

As I figured out, the last line always should be "0"
So what I needed to get was the previous value.

I did this code and looks like it works:

Code: [Select]


File dataFile = SD.open ("cold.txt", FILE_READ);
if (dataFile)
  {
    while (dataFile.available())
    {
      String  line_str = dataFile.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
              }
     
      // String  line = dataFile.readString();
      // lastline = dataFile.parseInt();

      lcd.print(line2);
      Serial.print(line2);
      delay(100);
    }
    lcd.clear();
Serial.print("Last line = ");   
Serial.print(line2);
lcd.print("Last line = ");   
lcd.print(line2);   

    dataFile.close();

Whandall

I think it is more a hack, than a solution. So from now on you have to write two last lines.

If you are happy with it...
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

ch0zen

Well, my skills on Arduino are limited now, I cold get back to that code later..
It's not clear to me why TWO lines?
The last line is always zero
So I have to pick up the previous one, which !=0.
As I can see, the code is working.

Thank you
Dmitry

ch0zen

You mean, I have to write to the SD file twice?
No. The writing procedure remains the same and the rest of the code reads the last value perfectly.. as I can see it.
Thank you once again!

Whandall

If the last value has to be a 0 because you need it to find the second last,
writing a new second last needs another 0, don't you think so?

You were not able make the positioning work on read, so I doubt you will use it on write.

Example:
....
 '5545,'
'0,'

 + new value (5654) via appending
....
'5545,'
'0,'
'5644,'
'0,'

wasting a lot of space (about same as used).
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

ch0zen

I use the previous code for writing, no nulls added.
Writing code makes these numbers on Sdcard:

5742,
5743,
5744,

I suppose that command

String  line_str = dataFile.readStringUntil(',');

always returns "0" at the end. I read about this command and the manual says, that it returns a value when bumps into termination symbol (comma in my case) AND stops by timeout. This timeout is 1 sec by default. I supposed the last "0", which I always got in the end, is that timeout exit (sorry my clumsy explanation). To make sure, I changed the default timeout to 10000msec and saw the "0" appears in Comport monitor after that time.

So the monitor showed me:

5739,5740,5741,5742,5743,5744, (after timeout) 0

That's why I decided to take not the last value of what the function returns, but the pre-last.

Maybe my solution is clumsy (you've said it is hardly a solution), but I can't where the hack is.
As I mentioned,  my skills in programming are limited.

As I can predict, to use this Sdfat lib, I should re-write all the code regarding reading/writing to/from SD card, because it conflicts with the standard SD lib.

Thank you for your help,
I can provide the whole sketch if you're still interesting in.
Anyway, I have made a fail-safe logger, which saves counter counts on SD and reads them back if there is a shutdown or reboot of Arduino.

fat16lib

Quote
As I can predict, to use this Sdfat lib, I should re-write all the code regarding reading/writing to/from SD card, because it conflicts with the standard SD lib.
The standard SD library is just a wrapper for a very old version of SdFat.  You can use SdFat without rewriting your program but you won't get new SdFat features.

Just replace
Code: [Select]
#include <SD.h"

With:
Code: [Select]

#include <SdFat.h>
SdFat SD;


SdFat has a number of examples for reading CSV files.

ReadCsvArray

ReadCsvFields

readCSV

You can also use readStringUntil() but it returns less information than readField

Code: [Select]


/*
 * Read a file one field at a time.
 *
 * file - File to read.
 *
 * str - Character array for the field.
 *
 * size - Size of str array.
 *
 * delim - String containing field delimiters.
 *
 * return - length of field including terminating delimiter.
 *
 * Note, the last character of str will not be a delimiter if
 * a read error occurs, the field is too long, or the file
 * does not end with a delimiter.  Consider this an error
 * if not at end-of-file.
 *
 */
size_t readField(File* file, char* str, size_t size, const char* delim);



Go Up