How to read waypoints from SD card using NeoGps

The program I have created runs out of space quickly as I add more waypoints to the array. In turn this causes my program not work properly. I have now saved all my waypoints on an SD card. I can read the data from the file using the SdFat readwrite sketch. What I have spent hours and days on, is being able to read one waypoint at a time. The main goal is to start with the first waypoint on the card. Once I reach the first waypoint, move to the next. Rinse and repeat until the end of file.

Included is my current program to show what my program looks like. However, it does not have the SD card information included. I've just been trying the simple readwrite sketch to try and meet my end state. If any kind soul reading this could throw me a bone, then the drywall I keep putting my fist through will thank you as well as I.

MoBot_v0.4.3.ino (5.49 KB)

What I have spent hours and days on, is being able to read one waypoint at a time.

With success?

If any kind soul reading this could throw me a bone, then the drywall I keep putting my fist through will thank you as well as I.

Creating a function, getNextWaypoint(), should really be pretty simple. Calling that function at the appropriate place should really be pretty simple.

The function should read from the file, storing the data in a char array, until it finds an end-of-record marker. When that happens, it should use strtok() to parse the data, getting lat and lon as separate values (converting them from strings to the appropriate type, in the process).

What, exactly, do you need help with?

Thank you PaulS for the response. I feel like stalker, because almost all the google searches I've done on this issue, you have responded in some way. I've tried many of your examples using strtok without success. I've researched how to use strtok, but I still don't have a good understanding. I'm so far in the deep end of the pool with my C++ knowledge, I need a lifeguard....

This is as far as I can go. If I don't iterate i++ and just do i, I can stay on one line. While I put this post out, I've been reading a lot and learning more efficient ways to do this. I just need a better understanding, (i.e. re re terms) to get a good grasp on this. My coding skills be weak yo.

#include <SPI.h>
#include <SdFat.h>

SdFat SD;

const uint8_t chipSelect = 10;
File myFile;

// WORKING VALUES
String buffer;

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

  // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) {
      //Serial.println(F("Card failed, or not present"));
      while (1) ;
    }
    //Serial.println(F("card initialized."));

  // Open up the file we're going to log to!
  myFile = SD.open("GPSData.txt");

  ReadSDCard();
}


void loop() 
{
  
}

void ReadSDCard()
{
  while (myFile.available()) {
    buffer = myFile.readStringUntil('\n');
    for (int i = 0; i < 1; i++) {
    Serial.println(buffer); //Printing for debugging purpose         
    //do some action here 
    }
  }
  myFile.close();
}

GPSData.txt (8.41 KB)

Since you are determined to piss away resources using the String class, you need to forget about using the string functions to parse the data.

The String class has indexOf() and substring() and toFloat() methods that allow you to extract meaningful data from the String instance you get from readStringUntil().

Could you please show me the correct and efficient way of what I am trying to do. Or point me to a post/website that would help.

Could you please show me the correct and efficient way of what I am trying to do.

The "correct and efficient" way involves a LOT of changes to your code.

The quick and dirty way involves a lot fewer changes.

bool getWayPoint()
{
   if(myFile && myFile.available() > 0) // If there is a file and it has data...
   {
      String wayPoint = myFile.readStringUntil('\n'); // Get a record
      if(wayPoint.length() > 0) // If there String has data...
      {
         parseWayPoint(wayPoint);
      }
      return true;
   }
   else
      return false;
}

parseWayPoint() will use indexOf() and substring() and other functions to get the latitude and longitude information from the String. Exactly HOW it does that will depend on exactly how you need the latitude and longitude information.

Do you need them as doubles? As floats? As strings? As Strings?

The indexOf() method takes a character or String, and determines if it exists in the String that it is called on.

   int openBracePos = wayPoint.indexOf('{');
   int commaPos = wayPoint.indexOf(',', openBracePos); // Find the comma after the open brace

