Extracting and checking elements from a char x[n]; item

I'm confused and I hope someone is able to help but please treat me gently, I am still learning!

I collect NMEA data and need to analyse it to select the correct record from the incoming stream. I've pasted a very small part below which fails to provide the expected result and it is driving me mad.

//NMEA String Parser test

char Sentence[90] = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M, 46.9, M,  , *47";
char *p, *i;
char *latitude;
char *longtitude;

void setup()

{
  Serial.begin(9600);

  Serial.print("\nNMEA Sentence: ");
  Serial.println(Sentence);

  // Extract first element which is the type of NMEA Sentence
  p = strtok_r(Sentence, ",", &i);
  Serial.print("\nNMEAtype =  ");
  Serial.println(p);

  if (p == "$GPGGA") {
    Serial.println("$GPGGA found");
  }
  else {
    Serial.println("\nIncorrect type");
    while (1);
  }

  // then choose 3rd element which is Latitude
  p = strtok_r(NULL, ", ", &i);
  p = strtok_r(NULL, ", ", &i);

  Serial.print("\nLatitude = ");
  Serial.println(p);

  // Finally 5tht element required which is Longtitude
  p = strtok_r(NULL, ", ", &i);
  p = strtok_r(NULL, ", ", &i);
  Serial.print("\nLongtitude = ");
  Serial.println(p);
}
void loop() {};

Despite the Sentence being of the correct type - $GPGGA - the test at the if fails and the sketch terminates at the while(1): as shown below in the serial monitor output.

NMEA Sentence: $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M, 46.9, M,  , *47

NMEAtype =  $GPGGA

Incorrect type

Strings, strings and *pointers are a bit of dark magic in my current world and I need to understand why the test fails - is anyone able to help please.

Many thanks.

Try strcmp

RoddyJoff:
I'm confused and I hope someone is able to help but please treat me gently, I am still learning!

String comparison in C/C++ is done with a strcmp() function call and not with a "==" comparison operator.

And you can only compare a string against a string.

Many thanks for your replies but I'm afraid strcmp() didn't work for me.

If I use the following code

NMEA String Parser test

char Sentence[90] = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M, 46.9, M,  , *47";
char *p, *i, *pp;
char *latitude;
char *longtitude;
int ret;

void setup()

{
  Serial.begin(9600);

  Serial.print("\nNMEA Sentence: ");
  Serial.println(Sentence);

  // Extract first element which is the type of NMEA Sentence
  p = strtok_r(Sentence, ",", &i);

  Serial.print("\nNMEAtype = ");
  Serial.println(p);

  //  pp="$GPGGA";             //I have tried this also
  //  Serial.print("pp= ");
  //  Serial.println(pp);
  //  ret = strcmp(p,pp);

  ret = strcmp(p, "$GPGGA");

  if (ret = 1)
    Serial.println("$GPGGA found");
  else {
    Serial.println("\nIncorrect type");
    while (1);
  }

  // then choose 3rd element which is Latitude
  p = strtok_r(NULL, ", ", &i);
  p = strtok_r(NULL, ", ", &i);

  Serial.print("\nLatitude = ");
  Serial.println(p);

  // Finally 5th element required which is Longtitude
  p = strtok_r(NULL, ", ", &i);
  p = strtok_r(NULL, ", ", &i);
  Serial.print("\nLongtitude = ");
  Serial.println(p);
}
void loop() {};

The if statement works and the sketch continues to the end. But as a test I changed the $GPGGA entry in the Sentence declaration and it still worked despite there being an obvious mismatch.

I'm doing something silly I just do not know what it is.

  if (ret = 1)Wrong.

Apologies!

My typo. I did have the correct entry in my code '==' and it still failed.

Please post the actual code that does not work

strcmp returns 0 when the strings match.

Regards,
Ray L.

RoddyJoff:
I'm doing something silly I just do not know what it is.

I've done a different programming example for you:

// NMEA messages should never be more than 80 chars, so including terminating \0 this is 81:
char Sentence[81] = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M, 46.9, M,  , *47";

void parseNMEA(char* str)
{
  Serial.print("Parse: ");
  Serial.println(str);
  char* substr=strtok(str,",");
  if (strcmp(substr,"$GPGGA")==0) Serial.println("This is a $GPGGA sentence");
  else Serial.println("unknown type of sentence");
  int i=0;
  while (substr!=NULL)
  {
    Serial.print("Index ");
    Serial.print(i);
    Serial.print("= ");
    Serial.println(substr);
    substr=strtok(NULL,",");
    i++;
  }
}


void setup()
{
  Serial.begin(9600);
  parseNMEA(Sentence);
}

void loop() {};

