Reading and parsing serial strings

Evening all,

I've been banging my head on this problem for a few days now and I just don't see why I am not getting any traction on it so, I come to you for help. I seem to be not understanding something about either arduino serial data handling or arduino string handling or, both. The problem is this: I am trying to read a gps module (Argent Data Systems) which outputs standard NMEA sentences. The unit puts out a paragraph every second starting with a "$GPGGA" string. The paragraph content varies in a cyclic manner. Three seconds of typical output looks like the following:

$GPGGA,033332.00,0000.0000,N,00000.0000,E,0,00,0.0,,M,,M,,*5F
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,033332.00,V,0000.0000,N,00000.0000,E,,,050212,,,N*40

$GPGGA,033333.00,0000.0000,N,00000.0000,E,0,00,0.0,,M,,M,,*5E
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,033333.00,V,0000.0000,N,00000.0000,E,,,050212,,,N*41

$GPGGA,033334.00,0000.0000,N,00000.0000,E,0,00,0.0,,M,,M,,*59
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,4,1,15,26,88,090,,02,88,090,,04,88,090,,05,88,090,*7F
$GPGSV,4,2,15,24,88,090,,29,88,090,,08,88,090,,07,88,090,*79
$GPGSV,4,3,15,27,88,090,,10,88,090,,12,88,090,,30,72,270,25*70
$GPGSV,4,4,15,20,72,090,,03,80,090,21,06,80,270,19*41
$GPRMC,033334.00,V,0000.0000,N,00000.0000,E,,,050212,,,N*46

I want to key my code to the GPGGA string that starts every paragraph and that is the only sentence I actually need to extract. I have tried straight strings, the String class, serialEvent(), I've hunted the web for examples and I have yet to find anything with which I can pull this sentence out. I really don't see what I'm doing wrong. I haven't tried TinyGPS or similar yet. When I first read about it, it didn't appear to do what I wanted which is to give me the actual sentence. I will revisit it after I post this. I'd still love to know why the things I have tried haven't worked.

If I read in each character and then just immediately dump it back out on the serial, I see everything (in fact, that's how the list of sentences/paragraphs above was generated:

void setup() {
  Serial.begin(4800);
}

void loop() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    Serial.print(inChar);
  }
}

If I try to do anything else, I either get a garbled, incomplete mess or, nothing at all. I just tried tonight to solve this in the most basic, classical way I could think and even that didn't work. I get no output at all.

/*  gps_read_GPGGA
    Written 2/12/2012 Steven Buczkowski
 
    Yet another attempt to read GT-320FW GPS module for ProjectDEBO
    HAB C&DH and key in on GPGGA sentences for timing, sensor
    reads and radio xmit.    
    */

char sBuffer[250]; // to store incoming NMEA sentence
int i;
char inChar;

void setup() {
  Serial.begin(4800);
}

void loop() {
  while (Serial.available()) {
    do { // look for '

I am writing this in the Arduino 1.0 environment and running presently on a Duemilanove (once it works, the operational platform will be a pro mini). I'll take any advice I can get on how to break this serial input into strings and then do comparisons on it.

Thanks for any help you can give me!
      inChar = Serial.read();
    }
    while (inChar != '


I am writing this in the Arduino 1.0 environment and running presently on a Duemilanove (once it works, the operational platform will be a pro mini). I'll take any advice I can get on how to break this serial input into strings and then do comparisons on it. 

Thanks for any help you can give me!); // end do {
    // found start of NMEA sentence. Now look to see which one
    sBuffer[0] = inChar;
    for (i=1;i<6;i++) {
      inChar = Serial.read();
      sBuffer[i] = inChar;
    } // end for (i=1;i<6;i++)
    sBuffer[i] = '\0'; // terminate this portion of string
    if (strcmp(sBuffer, "$GPGGA") != 0) {
      //goto nextloop;
      return;
    } // end if (strcmp)
    else {
      // we've found a NMEA GPGGA string. read in the rest and do something
      inChar = Serial.read();
      while (inChar != '*') {
        sBuffer[i++] = inChar;
        inChar = Serial.read();
      } // end while (inChar != '*'

      // we have the string except for the '*', checksum and \r\n
      sBuffer[i++] = inChar; // add '*'
      inChar = Serial.read(); // read first byte of checksum
      sBuffer[i++] = inChar;
      inChar = Serial.read(); // read in second byte of checksum
      sBuffer[i++] = inChar;
      sBuffer[i] = '\0'; // add null (we'll add \r\n before sending to radio)
    } // end else
    // send out data for debug check
    Serial.println(sBuffer);

    // delay(50);

  } // end while (Serial.available())
} // end loop()

