save serial data to variable?

I’m using the code from the GPS tutorial at the playground to get data from my gps unit, its working great, now what I want to do is have 2 variables (lat and long) to be constantly updated from the gps unit.

 #include <string.h>
 #include <ctype.h>

 int ledPin = 13;                  // LED test pin
 int rxPin = 0;                    // RX PIN 
 int txPin = 1;                    // TX TX
 int byteGPS=-1;
 char linea[300] = "";
 char comandoGPR[7] = "$GPRMC";
 int cont=0;
 int bien=0;
 int conta=0;
 int indices[13];

 void setup() {
   pinMode(ledPin, OUTPUT);       // Initialize LED pin
   pinMode(rxPin, INPUT);
   pinMode(txPin, OUTPUT);
   Serial.begin(4800);
   for (int i=0;i<300;i++){       // Initialize a buffer for received data
     linea[i]=' ';
   }   
 }

 void loop() {
   digitalWrite(ledPin, HIGH);
   byteGPS=Serial.read();         // Read a byte of the serial port
   if (byteGPS == -1) {           // See if the port is empty yet
     delay(100); 
   } else {
     linea[conta]=byteGPS;        // If there is serial port data, it is put in the buffer
     conta++;                      
     Serial.print(byteGPS, BYTE); 
     if (byteGPS==13){            // If the received byte is = to 13, end of transmission
       digitalWrite(ledPin, LOW); 
       cont=0;
       bien=0;
       for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPR
         if (linea[i]==comandoGPR[i-1]){
           bien++;
         }
       }
       if(bien==6){               // If yes, continue and process the data
         for (int i=0;i<300;i++){
           if (linea[i]==','){    // check for the position of the  "," separator
             indices[cont]=i;
             cont++;
           }
           if (linea[i]=='*'){    // ... and the "*"
             indices[12]=i;
             cont++;
           }
         }
         Serial.println("");      // ... and write to the serial port
         Serial.println("");
         Serial.println("---------------");
         for (int i=0;i<12;i++){
           switch(i){
             case 0 :Serial.print("Time in UTC (HhMmSs): ");break;
             case 1 :Serial.print("Status (A=OK,V=KO): ");break;
             case 2 :Serial.print("Latitude: ");break;
             case 3 :Serial.print("Direction (N/S): ");break;
             case 4 :Serial.print("Longitude: ");break;
             case 5 :Serial.print("Direction (E/W): ");break;
             case 6 :Serial.print("Velocity in knots: ");break;
             case 7 :Serial.print("Heading in degrees: ");break;
             case 8 :Serial.print("Date UTC (DdMmAa): ");break;
             case 9 :Serial.print("Magnetic degrees: ");break;
             case 10 :Serial.print("(E/W): ");break;
             case 11 :Serial.print("Mode: ");break;
             case 12 :Serial.print("Checksum: ");break;
           }
           for (int j=indices[i];j<(indices[i+1]-1);j++){
             Serial.print(linea[j+1]); 
           }
           Serial.println("");
         }
         Serial.println("---------------");
       }
       conta=0;                    // Reset the buffer
       for (int i=0;i<300;i++){    //  
         linea[i]=' ';             
       }                 
     }
   }
 }

I’m assuming I would just declare 2 new ints with the rest of the variables, but I dont know how I should get them set to the latitude and longitude. Any help?

At a guess (and with only a quick glance at the code), it looks like all the GPS data is held in the linea[] buffer. indices[] seems to contains 13 integers which refer to the position in linea[] of each item of data.

So, the third item of data (which according to the switch statement is latitude) starts in linea[] at the position indicated by indices[2]. To extract it, you'd probably have to grab the bytes between indices[2] and indices[3].

As I said, I'm guessing a bit here so can only apologise if I'm wrong.

ok I’ve looked at it a little more closely and I think I can just do this

         for (int i=0;i<12;i++){
           switch(i){
             case 0 :Serial.print("Time in UTC (HhMmSs): ");break;
             case 1 :Serial.print("Status (A=OK,V=KO): ");break;
             case 2 :Serial.print("Latitude: ");break;
             case 3 :Serial.print("Direction (N/S): ");break;
             case 4 :Serial.print("Longitude: ");break;
             case 5 :Serial.print("Direction (E/W): ");break;
             case 6 :Serial.print("Velocity in knots: ");break;
             case 7 :Serial.print("Heading in degrees: ");break;
             case 8 :Serial.print("Date UTC (DdMmAa): ");break;
             case 9 :Serial.print("Magnetic degrees: ");break;
             case 10 :Serial.print("(E/W): ");break;
             case 11 :Serial.print("Mode: ");break;
             case 12 :Serial.print("Checksum: ");break;
           }
           for (int j=indices[i];j<(indices[i+1]-1);j++){
             if (j == 2) lat = linea[j+1];
             if (j == 4) long = linea[j+1];
             Serial.print(linea[j+1]);

though now I’m not sure what the purpose of (indices[i+1]-1) is.

The "indices" is an array of pointers to the sub-strings in the NMEA sentence. Because it is an ASCII string, you can't simply assign the value to a numeric variable. You'll need to use "atoi" or something similar.

so what about

           for (int j=indices[i];j<(indices[i+1]-1);j++){
             if (j == 2){
                  char  temp1[] = linea[j+1];
                  lat = atoi(temp);
             }
             if (j == 4){
                  char temp2[] =  linea[j+1];
                  long = atoi(temp2);
             }
             Serial.print(linea[j+1]);

never used atoi before but from what I looked up it seems like that should work. Maybe?

No, that won't work. "atoi" expects null-terminated strings. You could copy from the NMEA string using the "indices" array to a temporary array, terminate that, and then use "atoi". Maybe terminate in-place? (not sure if the NMEA syntax has sufficient space for that - it probably does.)

[edit]This:

char temp2[] =  linea[j+1];

certainly won't work.[/edit]

You could copy from the NMEA string using the "indices" array to a temporary array, terminate that, and then use "atoi".

I'm not really sure how I could go about that. a new array is obviously necessary but would I just have 2 entries, latitude and longitude, and have them pull from indices? but how? sorry I'm still learning.

I'd probably use "memcpy" and the data from the "indices" array to work out the start and length, then terminate the temporary array afterwards.

ok so lets say I make a new array

char temp[]

then memcpy(temp, indices[2], size?) memcpy(temp, indices[4], size?)

then lat = atoi(temp[0]); long = atoi(temp[0]);

?

Yes, but you'll have to dimension your temporary string, figure out what "size?" is, and terminate your array.

Then you need to fix this:

lat = atoi(temp[0]);

dimension the temporary string? you mean char temp[20]

size would be the length of the latitude/longitude (ex 4428.2011) so 9 +1 for the null

but now to get the lat variable its data would I need a for loop going from 0-9 with doing atoi until the null? and then do that from 10-20 for long?

It doesn’t seem like a difficult problem (taking data that is printed out and setting a variable to it) but I just cant seem to figure it out.

This is pretty urgent, so any help would be greatly appreciated.

but now to get the lat variable its data would I need a for loop going from 0-9 with doing atoi until the null?

You need a loop, going from the starting position in the linea array to the ending position in the linea array, to copy the character into the temp array.

After all the data is copied into the temp array, NULL terminate the array.

After the array is NULL terminated, pass the array to atoi. You do not pass individual characters to atoi.

I’ve been busy the past few days but I’m wondering if this would work. make 2 new arrays called tempLat and tempLong

tempLat[10] //0-9 for the lat digits, 10 for null
tempLong[11] //0-10 for the long digits, 11 for null

        for (int i=0;i<12;i++){
           switch(i){
             case 0 :Serial.print("Time in UTC (HhMmSs): ");break;
             case 1 :Serial.print("Status (A=OK,V=KO): ");break;
             case 2 :Serial.print("Latitude: ");break;
             case 3 :Serial.print("Direction (N/S): ");break;
             case 4 :Serial.print("Longitude: ");break;
             case 5 :Serial.print("Direction (E/W): ");break;
             case 6 :Serial.print("Velocity in knots: ");break;
             case 7 :Serial.print("Heading in degrees: ");break;
             case 8 :Serial.print("Date UTC (DdMmAa): ");break;
             case 9 :Serial.print("Magnetic degrees: ");break;
             case 10 :Serial.print("(E/W): ");break;
             case 11 :Serial.print("Mode: ");break;
             case 12 :Serial.print("Checksum: ");break;
           }
           for (int j=indices[i];j<(indices[i+1]-1);j++){
             int n=0;
             int m=0;
             Serial.print(linea[j+1]);
             if (i == 2)
             {
               tempLat[n] = linea[j+1];
               n++;
             }
             if (i == 4)
             {
               tempLong[m] = linea[j+1];
               m++;
             }
           }

now what about that code? I know n=0 and m=0 there is dumb but thats just to show the variables. In actuality I would set them to 0 at the beginning of the loop function. However, I think this would store the latitude and longitude each as their own array of chars. Then I could atoi each array right? and then I’d have each array stored into an int?

Also, these numbers are going to have 4 decimal places, so I have to use floats? so I’d use atof instead of atoi?

for (int j=indices[i];j<(indices[i+1]-1);j++){
             int n=0;
             int m=0;

Each time through the loop, n and m will be reset to 0. Incrementing n and m within the loop is a waste of effort, then.

Move the declarations if n and m outside of the loop.

Also, these numbers are going to have 4 decimal places, so I have to use floats? so I’d use atof instead of atoi?

Right now, you are printing the values of the lat and lon strings. Do the values that you print contain decimal points? If so, then you should convert the strings to floats (using atof). Otherwise, convert them to integers (or longs) using atoi (or atol).

yeah I know n and m should be moved. they'll be put at the beginning of loop().

And yes, the values that are printed each have 4 decimal places. So thanks for confirming about the atof.

To null terminate the 2 new arrays do I have to do anything? or will it be fine since I made the arrays 1 larger than they need to be so the last spot should be a null right?

To null terminate the 2 new arrays do I have to do anything? or will it be fine since I made the arrays 1 larger than they need to be so the last spot should be a null right?

Suppose the string initially contains "...,45.00034,60.9998,..." (where lat is 45.00034 and lon is 60.9932). You will copy 45.00034 into the lat array, and 60.9998 into the lon array.

You'll call atof, and the conversion to floats will work fine.

Now, the gps moves, so the string contains "...,45.0004, 61.0,...". You will copy 45.0004 into the lat array, which will then contain 45.00044. You will then copy 61.0 into the lon array, which will now contain 61.0998.

Your call as to whether you think it is necessary to NULL terminate the arrays.