atoi(x) function for integer confirmation?

Hello. In the following code, I want to confirm that the time record (HHMMSS) that I have isolated from a serial GPS feed (in this case from the NMEA $GPRMC sentence) contains only integers from 0 to 9, thereby validating that it is of the time format. It works most of the time, but occasionally I get a non-integer format. My question: is the ATOI function an appropriate use for this issue. Does verifying that atoi(x), where x is a variable of type CHAR, is between 0 and 9 ensure that x is a number?

if(!(0 <= (atoi(time_record[counter]) <= 9)))

for (i = 0; i <242; i ++){
                if (GPSStream[i] == 'R' && GPSStream[i + 1] == 'M' && GPSStream[i + 2] == 'C'){
                    
                         for (counter = 0; counter < 6; counter ++){ // read the 6 time characters from the $GPRMC string into the time variable array.
                        time_record[counter] = GPSStream[counter + i + 4]; // time field begins on the 4th character after the 'R' of $GPRMC
                         if(!(0 <= (atoi(time_record[counter]) <= 9))){
                                  request_timeRecord = 1;// if any of the characters are NOT numbers, get out of this loop, and begin the GPS stream loop again.
                                  }
                             else {
                                  request_timeRecord = 0;     
                                 
                                  }
                        }
               
      }

There is a isDigit function somewhere that's probably more appropriate. I simple if statement may be more compact.

is the ATOI function an appropriate use for this issue.

Probably not. Even if it was, not that way.

atoi() expects a NULL terminated array of chars, not a single char. The isdigit() macro expects a single char, and return true or false.

You do NOT test that a value is in a range that way. You MUST use two separate tests and the && operator.

  byte b = 'A';

   if(b >= 0 && b <= 9)

PaulS:
Probably not. Even if it was, not that way.

atoi() expects a NULL terminated array of chars, not a single char. The isdigit() macro expects a single char, and return true or false.

You do NOT test that a value is in a range that way. You MUST use two separate tests and the && operator.

  byte b = 'A';

if(b >= 0 && b <= 9)

Ok, I changed that yesterday after a couple of weeks with

if(!((atoi(time_record[x])>= 0 && (atoi(time_record[x])) <=9)))

so I will go back to the way it was. Thank you.

Code is now altered to use the atoi(x) on the NULL terminated CHAR array, vice individual CHARs. It is running fine now and I will test under various scenarios. Thank you.

John

PaulS:
You do NOT test that a value is in a range that way. You MUST use two separate tests and the && operator.

  byte b = 'A';

if(b >= 0 && b <= 9)

… which, for what the OP is doing, should be:

  byte b = 'A';

   if(b >= '0' && b <= '9')

because, to our compiler, characters are their ASCII codes, and the ASCII codes of the characters ‘0’ through ‘9’ are not the numeric values zero through nine! (In fact, the ASCII code of any digit character is forty-eight in excess of the numeric value which humans generally understand that character to mean.)

https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html

Can't check at the moment but as far as I know, atoi() returns 0 if it converts an invalid number string.

The strtol function allows you to convert and check. Or use the isDigit mentioned above.

Hi all. For those checking this thread in the future, here is the the bulk of the entire program. Some modules commented out/removed while I debug, but the getGPS_time() and getGPS_date() functions use atoi(x) to verify that the date and time fields are indeed numbers (from a serial stream of characters from a GPS…$GPRMC sentence needed). Once those fields are verified with atoi(x) to be valid numbers, a date and time stamp are used to create a folder (date) and file (time) on a microSD card (I’m using Catalex adapter).

The full program is an attempt at creating an airspeed indicator/logger for use while I wingsuit(skydive). I’m hoping to field test it in a few weeks in Arizona.

Cheers

   #include <Adafruit_BMP280.h> // #include <LiquidCrystal_I2C.h>  //#include <SoftwareSerial.h> 
   #include <math.h>    
   #include <Wire.h>
   #include <SPI.h>
   #include "SdFat.h"
   
   SdFile file;
   SdFat sd;
   Adafruit_BMP280 bmp; 
   // LiquidCrystal_I2C lcd(0x3F, 16, 2);
  

   #define GPSSerial Serial //this program also works with: Serial GPSSerial declaration using the hardware
   #define Register_ID 0
   #define Register_2D 0x2D
   #define BME280_Address 0x77
   #define MS4525DO_Address 0x28
   #define MS5525_Address 0x76   //#define ADXL345_Address 0x53 [not using pitch at the moment, uncomment it when ready]
   #define error(msg) sd.errorHalt(F(msg))
   #define SEALEVEL_SOUND_KNOTS 661.4788
   #define SEALEVEL_PRESSURE_PSI 14.6959465
  

   const uint8_t  rxPin = 0; // RX pin
   const uint8_t  txPin = 1; // TX pin

   const float P_max = 1.0, P_min = - P_max, pi = 3.14;
   const double PSI_to_Pa = 6894.757;
   const uint8_t chipSelect = 10;
   int8_t aircraft_pitch;   
   uint8_t airspeed = 0;
   uint8_t true_airspeed = 0;;
   uint8_t temperature = 0; //Register[3][2] = {{0x32, 0x33},{0x34,0x35},{0x36,0x37}};
   uint8_t request_dateRecord = 1;
   uint32_t static_pressure = 0;  //To store the barometric pressure (Pa) //byte Press_H, Press_L; // data[4];
   float pressureSensorOffset =0.00; //lastKIAS;  //double offset_sum = 0.0; // need this to persist outside of the function so it gets returned to the function //char *timeRecord;// pointer to char variable
   
   char timeRecord[7] = "000000"; 
   char dateRecord[7] = "000000";
  


  
float getOffset(){

//removed for this post
 
}



uint8_t getKnots(float x){

   // removed for size limits of this post
}
 


uint8_t getTrueAirspeed(uint8_t IAS, uint32_t pressure, uint8_t temp){
 // removed for this post

}

void initializeSDcard(char *var1, char *var2){
  
    uint8_t counter;
    char fileName[11] = "000000.csv";
    char folderName[7] = "000000";
  
    for(counter=0; counter<6; counter++){
       fileName[counter] = *(var1 + counter);
       folderName[counter] = *(var2 +counter);
       }
     
    
    if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { // TRY A RATE LESS THAN 50 IF NECESSARY
        sd.initErrorHalt();
        }
        
    if (!(sd.exists(folderName))){    //if the date folder has not been created yet, create it
        Serial.print("The folder does not exist. Here is the name of the new folder: ");
        Serial.println(folderName);
        sd.mkdir(folderName);
        }

    if (sd.exists(folderName)){
        Serial.print("The folder already exists. Here is the name of the existing folder: ");
        Serial.println(folderName);
        }
  
    if (!sd.chdir(folderName)) {  //If we are not in today's folder then change to today's folder
        error("chdir to root failed.\n");
        }
         
    if (!file.open(fileName, O_CREAT | O_WRITE)) {   // create and open the timestamped file for writing of data
        error("file.open");
        }


       Serial.print("And the file we will print to is called ");
       Serial.println(fileName);
       file.println(F("    Time  IAS Pitch Pressure Temperature TAS"));
 

    
}



void getGPS_date(char *var1){

     char GPSStream[256];
     char *date_record;
     date_record = var1;
     uint8_t i = 0;
     uint8_t  n = 0;
     uint8_t counter = 0;

    
        for (i = 0; i <255; i ++){
          GPSStream[i] = GPSSerial.read(); 
           delay(8);
        }
        for (i = 0; i <242; i ++){// read the buffer. Only go to position 242 to allow for enough characters to be read after that position that don't go pass the end of the buffer
                 
                   if ((GPSStream[i] == 'R' && GPSStream[i + 1] == 'M' && GPSStream[i + 2] == 'C')){ //if have found the RMC character string, I know where I am in the NMEA sentence
                       
                        counter = 0; n = i + 3;/// starting the count at the 3rd character after the 'R' character of the '$GPRMC' string
                        while (counter <9){// 'counter' keeps track of the number of commas read. 
                             if (GPSStream[n] == ','){ 
                                 counter++;
                                 n++;
                                
                                 }
                             else n++;
                             }
                        for (counter = n; counter < (n + 6); counter ++){
                             date_record[counter - n] = GPSStream[counter];
                            }
                        date_record[6] = '\0';
                        if (atoi(date_record) != 0){
                            break;
                          }
                   }
               }
}
void getGPS_time(char *var1){

      char GPSStream[256];
      char *time_record;
      time_record = var1;
      uint8_t i = 0;
      uint8_t  n = 0;
      uint8_t counter = 0;

        for (i = 0; i <255; i ++){
           GPSStream[i] = GPSSerial.read(); 
           delay(8);
           }


   
      
           for (i = 0; i <242; i ++){
                if (GPSStream[i] == 'R' && GPSStream[i + 1] == 'M' && GPSStream[i + 2] == 'C'){
                    for (counter = 0; counter < 6; counter ++){ // read the 6 time characters from the $GPRMC string into the time variable array.
                        time_record[counter] = GPSStream[counter + i + 4]; // time field begins on the 4th character after the 'R' of $GPRMC
                        }


                     time_record[6] = '\0';
                        if (atoi(time_record) != 0){
                            break;
                          }
                    
                     } 
     
        
                   
         }
}

void logData(char *time_record, uint8_t IAS, int8_t pitch, uint32_t static_pressure, int8_t air_temperature, uint8_t TAS){
    
    

    file.print(time_record);
    //Serial.println(time_record);
    file.write(',');
    file.print(IAS);
    
    file.write(',');
    file.print("N/A");
    
    file.write(',');
    file.print(static_pressure);
    file.write(',');
    file.print(air_temperature);
    file.write(',');
    file.print(TAS);
    file.write(',');
    file.println();

    if (!file.sync() || file.getWriteError()) {
       error("write error");
    }
 
  
}

void setup(){
  
    Wire.begin(); // wake up I2C bus
    GPSSerial.begin(9600);
   // bmp.begin();    //Begin the sensor
   
   /* 
    Wire.beginTransmission(ADXL345_Address);
    Wire.write(Register_2D);
    Wire.write(8);
    Wire.endTransmission();
    */
   /* 
    lcd.begin();
    lcd.backlight();
    lcd.clear();

   */ 
    
   
   //pressureSensorOffset = getOffset();
   getGPS_date(dateRecord);
   getGPS_time(timeRecord);
   initializeSDcard(timeRecord, dateRecord);
}

void loop (){
    getGPS_time(timeRecord);
   // airspeed = getKnots(pressureSensorOffset);
    static_pressure = bmp.readPressure();
    temperature = bmp.readTemperature();
    true_airspeed = getTrueAirspeed(airspeed, static_pressure, temperature);
    aircraft_pitch = 0;//aircraft_pitch = getPitch() not using the getPitch function right now;//  printResultsToLCD(airspeed);
    logData(timeRecord, airspeed, aircraft_pitch, static_pressure, temperature, true_airspeed);
    
}

I'm puzzled. I have this from a C reference:

The [source] string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.

If I understand correctly, atoi() would then see "1xJJ" as "1" and not see the "xJJ" as an error (i.e. not recognize the string as non-numeric).

Yes, that’s how I read it.
Why is that a problem?

What about a ‘1’ followed by a space?
Or a comma?

Seems perfectly reasonable to me.

aarg:
I'm puzzled. I have this from a C reference:
If I understand correctly, atoi() would then see "1xJJ" as "1" and not see the "xJJ" as an error (i.e. not recognize the string as non-numeric).

Crap. Ok, I will look into that isdigit() macro.

John

AWOL:
Yes, that's how I read it.
Why is that a problem?

What about a '1' followed by a space?
Or a comma?

Seems perfectly reasonable to me.

I thought the OP was trying to confirm that the entire string is numeric. The ones mentioned are fixed length numeric fields, there was no mention of specifically what alternate data might be passed to the function.

The CHAR data coming from the GPS serial stream is sometimes corrupted. I'm trying to verify that the data I isolate is at least numeric. There is a CKSUM field at the end of each GPS sentence but I have not opted to use that for error checking. Commas seperate each field in the GPS stream, so they could end up in the desired (corrupted) data.

Code blocks in getGPS_time() and getGPS_date() changed to incorporate isDigit() instead of atoi(). Program working so far.

void getGPS_date(char *var1){

     char GPSStream[256];
     char *date_record;
     date_record = var1;
     uint8_t i = 0;
     uint8_t  n = 0;
     uint8_t counter = 0;
     boolean corrupted_dateRecord = true;
     

     while (corrupted_dateRecord){
        corrupted_dateRecord = false;
        for (i = 0; i <255; i ++){
          GPSStream[i] = GPSSerial.read(); 
           delay(8);
           }
        for (i = 0; i <242; i ++){// read the buffer. Only go to position 242 to allow for enough characters to be read after that position that don't go pass the end of the buffer
                 
                   if ((GPSStream[i] == 'R' && GPSStream[i + 1] == 'M' && GPSStream[i + 2] == 'C')){ //if have found the RMC character string, I know where I am in the NMEA sentence
                       
                        counter = 0; n = i + 3;/// starting the count at the 3rd character after the 'R' character of the '$GPRMC' string
                        while (counter <9){// 'counter' keeps track of the number of commas read. 
                             if (GPSStream[n] == ','){ 
                                 counter++;
                                 n++;
                                
                                 }
                             else n++;
                             }
                        for (counter = n; counter < (n + 6); counter ++){
                             date_record[counter - n] = GPSStream[counter];
                             if (!(isDigit(date_record[counter - n]))){
                              corrupted_dateRecord = true ;
                             }
                            }
                        if (!(corrupted_dateRecord)){
                        date_record[6] = '\0';
                        break;
                          }
                   }
               }
}

}
void getGPS_time(char *var1){

      char GPSStream[256];
      char *time_record;
      time_record = var1;
      uint8_t i = 0;
      uint8_t  n = 0;
      uint8_t counter = 0;
      boolean corrupted_timeRecord;
      corrupted_timeRecord = true;
           
        while (corrupted_timeRecord){     
           corrupted_timeRecord = false;
           for (i = 0; i <255; i ++){
               GPSStream[i] = GPSSerial.read(); 
               delay(8);
               }

           for (i = 0; i <242; i ++){
                if (GPSStream[i] == 'R' && GPSStream[i + 1] == 'M' && GPSStream[i + 2] == 'C'){
                    for (counter = 0; counter < 6; counter ++){ // read the 6 time characters from the $GPRMC string into the time variable array.
                        time_record[counter] = GPSStream[counter + i + 4]; // time field begins on the 4th character after the 'R' of $GPRMC
                        if (!(isDigit(time_record[counter]))){
                              corrupted_timeRecord = true ;
                             }
                        }

                        if (!(corrupted_timeRecord)){
                            time_record[6] = '\0';
                            break;
                            }
                     } 
           
         }
    }
}

Curious_John:
The CHAR data coming from the GPS serial stream is sometimes corrupted. I'm trying to verify that the data I isolate is at least numeric. There is a CKSUM field at the end of each GPS sentence but I have not opted to use that for error checking. Commas seperate each field in the GPS stream, so they could end up in the desired (corrupted) data.

Perhaps you should use SoftwareSerial for your GPS and get some Serial print debugging into your program...

Serial is pretty reliable; I'm not sure your parsing functions work as you expect (i.e. how do you know your functions are starting at the first char in a stream from the GPS device?). I didn't really go through all of your parsing functions, in all fairness...

Hi Bulldog.

I have been using softwareSerial for most of the development of this project, but switched back to Serial a couple of days ago for more reliable (I hope) data.

My code parses each character from the various sentences ($GPRMC, $GPGGA etc) and is only looking for the 'RMC' string sequence. From there it knows where it is and then bases the time field and date field locations on starting there.

And I have made extensive use of debugging serial.print functions. Right now the setup function prints out the name of the date folder (MMDDYY) and the time folder (HHMMSS.csv) to the serial.monitor before it begins logging data so I quickly know if the code works.

Curious_John:
I have been using softwareSerial for most of the development of this project, but switched back to Serial a couple of days ago for more reliable (I hope) data.

No library for your GPS, then? what device is it?

it just seems to me that your code doesn't have a mechanism of assuring that the packets align correctly (i.e. look for the end of packet new line).

I looked at librairies, all of them, but couldn't seem to make the programs work with what I wanted to do.

As it is, I just read 256 characters into a buffer from the Serial input, then I look at each character:

if ((GPSStream[i] == 'R' && GPSStream[i + 1] == 'M' && GPSStream[i + 2] == 'C')){ /if have found the RMC character string, I know where I am in the NMEA sentence

Once I have found the 'RMC' sequence, I know I am at the 'C' in '$GPRMC' and the next character should be ',' followed by the time field. In a similar way, I count 9 commas after the 'C' and get to the date field.

Curious_John:
...with what I wanted to do..

... which is?

Curious_John:
Once I have found the ‘RMC’ sequence, I know I am at the ‘C’ in ‘$GPRMC’ and the next character should be ‘,’ followed by the time field. In a similar way, I count 9 commas after the ‘C’ and get to the date field.

you are doing this in a kind of hacky way, sorry to say…

You can try using this example:

#include "SerialMessenger.h"

void parseSerialMssg(const char* mssg);
void parseSoftSoftSerialMssg(const char* mssg);

SoftwareSerial softSerial(4,5);

SerialMessenger serial(Serial, 256, '

with this library:
SerialMessenger.h

#ifndef SERIALMESSENGER_H
#define SERIALMESSENGER_H

#include <SoftwareSerial.h>

#if ARDUINO >= 100
  #include "Arduino.h"
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
    #define MAX_SERIAL_PORTS 2
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
    #define MAX_SERIAL_PORTS 1
  #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    #define MAX_SERIAL_PORTS 5
  #endif
#else
  #define MAX_SERIAL_PORTS 2
#endif

typedef uint8_t byte_size_t; 

class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err = false);
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err = false);
    ~SerialMessenger();
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    
  private:
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    byte_size_t bufferSize;
    byte_size_t idx = 0;
    char startMkr;
    char endMkr;
    bool errors;
    static byte_size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum State{
      WAITING,
      PARSING,
    }state = WAITING;
};

#endif

and SerialMessenger.cpp

#include "SerialMessenger.h"
#include "SoftwareSerial.h"

byte_size_t SerialMessenger::instanceCount = 0;
SerialMessenger* SerialMessenger::instances[MAX_SERIAL_PORTS] = {nullptr};

SerialMessenger::SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  instances[instanceCount++] = this;
  hwStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  instances[instanceCount++] = this;
  swStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::~SerialMessenger(){ free(incomingMessage); incomingMessage = NULL;}

void SerialMessenger::update()
{
  for (size_t i = 0; i < instanceCount; i++)
  {
    if (const char* message = instances[i]->checkForValidMessage(instances[i]->startMkr, instances[i]->endMkr))
    {
      instances[i]->callback(message);
    }
  }
}

char* SerialMessenger::checkForValidMessage(const char startMarker, const char endMarker) //, const char checksumToken)
{
  stream = !hwStream ? (Stream*)swStream : hwStream;
  if (stream->available())
  {
    if (state == WAITING)
    {
      if (startMkr)
      {
        if (stream->read() == startMarker)
        {
          state = PARSING;
        }
        return nullptr;
      }
      else
      {
        state = PARSING;
      }
    }
    else if (state == PARSING)
    {
      incomingMessage[idx] = stream->read();
      if (incomingMessage[idx] == endMarker)
      {
        incomingMessage[idx] = '\0';
        idx = 0;
        if (startMkr)
        {
          state = WAITING;
        }
        return incomingMessage;
      }
      else
      {
        idx++;
        if (idx > bufferSize - 1)
        {
          if (errors)
          {
            stream->println(F("Bad Message"));  //<< Error back to sender
          }
          idx = 0;
          incomingMessage[idx] = '\0';
          if (startMkr)
          {
            state = WAITING;
          }
        }
      }
    }
  }
  return nullptr;
}

void SerialMessenger::begin(uint32_t baudRate)
{
  if (hwStream)
  {
    hwStream->begin(baudRate);
  }
  else
  {
    swStream->begin(baudRate);
  }
}

Testing with this RMC sentence…

$GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,251211,1.2,E,A*03

that should give you what you need to parse the date, time, etc… output looks like this

From callback function: GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,251211,1.2,E,A*03

Parsing GPRMC sentence...
GPRMC
Time: 001225
Status: A
Lat: 2832.1834
Dir: N
Lon: 08101.0536
Dir: W
Speed: 12
Angle: 25
Date: 251211
Var: 1.2
Dir: E
ChkSum: A*03

, ‘\n’, parseSerialMssg);  // defining start & end markers as ’


with this library:
SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


and SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§


Testing with this RMC sentence...

§DISCOURSE_HOISTED_CODE_3§


that should give you what you need to parse the date, time, etc... output looks like this

§DISCOURSE_HOISTED_CODE_4§


 and '\n' respectively

SerialMessenger sw(softSerial, 256, '

with this library:
SerialMessenger.h

§_DISCOURSE_HOISTED_CODE_1_§

and SerialMessenger.cpp

§_DISCOURSE_HOISTED_CODE_2_§

Testing with this RMC sentence…

§_DISCOURSE_HOISTED_CODE_3_§

that should give you what you need to parse the date, time, etc… output looks like this

§_DISCOURSE_HOISTED_CODE_4_§

, ‘\n’, parseSerialMssg);
//SerialMessenger serial1(Serial1, 16, ‘\0’, ‘\n’, parseMssg);

void setup()
{
 serial.begin(9600);
 sw.begin(9600);
}

void loop()
{
 SerialMessenger::update();
}

void parseSerialMssg(const char* mssg)
{
 Serial.print(F(“From callback function:\t”));
 Serial.println(mssg);
 if (strstr(mssg, “GPRMC”))
 {
    Serial.print(F(“Parsing GPRMC sentence…\n”));
    char gprmc[strlen(mssg) + 1];
    char fieldData[16];
    strcpy(gprmc, mssg);
    strtok(gprmc, “,”);
    Serial.println(gprmc);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Time:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Status:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Lat:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Dir:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Lon:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Dir:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Speed:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Angle:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Date:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Var:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Dir:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“ChkSum:\t”));
    Serial.println(fieldData);
 }
 else if (strstr(mssg, “GPGGA”))
 {
   Serial.print(F(“Parsing GPGGA sentence…\n”));
 }
}

//void parseSoftSoftSerialMssg(const char* mssg)
//{
//  Serial.print(F(“New SoftSerial Message:\t”));
//  Serial.println(mssg);
//}


with this library:
SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


and SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§


Testing with this RMC sentence...

§DISCOURSE_HOISTED_CODE_3§


that should give you what you need to parse the date, time, etc... output looks like this

§DISCOURSE_HOISTED_CODE_4§