With the open brace position and the comma position, you can extract a substring (stupid name, since what you extract isn't a string) that contains the latitude information.

   latitudeString = wayPoint.substring(openBracePos+1, commaPos);

(This assumes that latitudeString is a global String.)

You can do something similar to get the longitude String, except that you already know where that String starts (commaPos+1). It ends at the position where the '}' is.

If I knew just how you needed the latitude and longitude information, I could tell you how to get the Strings into that format.

By the way, you'd need to open the file somewhere (probably in setup()) and close the file (probably in loop() when getWayPoint() returned false).

You'd call that function whenever you needed the next waypoint.

First, let me thank you for the code you posted. I’ve reread one of your post on here PaulS, about using strtok(). So today I read quite a bit about using it over strings. Now I understand why you said, “If you’re determined to piss away your resources”. Here is the code you offered someone on another post. I’ve been working with my new found knowledge, on how to read from an SD card using this. Would it make more sense to remove the {}’s from the waypoints leaving only the comma’s?

Also, you asked, “If I knew just how you needed the latitude and longitude information, I could tell you how to get the Strings into that format”. I honestly don’t know. What would you recommend based on my sketch, be the best way to read the lat/long’s from the SD card?

char data[] = "0 23 45 99";

char *token = strtok(data, " ");
while(token)
{
  int iVal = atoi(token); // iVal will be 0, 23, 45, and 99

  token = strtok(NULL, " "); // Use NULL to keep parsing original data
}

I honestly don't know.

You must have some reason for wanting to read the data from the SD card. If it were me, it would be so that I could compare the lat/lon values from a GPS to the lat/lon values on the SD card.

If that were the case, I'd look at the code that got the lat/lon values from the GPS, to see what type the values are. Then, I could tell you what the type was, and you could write the function to convert the strings/Strings containing the data read from the file to that type.

If the GPS parsing library returns doubles for lat/lon, the strings/Strings can be convert to doubles.

If the GPS parsing library returns strings, then the strings/Strings returned may, or may not, need any conversion.

However, it will be difficult to compare the lat/lon data from the GPS to the lat/lon data from the file, if the data is of type string or String. You can tell if you got an exact match. to 8 decimal places, but that is all you will be able to tell.

From what I understand about NeoGps is that it returns the values from the GPS into floating point numbers up to 10 decimal places. Although, the UNO will do only about 6 or 7 at best. My future goal is to move to a Due. That way I can use doubles for better accuracy.

This is my first real attempt at using strtok(). I'm still not clear on how to read the data from the SD card correctly. The out put looks like this.

34
00
034
00
034
...

I'm almost certain its how I'm reading the data from the SD card that is creating this kind of output...

#include <SPI.h>
#include "SdFat.h"

SdFat SD;

const uint8_t chipSelect = 10;
File myFile;

using namespace std;

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

  // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) {
      //Serial.println(F("Card failed, or not present"));
      // don't do anything more:
      while (1) ;
    }
    //Serial.println(F("card initialized."));

  // Open up the file we're going to log to!
  myFile = SD.open("GPSData.txt");
  if (! myFile) {
    Serial.println(F("Error opening GPSData.txt"));
    // Wait forever since we cant write data
    while (1) ;
  }
}

void loop() 
{
  getWaypoint();
}

int getWaypoint()
{
  #define waypoints_SIZE 100
  char waypoints[waypoints_SIZE + 1];
  int size = myFile.readBytes(waypoints, waypoints_SIZE);
  // Add the final 0 to end the string
  waypoints[size] = 0;

  // Read each lat long pair 
    char* lat_long = strtok(waypoints, " {");
    while (lat_long != 0)
    {
    // Split the lat_long in two values
    char* separator = strchr(lat_long, ',');
    if (separator != 0) {
        // Actually split the string in 2: replace ':' with 0
        *separator = 0;
        int waypoint_data = atof(lat_long);
        separator;
        int position = atof(separator);

        // Do something with waypoint data and position
        Serial.println(waypoint_data);
        Serial.print(position);
    }
    // Find the next lat long in waypoints string
    lat_long = strtok(0, " }");
  }
}
  // Add the final 0 to end the string
  waypoints[size] = 0;

That should be:

  // Add the final NULL to end the string
  waypoints[size] = '\0';

\0 and 0 are the same value, but \0 is usually used to indicate that you are NULL terminating a string.

    char* lat_long = strtok(waypoints, " {");
    while (lat_long != 0)

Why are you using a while statement there? I would expect an if statement there.

    char* separator = strchr(lat_long, ',');
    if (separator != 0) {
        // Actually split the string in 2: replace ':' with 0
        *separator = 0;
        int waypoint_data = atof(lat_long);

There are several problems here. First, you are not replacing a ':' with a '0'. You are putting a NULL at the memory address that separator points to. That is not necessary, as the comma that is there will stop atof() as effectively as the NULL.

The, you use atof() to convert a string to a FLOAT, and store the resulting float in an int. I can't imagine why.

        separator;

That is as effective as

        42;

What did you think it was doing?

        int position = atof(separator);

separator points to a NULL string now. Not much sense in calling atof() and passing it a NULL. Not much sense in storing the float in an int, either.

        Serial.println(waypoint_data);
        Serial.print(position);

Anonymous printing sucks. Print an identifier. Print delimiters before and after printing strings.