Writing to SD card using GPS produce distorted sentences

I have connected up my GPS, accelerometer and tft display to my Arduino nano.

In the Serial COM port I notice that all the NMEA sentences are printing out nicely and correctly. An RMC sentence followed by a GGA sentence.

However, when I check to see what was logged on the SD card I notice that the RMC sentences contain strange characters and symbols.

After some researching I discovered that this may be due to a low SRAM availaibility problem. Upon compilation, I received the warning "Low memory available, stability problems may occur" and:
Sketch uses 29036 bytes (94%) of program storage space. Maximum is 30720 bytes.
Global variables use 1592 bytes (77%) of dynamic memory, leaving 456 bytes for local variables

As a result I tried to free up memory. Having read that STRINGS take up a lot of SRAM, I removed one of the NMEA sentences I was storing on the SD card. This resulted in good NMEA sentences being stored on the SD card. However, This means I am unable to capture all the good NMEA sentences coming through.

My Question: How can I send both NMEA sentences to the SD card.

my guesses: 1) Using an alternate data type to string. Maybe a character array? I am not sure how to implement this...
2) Storing the string temporarily in EEPROM - again I am not sure how to implement this.

I was hoping someone could take a look at my code below and aid me in finding a solution.

Many thanks in advance for your time!

Laurence

p,s It may have nothing to do with Strings and Memory. If anyone can think of any other problems (Most like to do with the code) please let me know.

//Make sure to install the adafruit GPS library from https://github.com/adafruit/Adafruit-GPS-Library
#include <Adafruit_GPS.h> //Load the GPS Library. Make sure you have installed the library form the adafruit site above
#include <SoftwareSerial.h> //Load the Software Serial Library. This library in effect gives the arduino additional serial ports
SoftwareSerial mySerial(3, 2); //Initialize SoftwareSerial, and tell it you will be connecting through pins 2 and 3
Adafruit_GPS GPS(&mySerial); //Create GPS object
#include "Adafruit_ILI9341.h" 
#include <Adafruit_GFX.h>
#include <SD.h>
#include <SPI.h>
#define TFT_DC 9
#define TFT_CS 8

int chipSelect = 4;
File gpsData; //Variable for SD cards Object
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
String NMEA1;  //We will use this variable to hold our first NMEA sentence
String NMEA2;  //We will use this variable to hold our second NMEA sentence
char c;       //Used to read the characters spewing from the GPS module

void setup()  
{
  tft.begin();
  tft.setRotation(3);
  tft.setTextSize(2);
  tft.fillScreen(ILI9341_BLACK);
  Serial.begin(115200);  //Turn on the Serial Monitor
  GPS.begin(9600); //Turn GPS on at baud rate of 9600
    pinMode(10,OUTPUT);
  SD.begin(chipSelect); //Initialise SD card and say it is connected to chipSelect (4)
  
  if (SD.exists("NMEA.txt")) //Checks to see if the files exists (if it does and we don't remove it, we append it. We don't want old data)
    {
      SD.remove("NMEA.txt");
    }
  if (SD.exists("GPSdata.txt")) //Same as above. Get rid of old data so we can obtain new data
    {
      SD.remove("GPSdata.txt");
    }
  GPS.sendCommand("$PGCMD,33,0*6D"); // Turn Off GPS Antenna Update
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); //Tell GPS we want only $GPRMC and $GPGGA NMEA sentences
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
  delay(1000);  //Pause
}


void loop()                     // run over and over again
{
readGPS();//This is a function we define below which reads two NMEA sentences from GPS
if (GPS.fix == 1) //only log data after we have a fix
    {
      gpsData = SD.open("NMEA.txt",FILE_WRITE); //open NMEA.txt to write to it.
      gpsData.println(NMEA1); //Write first sentence to file
      gpsData.println(NMEA2); //Write second sentence to file
      gpsData.close();
      gpsData = SD.open("GPSdata.txt", FILE_WRITE);
      gpsData.print(GPS.latitude,4); //Write latitude to the SD file to 4 decminal places
      gpsData.print(GPS.lat); //Which hemispher are we in? N or S?
      gpsData.print(",");
      gpsData.print(GPS.longitude,4);
      gpsData.print(GPS.lon); //Which hemisphere are we in, E or W
      gpsData.print(",");
      gpsData.println(GPS.altitude);
      gpsData.close();
      
      
    }

}
void readGPS(){  //This function will read and remember two NMEA sentences from GPS
  clearGPS();    //Serial port probably has old or corrupt data, so begin by clearing it all out
  while(!GPS.newNMEAreceived()) { //Keep reading characters in this loop until a good NMEA sentence is received
  c=GPS.read(); //read a character from the GPS
  }
GPS.parse(GPS.lastNMEA());  //Once you get a good NMEA, parse it
NMEA1=GPS.lastNMEA();      //Once parsed, save NMEA sentence into NMEA1
while(!GPS.newNMEAreceived()) {  //Go out and get the second NMEA sentence, should be different type than the first one read above.
  c=GPS.read();
  }
GPS.parse(GPS.lastNMEA());
NMEA2=GPS.lastNMEA();
  Serial.println(NMEA1);
  Serial.println(NMEA2);
  Serial.println("");
  PrintDateTime();
}
void clearGPS() {  //Since between GPS reads, we still have data streaming in, we need to clear the old data by reading a few sentences, and discarding these
while(!GPS.newNMEAreceived()) {
  c=GPS.read();
  }
GPS.parse(GPS.lastNMEA());
while(!GPS.newNMEAreceived()) {
  c=GPS.read();
  }
GPS.parse(GPS.lastNMEA());

}

void PrintDateTime(){
  
 int Offset = 1;
 GPS.hour = GPS.hour + Offset;
 tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); 
 tft.setCursor(90,100);
 if (GPS.hour < 10)
 {
  tft.print("0");
  tft.print(GPS.hour); 
 }
 else if (10 < GPS.hour < 25)
 {
  tft.print(GPS.hour);
  if (GPS.hour = 24){GPS.day = (GPS.day+1);}
  
 }
 else 
 {
  GPS.hour = 1;
  tft.print("0");
  tft.print(GPS.hour);
 }
  
 tft.print(":");
 if(GPS.minute < 10)
 {
    tft.print("0"); 
    tft.print(GPS.minute);
 }
 else
 {
    tft.print(GPS.minute); 
 }
 tft.print(":");
 
 if(GPS.seconds < 10)
 {
    tft.print("0");
    tft.print(GPS.seconds); 
 }
 else
 {
    tft.print(GPS.seconds);
 }
 tft.setCursor(90,120);
  if(GPS.day < 10)
  {
    tft.print("0"); 
    tft.print(GPS.day); 
  }  
  else
  {
    tft.print(GPS.day);
  }
  tft.print(":"); 
 if(GPS.month < 10)
 {
    tft.print("0"); 
    tft.print(GPS.month);
 } 

 else
 {
    tft.print(GPS.month); 
 }
 tft.print(":");
 tft.print(GPS.year);

}

OP code posted so everyone can see it. OP, read the how to use this forum-please read sticky to see how to properly post code.

//Make sure to install the adafruit GPS library from https://github.com/adafruit/Adafruit-GPS-Library
#include <Adafruit_GPS.h> //Load the GPS Library. Make sure you have installed the library form the adafruit site above
#include <SoftwareSerial.h> //Load the Software Serial Library. This library in effect gives the arduino additional serial ports
SoftwareSerial mySerial(3, 2); //Initialize SoftwareSerial, and tell it you will be connecting through pins 2 and 3
Adafruit_GPS GPS(&mySerial); //Create GPS object
#include "Adafruit_ILI9341.h" 
#include <Adafruit_GFX.h>
#include <SD.h>
#include <SPI.h>
#define TFT_DC 9
#define TFT_CS 8

int chipSelect = 4;
File gpsData; //Variable for SD cards Object
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
String NMEA1;  //We will use this variable to hold our first NMEA sentence
String NMEA2;  //We will use this variable to hold our second NMEA sentence
char c;       //Used to read the characters spewing from the GPS module

void setup()  
{
  tft.begin();
  tft.setRotation(3);
  tft.setTextSize(2);
  tft.fillScreen(ILI9341_BLACK);
  Serial.begin(115200);  //Turn on the Serial Monitor
  GPS.begin(9600); //Turn GPS on at baud rate of 9600
    pinMode(10,OUTPUT);
  SD.begin(chipSelect); //Initialise SD card and say it is connected to chipSelect (4)
  
  if (SD.exists("NMEA.txt")) //Checks to see if the files exists (if it does and we don't remove it, we append it. We don't want old data)
    {
      SD.remove("NMEA.txt");
    }
  if (SD.exists("GPSdata.txt")) //Same as above. Get rid of old data so we can obtain new data
    {
      SD.remove("GPSdata.txt");
    }
  GPS.sendCommand("$PGCMD,33,0*6D"); // Turn Off GPS Antenna Update
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); //Tell GPS we want only $GPRMC and $GPGGA NMEA sentences
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
  delay(1000);  //Pause
}


void loop()                     // run over and over again
{
readGPS();//This is a function we define below which reads two NMEA sentences from GPS
if (GPS.fix == 1) //only log data after we have a fix
    {
      gpsData = SD.open("NMEA.txt",FILE_WRITE); //open NMEA.txt to write to it.
      gpsData.println(NMEA1); //Write first sentence to file
      gpsData.println(NMEA2); //Write second sentence to file
      gpsData.close();
      gpsData = SD.open("GPSdata.txt", FILE_WRITE);
      gpsData.print(GPS.latitude,4); //Write latitude to the SD file to 4 decminal places
      gpsData.print(GPS.lat); //Which hemispher are we in? N or S?
      gpsData.print(",");
      gpsData.print(GPS.longitude,4);
      gpsData.print(GPS.lon); //Which hemisphere are we in, E or W
      gpsData.print(",");
      gpsData.println(GPS.altitude);
      gpsData.close();
      
      
    }

}
void readGPS(){  //This function will read and remember two NMEA sentences from GPS
  clearGPS();    //Serial port probably has old or corrupt data, so begin by clearing it all out
  while(!GPS.newNMEAreceived()) { //Keep reading characters in this loop until a good NMEA sentence is received
  c=GPS.read(); //read a character from the GPS
  }
GPS.parse(GPS.lastNMEA());  //Once you get a good NMEA, parse it
NMEA1=GPS.lastNMEA();      //Once parsed, save NMEA sentence into NMEA1
while(!GPS.newNMEAreceived()) {  //Go out and get the second NMEA sentence, should be different type than the first one read above.
  c=GPS.read();
  }
GPS.parse(GPS.lastNMEA());
NMEA2=GPS.lastNMEA();
  Serial.println(NMEA1);
  Serial.println(NMEA2);
  Serial.println("");
  PrintDateTime();
}
void clearGPS() {  //Since between GPS reads, we still have data streaming in, we need to clear the old data by reading a few sentences, and discarding these
while(!GPS.newNMEAreceived()) {
  c=GPS.read();
  }
GPS.parse(GPS.lastNMEA());
while(!GPS.newNMEAreceived()) {
  c=GPS.read();
  }
GPS.parse(GPS.lastNMEA());

}

void PrintDateTime(){
  
 int Offset = 1;
 GPS.hour = GPS.hour + Offset;
 tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); 
 tft.setCursor(90,100);
 if (GPS.hour < 10)
 {
  tft.print("0");
  tft.print(GPS.hour); 
 }
 else if (10 < GPS.hour < 25)
 {
  tft.print(GPS.hour);
  if (GPS.hour = 24){GPS.day = (GPS.day+1);}
  
 }
 else 
 {
  GPS.hour = 1;
  tft.print("0");
  tft.print(GPS.hour);
 }
  
 tft.print(":");
 if(GPS.minute < 10)
 {
    tft.print("0"); 
    tft.print(GPS.minute);
 }
 else
 {
    tft.print(GPS.minute); 
 }
 tft.print(":");
 
 if(GPS.seconds < 10)
 {
    tft.print("0");
    tft.print(GPS.seconds); 
 }
 else
 {
    tft.print(GPS.seconds);
 }
 tft.setCursor(90,120);
  if(GPS.day < 10)
  {
    tft.print("0"); 
    tft.print(GPS.day); 
  }  
  else
  {
    tft.print(GPS.day);
  }
  tft.print(":"); 
 if(GPS.month < 10)
 {
    tft.print("0"); 
    tft.print(GPS.month);
 } 

 else
 {
    tft.print(GPS.month); 
 }
 tft.print(":");
 tft.print(GPS.year);

}

Thank you! I've modified it above.
Laurence

Update:

Having tried to reduce the amount of SRAM used, I then decided run the entire code without the addition of the TFT Display..Everything seem to work well without the tft display connected.

Even with the TFT connected AND all print strings statements commented out, there are still issues writing to the display.

I believe that I have an issue with both the TFT and SD card trying to communicate simultaneously - possibly as they are both using SPI pins?

Is there a better place to initialise the SD card than in the Set-up loop? Perhaps when I only need it (fix ==1?)
Wondering if anyone could shed some light on this issue?

You are still using String with the capital S. BAD.

You must learn to use char arrays.

How many characters are in the NMEA1 and NMEA2 ?

ieee488:
You are still using String with the capital S. BAD.

You must learn to use char arrays.

How many characters are in the NMEA1 and NMEA2 ?

The compiler only recognises it is a string if I use a Capital S?

After some googling, I found there is a possible max of 82 characters (80 visible characters plus terminators).

There are Strings (Srting objects) and there are strings (null terminated character arrays). The String objects take up more memory and, if misused, (easily done) can cause memory fragmentation resulting hard to diagnose bugs. See here for why Strings are bad and solutions using strings.

LaurenceR:
The compiler only recognises it is a string if I use a Capital S?

After some googling, I found there is a possible max of 82 characters (80 visible characters plus terminators).

char NMEA1[83];
char NMEA2[83];

82 plus the terminating NULL ( '\0' )

Unfortunately for you the Adafruit library uses the capital S String objects, but they probably didn't anticipate all the other stuff you have thrown in.

LaurenceR:
The compiler only recognises it is a string if I use a Capital S?

After some googling, I found there is a possible max of 82 characters (80 visible characters plus terminators).

char NMEA1[83];
char NMEA2[83];

82 plus the terminating NULL ( '\0' )

Whoever wrote the sketch you are using decided to use String objects instead of strings (character arrays).

Many thanks for all the help ieee and groundfungus.

Memory space seems to be a persistent issue but I will look to fix it using the Char arrays described above.
Nevertheless, the issue seems to have been SOLVED.

simply by using the SdFat library instead of the standard SD library, the NMEA sentences are being stored as expected on the SD modules when used in conjuction with the tft display (this combination of 2 spi devices previously causing issues with the standard SD library)

It also uses less storage which is a bonus!

Thanks again,

Laurence