But be warned: There are several reasons why you cannot use that for GPS parsing. The main reason is that strtok() is greedy for seperating chars. So in case your sentence contains several commas following each other, the strtok() function will skip over multiple commas at once.

Here is my code with the corrections, thanks.

//NMEA String Parser test

char Sentence[90] = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M, 46.9, M,  , *47";
char *p, *i, *pp;
char *latitude;
char *longtitude;
int ret;

void setup()

{
  Serial.begin(9600);

  Serial.print("\nNMEA Sentence: ");
  Serial.println(Sentence);

  // Extract first element which is the type of NMEA Sentence
  p = strtok_r(Sentence, ",", &i);

  Serial.print("\nNMEAtype = ");
  Serial.println(p);

  //  pp="$GPGGA";             //I have tried this also
  //  Serial.print("pp= ");
  //  Serial.println(pp);
  //  ret = strcmp(p,pp);

  ret = strcmp(p, "$GPGGA");

  if (ret == 1)
    Serial.println("$GPGGA found");
  else {
    Serial.println("\nIncorrect type");
    while (1);
  }

  // then choose 3rd element which is Latitude
  p = strtok_r(NULL, ", ", &i);
  p = strtok_r(NULL, ", ", &i);

  Serial.print("\nLatitude = ");
  Serial.println(p);

  // Finally 5th element required which is Longtitude
  p = strtok_r(NULL, ", ", &i);
  p = strtok_r(NULL, ", ", &i);
  Serial.print("\nLongtitude = ");
  Serial.println(p);
}
void loop() {};

I think the single '=' might have crept into my code before I realised it so I ran a few more tests.

Here are my results:

With the Sentence set with $GPGGA as the first element then

ret = strcmp(p, "$GPGGA");

If (ret < 0) works OK
If (ret == 1 ) does not work
If (ret < 0) does not work

If I then change one character of the $GPGGA element in the Sentence declaration the following happens

If (ret < 0) does not work
If (ret == 1 ) does not work
If (ret < 0) does work, but shouldn't.

My understanding, albeit limited, is that '1' should be returned for a match, am I right?

Cheers.

Many thanks Jurs.

I've already discovered the 'consecutive commas' syndrome and fixed it with this in my routine which reads the entire Sentence:

if (lastcharacter == (',') && currentchar == (',')) //check for consequetive 'commas' and add a '0' if so to allow STRTOK to work.
          {
            currentchar = '0';
            ndx++;
          }

I'll put your code to good use thanks!!

Regards,
Ray L.

My understanding, albeit limited, is that '1' should be returned for a match, am I right?

Wrong. 0 is returned for a match.
Try this

void setup()
{
  Serial.begin(115200);
  Serial.println(strcmp("A", "A"));
  Serial.println(strcmp("A", "B"));
  Serial.println(strcmp("B", "A"));
}

void loop(){}

RoddyJoff:
My understanding, albeit limited, is that '1' should be returned for a match, am I right?

No the strcmp() function returns an integer less than, equal to, or greater than zero
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga46f3cbd2de457c0fb340a1f379fc33ba

equal zero would mean, that both strings are a match.

A result less than or greater than zero would tell you about alphabetical order and which string would be first in an alphabetical sorting.

lexicographic sorting based on the character coding, ie not alphabetical at all as "Z" comes before "a"
in the ASCII code:

void setup() 
{
  Serial.begin(115200) ;
  Serial.println ('a' < 'Z' ? "less" : "not less") ;
  Serial.println ('a' < 'z' ? "less" : "not less") ;
  Serial.println (strcmp ("a", "Z")) ;
  Serial.println (strcmp ("a", "z")) ;
}

RoddyJoff:
Many thanks Jurs.

I've already discovered the 'consecutive commas' syndrome and fixed it with this in my routine which reads the entire Sentence:

if (lastcharacter == (',') && currentchar == (',')) //check for consequetive 'commas' and add a '0' if so to allow STRTOK to work.

{
           currentchar = '0';
           ndx++;
         }

Replacing chars you actually have read with other chars you didn't read, will lead to the next problem: The checksum value will become invalid when checking the checksum of the NMEA sentence for being a valid checksum.

I'd better like to read the NMEA sentice as it is received, but instead of strtok() I'd possibly write my own NMEA tokenizing function which is not greedy with consecutive commas.

Many thanks everyone, I have plenty to go on now.

And I realise now that the match result is 0 - I just presumed I was looking for a TRUE.

And thanks Jurs. I understand your comment about data integrity. I only need two elements (lat and long in my example) and the rest is discarded. My second data stream will be a Depth Sensor and I will need to take readings over 5mins and then take the median before uploading to a Web site.

I may be back!!!!!

Regards.