Problem reading data in columns in SD

I want to read data into an SD that is separated by commas. But I don't quite know how to read them correctly. I have done a test with a .txt file that contains the following data:

0.000000, 0.000000
2774.000000, 0.012031
5368.915527, 0.023067
7854.816895, 0.032917
10677.939453, 0.042822
12990.595703, 0.049778
15493.816406, 0.056391

However, the serial monitor returns this:

X Y
0.000000, 0.000000
-1520.967163, 0.012031
1073.948120, 0.023067
-735.117615, 0.032917
2088.004639, 0.042822
105.693802, 0.049778
-1686.052490, 0.056391

It seems that the Y values ​​do read them well but with the X values ​​it shows strange things. Could someone tell me why this happens?

The code is the following:

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

#define SD_CS 5

File myFile;

void setup() {
  Serial.begin(115200);

  Serial.print("Initializing SD card...");

  if (!SD.begin(SD_CS)) {
    Serial.println("SD initialization failed!");
    while (!SD.begin(SD_CS)) {}
  }

  myFile = SD.open("/test.txt");
  Serial.print("File Open (READ):");
  Serial.println(myFile);

  myFile.seek(0);

  Serial.print("X        ");
  Serial.println("Y");

  while (myFile.available()) {
    float X = myFile.parseFloat();
    float Y = myFile.parseFloat();
    Serial.print(X, 6);
    Serial.print(", ");
    Serial.println(Y, 6);
    delay(500);
  }

}

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

}

Is there both a Carriage Return and Linefeed at the end of each line or just a Linefeed ?

The data in the SD I have previously written in another test. For that I have made a function in which I have written the following:

myFile.print(X,6);
myFile.print(", ");
myFile.println(Y,6);

And every time I want to write a new data I call the function.

Try using

myFile.print(X,6);
myFile.print(", ");
myFile.print(Y,6);
myFile.print('\n');

to create the file so that there is only one line ending character present

I just tried it and no... it doesn't work. The X values ​​keep coming out wrong and the Y values ​​keep going right. I do not know what it could be. I've also tried putting a comma right after the Y value. But that doesn't work either.

You have no line synchronization. Try reading

  while (myFile.available()) {
    float X = myFile.parseFloat();
    float Y = myFile.parseFloat();
  while (not isdigit(myFile.read() ));

or something along those lines...

1 Like

The problem is, believe it or not, integer overflow.

The .parseFloat() function puts all of the digits into a 'long int' before applying the decimal point. The value 2774000000 is too big to fit in a 'long int' so it overflows to -1520967296. That is then converted to 'float' which truncates the mantissa to 26(?) bits. That truncated value is divided by 1000000.0 to place the decimal point. The result is -1520.967163.

You might get correct-ish values if you use:

    long Xi = myFile.parseInt();  // Integer part
    long Xd = myFile.parseInt();  // Decimal part
    float X = Xi + (Xd / 1000000.0);

The results should be correct to 6 or 7 significant digits but you aren't going to fit 11 significant digits in a 32-bit 'float'.

2 Likes

I wonder who made that idiotic decision.

I would never have considered using the function, so I looked at the Arduino reference page to see if there were any warnings about this ridiculous limitation, but there aren't any.

Try something along these lines

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

File file;
const byte csPin = 4;
char * filename = "test2.txt";
char buffer[50];

void setup()
{
  Serial.begin(115200);
  initSD();
  openFile();
  readFile();
}

void loop()
{
}

void initSD()
{
  if (!SD.begin(csPin))
  {
    Serial.println(F("begin error"));
    while (1);
  }
  Serial.println(F("begin success"));
}

void openFile()
{
  file = SD.open(filename, FILE_READ);
  if (!file)
  {
    Serial.println(F("error opening file"));
    while (1);
  }
}

void readFile()
{
  if (file)
  {
    Serial.println(F("open success"));
    while (file.available())
    {
      readLine();
      parseLine();
    }
    file.close();
  }
}

void readLine()
{
  byte index = 0;
  char inChar = '\0';
  while (inChar != '\r')
  {
    inChar = file.read();
    buffer[index++] = inChar;
    buffer[index] = '\0';
  }
}

void parseLine()
{
  char * end;
  float x = atof(strtok(buffer, ","));
  float y = atof(strtok(NULL, ","));
  Serial.print(x, 6);
  Serial.print('\t');
  Serial.println(y, 6);
}
1 Like

Thanks for the explanation. So, if I write smaller numbers or numbers with fewer decimal places to the SD card, there shouldn't be an overflow, right?

Sounds plausible. Certainly worth a try.

Thanks for the proposal! I will try this too.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.