I am writing this in the Arduino 1.0 environment and running presently on a Duemilanove (once it works, the operational platform will be a pro mini). I'll take any advice I can get on how to break this serial input into strings and then do comparisons on it.

Thanks for any help you can give me!

If Serial.available() is non-zero it does not mean that you can read the whole line. It may mean that it only has one character. You can't just go reading lots of characters because then Serial.read() will return -1 instead of something useful.
You have to read and store the data up to a line feed and then parse it. Then read the next line etc.
There are a lot of topics in this particular forum which cover this sort of thing.

Pete

I would approach it this way:

/*----------------------------------------------------------------------------*/
/* Wait for and return a single character from the serial line                */
/*----------------------------------------------------------------------------*/
char getc(void)
{  while (!(Serial.available() > 0)) /* idle() */ ;
   return Serial.read();
}
/*----------------------------------------------------------------------------*/
/* Find '

, save everything to and including newline, and add NUL terminator /
/
----------------------------------------------------------------------------*/
char *getl(char *s,unsigned len)
{  char *b;
  int i;

do
  {  while (getc() != '


) ;
      for (b = s,i = 0; (i < len) && ((*b++ = getc()) != '\n'); i++) ;
      *b = '\0';
   }  while (strnmcp(s,"GPGGA",5));
   return s;
}

You can load the below test code and then copy/paste your strings in the serial monitor and send to see the result.

//zoomkat 11-8-11 simple delimited ',' string parce 
//from serial port input (via serial monitor)
//and print result out serial port
// CR/LF could also be a delimiter

String readString;

void setup() {
  Serial.begin(9600);
  Serial.println("serial delimit test 0022"); // so I can keep track of what is loaded
}

void loop() {

  //expect a string like wer,qwe rty,123 456,hyre kjhg,
  //or like hello world,who are you?,bye!,
  while (Serial.available()) {
    delay(1);  //small delay to allow input buffer to fill
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      break; //breaks out of capture loop to print readstring
    }  
    readString += c; //makes the string readString
  }

  if (readString.length() >0) {
    Serial.println(readString); //prints string to serial port out

    readString=""; //clears variable for new input
  }
}

There is a TinyGPS library that can read data as it arrives, and parse some sentences. At the very least, you can use that as inspiration for writing your own reader/parser.

Thank you all for the replies.

First, a little more background for the project. My daughter got the idea of launching a stratospheric balloon to measure temperature and pressure as a function of altitude to 25-30km as a science fair project. This is her first programming experience so I have been trying to abstract some of the details of reading the GPS, writing to the radio and reading the pressure sensor so that she has some simple commands to call. She's taken to it fairly well. While I was not expecting to have any issues myself, this turned into a good exercise for her to see how to ask for help.

I'm always glad to be a role model (my pride doesn't always enjoy it but, oh well). :slight_smile:

@PaulS: I was looking at TinyGPS. The problem is that I was trying to read the string in its entirety, stick some sensor values on the end of the string and then transmit the new string out. TinyGPS will parse the GPS string for me but then I would have had to reassemble it for my purposes. I certainly could do it that way but, for this project, it was supposed to be simpler for my daughter to do it as one chunk. I have another project in the pipeline where I think TinyGPS will work perfectly, though.

@Morris: That code chunk works beautifully. Thanks! I had tried something similar (not what I posted) early on but I never got it to work. I'll have to dissect the two of them and see what I was doing wrong. At least in part, I think many of the things I tried were getting stuck on an oversimplified assumption of how Serial.available() would deal with a serial stream which was not necessarily continuous (or, rather, how I should deal with). But, I grabbed your example and within about 30 seconds had GPGGA strings (and only those) coming at me with 1Hz regularity. My daughter has now finished her side of the code so, tonight/tomorrow we'll do end-to-end testing and a battery run-down lifetime test. Weather may even cooperate for a launch this weekend.

Again, thanks to all.

A great father-daughter project. If she has any interest in the programming, she might get a kick out of learning that the code I posted is remarkably similar to that used to retrieve commutated telemetry data from the TIROS-N weather satellites. :slight_smile: