Remplir un tableau à 2 dimensions avec une trame NMEA

Bonjour,
Voila un moment que j'essaye de récupérer les données de mon module GSM dans un tableau à deux dimensions.

Pour cette trame :
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A

Mon tableau aurait cette tête :

{{'$','G','P','R','M','C'},
{'1','2','3','5','1','9'},
{A},
{'4','8','0','7','.','0','3','8'},
{'N'},

[...]

{'0','0','3','.','1'},
{'W','*','6','A'}}

Le code doit fonctionner aussi bien pour une trame RMC que pour une trame GGA.

Voici mon code :

void test() {
  Serial.begin(9600);  
  char *stringptr = GPS.lastNMEA();
  int maxfieldsize = 0;
  int currentfieldsize = 0;
  int nbfield = 1;
  int sizeOfString = strlen(stringptr);
  
  String sFieldNb = "";
  String sMaxSize = "";
  //Je cast pour la concaténation
  String sSize = (String) sizeOfString;
  Serial.println("--+--+--: Size of String : "+sSize);

  // On parcours la chaîne
  for (int i=0; i < sizeOfString; i++) {
    // Si on a une virgule
    if (stringptr[i] == ',') {
      // Et que le champs est le plus grand rencontré
      if (currentfieldsize > maxfieldsize) {
        // Mise à jour de la taille maxi
        maxfieldsize = currentfieldsize;
      }
      nbfield++;
      currentfieldsize = 0;

      sFieldNb = (String) nbfield;
      sMaxSize = (String) maxfieldsize;
      
      delay(100);
      Serial.println("--+--: Entering New Field (nb: "+sFieldNb+" / maxSize: "+sMaxSize+")");
      delay(100);
      // Si ce n'est pas une virgule
    } else {
      currentfieldsize++;
      String sChar = (String) stringptr[i];
      delay(100);
      Serial.println("--: New char Counted: "+ sChar);
      delay(100);
    }
  }

  // Si le champs est le plus grand jamais rencontré  
  if (currentfieldsize > maxfieldsize) {
    // Mise à jour de la taille maxi
    maxfieldsize = currentfieldsize;
  }

  String sNbField = (String) nbfield;
  Serial.println("--D--: Number of field : "+ nbfield);
  String sMaxFieldSize = (String) maxfieldsize;
  Serial.println("--D--: Max Field Size  : "+ sMaxFieldSize);
  // On déclare tab2d avec une taille désormais connue
  char ParsedNMEA[nbfield][maxfieldsize+1];

  int currentfieldnumber = 0;
  int currentcharnumber  = 0;

  // On parcour la chaine 
  for (int i=0; i < strlen(stringptr); i++) {
    // Si on a une virgule
    if (stringptr[i] == ',') {

      // On termine la sous-chaîne par un '\0'
      ParsedNMEA[currentfieldnumber][currentcharnumber] = '\0';
      // On avance sur le champs courant
      currentfieldnumber++;
      currentcharnumber = 0;
      // Si ce n'est pas une virgule
    } else {
      // On ajoute au tableau
      ParsedNMEA[currentfieldnumber][currentcharnumber] = stringptr[i];
      // On avance sur la sous_chaîne
      currentcharnumber++;
    }
  }
  // On parcours le tableau
  for (int i=0; i<strlen(ParsedNMEA[i]); i++) {
    for (int j=0; j<strlen(ParsedNMEA[j]); j++) {
      // Si on arrive au caractère de fin de chaîne
      if (ParsedNMEA[i][j] == '\0') {
        break;
      } else {
        // On affiche les chaînes
        Serial.print(ParsedNMEA[i][j]);
      }
    }
    Serial.print('\n');
  }
      int tailleLatitude= strlen(ParsedNMEA[1]);
      char myLatitude[tailleLatitude];
      for (int i=0; i<strlen(ParsedNMEA[i]); i++) {
        for (int j=0; j<strlen(ParsedNMEA[j]); j++) {
          Serial.print(ParsedNMEA[i][j]);
        }         
      }
  
}

Et voici mon moniteur série (j'ai brouillé mes coordonnées GPS bien sûr :wink: ) :

-+--+--: Size of String : 68
--: New char Counted: 

--: New char Counted: $
--: New char Counted: G
--: New char Counted: P
--: New char Counted: R
--: New char Counted: M
--: New char Counted: C
--+--: Entering New Field (nb: 2 / maxSize: 7)
--: New char Counted: 1
--: New char Counted: 2
--: New char Counted: 1
--: New char Counted: 7
--: New char Counted: 4
--: New char Counted: 1
--: New char Counted: .
--: New char Counted: 0
--: New char Counted: 0
--+--: Entering New Field (nb: 3 / maxSize: 9)
--: New char Counted: A
--+--: Entering New Field (nb: 4 / maxSize: 9)
--: New char Counted: x
--: New char Counted: x
--: New char Counted: x
--: New char Counted: x
--: New char Counted: .
--: New char Counted: 1
--: New char Counted: 1
--: New char Counted: 6
--: New char Counted: 2
--: New char Counted: 5
--+--: Entering New Field (nb: 5 / maxSize: 10)
--: New char Counted: N
--+--: Entering New Field (nb: 6 / maxSize: 10)
--: New char Counted: x
--: New char Counted: x
--: New char Counted: x
--: New char Counted: x
--: New char Counted: 5
--: New char Counted: .
--: New char Counted: 7
--: New char Counted: 8
--: New char Counted: 5
--: New char Counted: 3
--: New char Counted: 5
--+--: Entering New Field (nb: 7 / maxSize: 11)
--: New char Counted: E
--+--: Entering New Field (nb: 8 / maxSize: 11)
--: New char Counted: 0
--: New char Counted: .
--: New char Counted: 7
--: New char Counted: 3
--: New char Counted: 7
--+--: Entering New Field (nb: 9 / maxSize: 11)
--+--: Entering New Field (nb: 10 / maxSize: 11)
--: New char Counted: 2
--: New char Counted: 9
--: New char Counted: 0
--: New char Counted: 5
--: New char Counted: 1
--: New char Counted: 7
--+--: Entering New Field (nb: 11 / maxSize: 11)
--+--: Entering New Field (nb: 12 / maxSize: 11)
--+--: Entering New Field (nb: 13 / maxSize: 11)
--: New char Counted: A
--: New char Counted: *
--: New char Counted: 7
--: New char Counted: 1
--: New char Counted: 

 of field : 
--D--: Max Field Size  : 11

$
12

$12    //la chaîne finale

J'ai essayé de prendre des bonnes habitudes de programmation mais mon petit doigt me dit c'est pas encore le top niveau..

J'utilise la librairie suivante :

Si quelqu'un à une solution pour que je remplisse mon tableau correctement merci !

explorez cette piste (mettez le moniteur série à 115200 et regardez ce que ça affiche) - ça devrait vous donner une idée

char trame[] = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A";

void setup() {
  Serial.begin(115200);
  
  Serial.print("Analyse de : [");
  Serial.print(trame);
  Serial.println("]");

  // ==================================================================
  const char delim[] = ",";
  char * item;
  int i = 0;

  //http://www.cplusplus.com/reference/cstring/strtok/
  // attention strtok va modifier la trame en mettant des '\0' sur les délimitations (tokens).
  item = strtok (trame, delim);
  while (item != NULL) {
    Serial.print(i++);
    Serial.print("\t");
    Serial.println(item);
    item = strtok(NULL, delim);
  }
  // ==================================================================
}

void loop() {}

vous devriez voir cela dans la console:

</sub> <sub>Analyse de : [$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A] 0 $GPRMC 1 123519 2 A 3 4807.038 4 N 5 01131.000 6 E 7 022.4 8 084.4 9 230394 10 003.1 11 W*6A</sub> <sub>

Bonjour,

Bien que vous m'ayez lâchement abandonné sur mon post précédent votre aide est toujours très appréciée :stuck_out_tongue:
Trêve de plaisanterie...

J'ai bien le même résultat que vous dans un sketch simple.
Mais quand j'essaye d'incorporer cela à mon code ça se complique.

J'ai remplacé trame par stringptr et voici le résultat :

0 
$GPRMC
1 070015.00
2 V
3 300517
4 N*7E

Dans mon implémentation j'ai une fonction readAndWriteData() qui appelle SplitString() :

void readAndWriteData() {
  char c = GPS.read();
  if (GPSECHO)
     if (c)   Serial.print(c);

  // if a sentence is received, we can check the checksum, parse it.
  if (GPS.newNMEAreceived()) {
    
    // This sets the newNMEAreceived() flag to false.
    if (!GPS.parse(GPS.lastNMEA())) { 
      
      // We can fail to parse a sentence in which case we should just wait for another.
      return;  
    }
    
    // Sentence parsed! 
    //Serial.println("OK");
    if (LOG_FIXONLY && !GPS.fix) {
        Serial.print("No Fix");
        return;
    }

    // Log the sentence.
    //Serial.println("Log");
     
    char *stringptr = GPS.lastNMEA();
    SplitString();

    [...] //a lot more stuff here

Et readAndWriteData() est appelée dans un case, dans la loop de mon programme principal :

[...]
    case GET_RMC_GGA:
      delay(1000);
      Serial.println("\n======================================");
      Serial.println("G.P.S TURN TO WORK.");
      Serial.println("======================================\n");
      delay(1000);
      while (1){
        readAndWriteData();
        //frametype is a simple flag (temporary)
        if(frametype == 1){
          Serial.println("\nEnd of the RMC frame");
          frametype = 0;
          break;
        }else if (frametype == 2){
          Serial.println("\nEnd of the GGA frame");
          frametype = 0;
          break;
        }
      }
      
      state = ANOTHER_STATE;
      break; 
[...]

Mon implémentation ne doit pas être très rigoureuse.
Savez-vous pourquoi je n'ai pas le même résultat qu'avec votre exemple dans un programme simple ?

    char *stringptr = GPS.lastNMEA();ce bout de code va pointer vers la chaîne en mémoire dans l'instance GPS... ce n'est pas une bonne idée de jouer avec :slight_smile:

ce qu'il faudrait c'est dupliquer la chaîne du GPS dans une variable locale et ensuite aller effectuer l'analyse sur celle ci

PS/ pour l'autre post j'ai supposé que vous aviez les éléments pour avancer

En effet, pas une très bonne idée :stuck_out_tongue:

J'ai donc procédé comme ceci :

    char oneFrame[75];
    for(int x=0; x<75;x++){
      oneFrame[x] = stringptr[x];
    }

Puis j'ai passé oneFrame en paramètre d'une fonction qui comporte le code que vous m'avez indiqué.

Tout me semble bien, sauf la fin !

10 ...
11 W*6A ==> W est un champ ! Le dernier séparateur de la trame est une étoile malheureusement...

Je pourrais m'en passer, ou alors je réutilise la même fonction en changeant le caractère de séparation et le tour est joué.

Par contre je ne comprend pas très bien strtok.. "This function returns a pointer to the last token found in the string." Un pointeur sur la dernière virgule ? Mais comment je fais pour remplir un tableau à deux dimensions avec ça ? :stuck_out_tongue:

Merci encore !

Si je rajoute de quoi connaitre la taille maxi d'un champ (voir "ICI"):

void Split(char maTrame[75]) {
  Serial.print("Analyse de : [");
  Serial.print(maTrame);
  Serial.println("]");
  // ======================
  const char delim[] = ",";
  char * item;
  int i = 0;
  int currentfieldsize = 0;
  int maxfieldsize = 0;
  //http://www.cplusplus.com/reference/cstring/strtok/
  // attention strtok va modifier la trame en mettant des '\0' sur les délimitations (tokens).
  item = strtok (maTrame, delim);
  while (item != NULL) {
    Serial.print(i++);
    Serial.print("\t");
    Serial.println(item);
    item = strtok(NULL, delim);

      //----------------ICI------------------------
      currentfieldsize = strlen(item);
      Serial.print(currentfieldsize);
      if (currentfieldsize > maxfieldsize) {
        // Mise à jour de la taille maxi
        maxfieldsize = currentfieldsize;
      }
  }  
  //---------------LA-----------------------
  char Tab2d[i][maxfieldsize]; 
}
      }

Je peux alors déclarer mon tableau 2d (voir "LA") mais comment le remplir ?

pour prendre en compte l'étoile comme séparateur en plus de la virgule (si elle n'est pas utilisée ailleurs) il suffit de faire  const char delim[] = ",*"; pour dire à la fonction strtok() qu'il y a plusieurs séparateurs possibles

pour l'allocation d'un buffer local - assurez vous de prendre "assez long" pour toute la trame + 1 caractère nul en fin de chaîne

Pour copier les data dans le buffer local pas besoin de boucle for - utilisez par exemple la fonction strcpy() qui copiera tout jusqu'au '\0' de fin de chaîne (cf les fonctions définies dans stdlib.h et string.h)

Si vous faites une allocation dynamique (pas sur la pile, mais avec un malloc() par exemple) du buffer local et que vous copiez dedans la trame NMEA, alors vous pouvez juste conserver la liste des pointeurs retournés par strtok() comme étant les points d'entrées vers vos chaînes de caractères. la fonction strtok() rajoutant les '\0' sur les séparateurs pour vous

voilà comment ça fonctionne par appel successif: dans le premier appel on done le buffer sur lequel on va travailler, ensuite on appelle en passant NULL comme paramètre et la fonction va chercher dans le même buffer qu'avant, après le '\0' qu'il aura mis la prochaine occurence de votre séparateur, jusqu'à arriver à la fin de chaîne

ça ressemble donc un peu à cela:

comme vous savez combien d'éléments il y à extraire (c'est toujours pareil dans une trame NMEA de votre GPS je crois) il suffit de mémoriser les pointeurs item obtenus dans un tableau, pas besoin de vous embêter à allouer encore plus de mémoire.

OK ?

C'est très bien détaillé mais je ne sais pas faire cela :cry:

Pour le malloc() j'imagine que c'est ça :

    char * oneFrame = (char*) malloc(100 * sizeof(char));
      strcpy(oneFrame ,stringptr);
    Split(oneFrame);

par contre je ne sais pas comment conserver la liste des pointeurs retournés par strtok() !

Un truc du genre

// en global
  char * tableauDePointeurs[12]; // je crois qu'il y en a 12, c'est ça? 

...

// dans la fonction

  char * item;
  int i = 0;

  item = strtok (trame, delim);
  while (item != NULL) {
    tableauDePointeurs[i++] = item; // on stocke le pointeur à l'emplacement i et ensuite on incrémente i
    item = strtok(NULL, delim);
  }
// ici tableauDePointeurs contient tous les pointeurs comme sur mon dessin
// et i vous dit combien il y en a.
// je n'ai pas testé le débordement de tableau, vaut mieux le faire

Il a fallu que je mette la taille à 24 pour avoir ceci :

$GPRMC,165421.00,A,4454.55779,N,00453.23522,E,1.303,,300517,,,A*7A
]
0	
$GPRMC
2	165421.00
4	A
6	4454.55779
8	N
10	00453.23522
12	E
14	1.303
16	300517
18	A*7A

Mon tableau de pointeurs : 

$GPRMC

165421.00

A

4454.55779

N

00453.23522

E

1.303

300517

A*7A

si je mets la taille à 12 j'ai que la moitié des éléments comme ceci :

0	
$GPRMC
2	165826.00
4	A
6	4454.55976
8	N
10	00453.24528
12	E
14	0.577
16	300517
18	A*79

Mon tableau de pointeurs : 

$GPRMC

165826.00

A

4454.55976

N

Une explication ?

Hum en fait j'ai une idée, en regardant le moniteur (2 4 6 8 ...) à un moment je fais :
Serial.print(i++);

ça n'incrémente pas vraiment i si ??
en quel cas je l'incrémente deux fois...

  char *stringptr = GPS.lastNMEA();
  Serial.print("Analyse de : [");
  Serial.print(trame);
  Serial.println("]");
  const char delim[] = ",";
  char * item;
  int i = 0;
  item = strtok (trame, delim);
  while (item != NULL) {
    Serial.print(i++);
    Serial.print("\t");
    Serial.println(item);
    tableauDePointeurs[i++] = item;
    item = strtok(NULL, delim);
  }
  Serial.println("Mon tableau de pointeurs : ");
  for (int x=1 ; x<12 ; x++){
  Serial.println(tableauDePointeurs[x]);
  }
    
  }

En tout cas merci vous m'avez été d'une grande aide !

Hum en fait j'ai une idée, en regardant le moniteur (2 4 6 8 ...) à un moment je fais :
Serial.print(i++);

ça n'incrémente pas vraiment i si ??

bravo vous avez trouvé votre bug :slight_smile:

Re-bonjour !

J'ai une petit question (pour changer).
J'aimerais que ma fonction Split me retourne mon tableau de pointeurs !

J'ai essayé un truc comme ça :

char** Split(char frame[]){
  // NMEA frames delimiter
  const char delim[] = ",";
  // We will split the frame into items 
  char * item;
  // Used to move forward into the pointer array
  int PointerArrayIndex = 0;

  // Notice that this string is modified by being broken into smaller strings.
  item = strtok (frame, delim);
  while (item != NULL) {
    Serial.print("\t");
    Serial.println(item);
    PointerArray[PointerArrayIndex++] = item;
    item = strtok(NULL, delim);  
  }

  return PointerArray;
}

Puis en appelant la fonction comme ceci :

    char * OneFrame = (char*) malloc(100 * sizeof(char));
    strcpy(OneFrame ,stringptr);
    // ici
    char * res[16] = Split(OneFrame);

Mais le compilateur est pas super contant.
C'est bien char** le type de ma fonction ?
J'ai mis ça parce qu'à un moment le compilateur m'a dit "can't convert from char** to char* in return" ou un truc similaire..

j'aimerais que ma fonction me retourne mon tableau pour poursuivre mon traitement,

typiquement, donner l'indice du champ latitude à une autre fonction qui va convertir les degrés minute seconde en degrés décimaux comme ceci :

float MonDegreDecimal = degMinSecToDecimalDeg(PointerArray[3]);

Encore une fois une petit interrogation... si c'est une trame RMC l'indice est bien 3 (le 4eme champ) mais si c'est une trame GGA on a un soucis, l'indice du champ latitude est 2 (le 3eme champ)...

Cette fonction de conversion est très très, très sale mais fonctionne bien, bon ok je vous la montre mais ne m'insultez pas x)

float degMinSecToDecimalDeg(char stringToConvert[11]){

char degree[11];
for(int i = 0; i<11; i++){
  degree[i] = stringToConvert[i];
}

float decimalDegree = (int)degree[0] -48;
float unitDegree = (int)degree[1] -48;
float Degree = (decimalDegree*10)+unitDegree;
float decimalMinute = (float)degree[2] -48;
float unitMinute = (float)degree[3] -48;
float tenthMinute = (float)degree[5] -48;
float hundredthMinute = (float)degree[6] -48;
float thousandthMinute = (float)degree[7] -48;
float tenThousandthMinute = (float)degree[8] -48;
float hundredThousandthMinute = (float)degree[9] -48;
float Minute = ((decimalMinute*10) + unitMinute + (tenthMinute/10) + (hundredthMinute/100) + (thousandthMinute/1000) + (tenThousandthMinute/10000) +(hundredThousandthMinute/100000))/60;
float DecimalDegree = Degree + Minute;
Serial.println("Decimal degree value : ");
Serial.println(DecimalDegree,7);
return DecimalDegree;
}

Du coup je me suis dit que j'allais devoir faire une truc du style :

if (res[0] == "$GPRMC"){
float MonDegreDecimal = degMinSecToDecimalDeg(PointerArray[3]);
else if (res[0] == "$GPGGA"){
float MonDegreDecimal = degMinSecToDecimalDeg(PointerArray[2]);
} else { // pas de trame }

Mais je n'arrive pas à faire sortir mon tableau de pointeurs dans res pour pouvoir effectuer ce traitement.

vous ne pouvez pas faire ça en C    char * res[16] = Split(OneFrame);il faut créer le tableau d'abord et le passer à la fonction par référence par exemple pour que la fonction le remplisse.

ce n'est pas comme cela que l'on compare deux chaînes de caractères
if (res[0] == "$GPRMC"){(ce code teste l'égalité de la valeur du pointeur res[0] et la valeur du pointeur vers la chaîne constante "$GPRMC"... donc pas ce que vous voulez obtenir :slight_smile: )

Cf les fonctions de traitement des c-strings: stdlib.h et string.h par exemple -> dans votre cas utiliser strcmp()

Oui je me doutais bien pour la comparaison, c'était juste illustratif.

Il faut créer le tableau et le passer à la fonction ? en paramètre ?

char tab[16];
Split(OneFrame, tab);

la fonction doit être comme cela ?

char Split(char frame[], tab[]){}

puis remplir tab dans la fonction ?

 for (int x=1 ; x<16 ; x++){
 tab[x] = PointerArray[x];
 }

et elle retourne tab ?

return tab;

et c'est comme ça qu'on récupère tab ?

tab = Split(OneFrame, tab);

Je ne comprend plus trop..

vous pouvez passer le tableau par référence en donnant sa taille, dans ce cas la fonction sizeof() dans la fonction connait la taille du tableau (on peut tester un dépassement par exemple) -> int split(char buffer[], char* (&ptr)[MAXPTR])

Par exemple comme cela:

char trame[] = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A";
const int MAXPTR = 20;
char* pointeurs[MAXPTR];

int split(char buffer[], char* (&ptr)[MAXPTR]) // de cette façon sizof() marche dans la fonction sur ptr
{
  const char delim[] = ",*";
  char * item;
  int i = 0;

  Serial.print(F("Taille du tableau de pointeurs ")); Serial.println(sizeof(ptr)/sizeof(ptr[0])); // donne bien 20

  item = strtok (buffer, delim);
  while (item != NULL) {
    ptr[i++] = item; // pour bien faire il faudrait ici tester qu'on ne dépasse pas :)
    item = strtok(NULL, delim);
  }
  return i;
}

void setup() {
  int nbPtr;

  Serial.begin(115200);

  Serial.print("\nAnalyse de : [");
  Serial.print(trame);
  Serial.println("]\n");

  nbPtr = split(trame, pointeurs);
  Serial.print(F("\nJ'ai reconnu "));
  Serial.print(nbPtr);
  Serial.println(F(" éléments de trames\n"));
  for (int i = 0; i < nbPtr; i++) {
    Serial.print(i);
    Serial.print("\t");
    Serial.println(pointeurs[i]);
  }

  if (!strcmp(pointeurs[0], "$GPRMC")) Serial.println(F("\nTrame GPRMC reconnue"));
}

void loop() {}

va envoyer sur le moninteur Série:

[sub][color=blue]
Analyse de : [$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A]

[color=red]Taille du tableau de pointeurs 20[/color]

J'ai reconnu 13 éléments de trames

0	$GPRMC
1	123519
2	A
3	4807.038
4	N
5	01131.000
6	E
7	022.4
8	084.4
9	230394
10	003.1
11	W
12	6A

Trame GPRMC reconnue
[/color][/sub]

Ou alors vous passez un pointeur sur le tableau, mais alors au sein de la fonction vous avez perdu les informations de taille du tableau -> int split(char buffer[], char* ptr[])

char trame[] = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A";
const int MAXPTR = 20;
char* pointeurs[MAXPTR];

int split(char buffer[], char* ptr[]) // on perd l'information de taille du tableau alors
{
  const char delim[] = ",*";
  char * item;
  int i = 0;

  Serial.print(F("Taille du tableau de pointeurs ")); Serial.println(sizeof(ptr)/sizeof(ptr[0])); // va dire 1 !!!

  item = strtok (buffer, delim);
  while (item != NULL) {
    ptr[i++] = item;  // pour bien faire il faudrait ici tester qu'on ne dépasse pas :)
    item = strtok(NULL, delim);
  }
  return i;
}

void setup() {
  int nbPtr;

  Serial.begin(115200);

  Serial.print("\nAnalyse de : [");
  Serial.print(trame);
  Serial.println("]\n");

  nbPtr = split(trame, pointeurs);
  Serial.print(F("\nJ'ai reconnu "));
  Serial.print(nbPtr);
  Serial.println(F(" éléments de trames\n"));
  for (int i = 0; i < nbPtr; i++) {
    Serial.print(i);
    Serial.print("\t");
    Serial.println(pointeurs[i]);
  }

  if (!strcmp(pointeurs[0], "$GPRMC")) Serial.println(F("\nTrame GPRMC reconnue"));
}

void loop() {}

ça envoie sur le moniteur Série:

[sub][color=blue]
Analyse de : [$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A]

[color=red]Taille du tableau de pointeurs 1[/color]

J'ai reconnu 13 éléments de trames

0	$GPRMC
1	123519
2	A
3	4807.038
4	N
5	01131.000
6	E
7	022.4
8	084.4
9	230394
10	003.1
11	W
12	6A

Trame GPRMC reconnue
[/color][/sub]

Donc la première méthode - si vous avez des tableaux de taille connue - est la meilleure

Votre idée fonctionne bien.
J'ai mis du temps à assimiler mais maintenant j'ai compris !

Il a fallu faire quelques retouches.

if (!strcmp(pointeurs[0], "\n$GPRMC")) Ne me demandez pas pourquoi...

Et là ça devient intéressant :

1) De nouveau, ma fonction degMinSecToDecimalDeg() (pour pas aller la chercher plus haut) :

float degMinSecToDecimalDeg(char stringToConvert[]){

char degree[11];
for(int i = 0; i<11; i++){
  degree[i] = stringToConvert[i];
}

float decimalDegree = (int)degree[0] -48;
float unitDegree = (int)degree[1] -48;
float Degree = (decimalDegree*10)+unitDegree;
float decimalMinute = (float)degree[2] -48;
float unitMinute = (float)degree[3] -48;
float tenthMinute = (float)degree[5] -48;
float hundredthMinute = (float)degree[6] -48;
float thousandthMinute = (float)degree[7] -48;
float tenThousandthMinute = (float)degree[8] -48;
float hundredThousandthMinute = (float)degree[9] -48;
float Minute = ((decimalMinute*10) + unitMinute + (tenthMinute/10) + (hundredthMinute/100) + (thousandthMinute/1000) + (tenThousandthMinute/10000) +(hundredThousandthMinute/100000))/60;
float DecimalDegree = Degree + Minute;
Serial.println("Decimal degree value : ");
Serial.println(DecimalDegree,7);
return DecimalDegree;
}

2) Puis ma fonction Compare() :

boolean Compare(float current, float last){
  //If moving : 1 ; if not moving : 0
  boolean MovingOrNotMoving;
  float diff = current - last;

  Serial.println("current : ");
  Serial.println(current);
  Serial.println("last : ");
  Serial.println(last);

  // If the distantce between 2 degree of latitide is 111km, then 111*0.0003 is around 33 meters. If the difference is higher than 33 meters we consider it moving.
  if(diff <=0.0003 && diff >= -0.0003){
    MovingOrNotMoving = 0;
    Serial.println("current - last : ");
    Serial.println(diff,7);
    Serial.println("CA BOUGE PAS TROP EN CE MOMENT...");
  }else{
    MovingOrNotMoving = 1;
    Serial.println("current - last : ");
    Serial.println(diff,7);
    Serial.println("CA A BEAUCOUP BOUGÉ !!!!");
  }
return MovingOrNotMoving;  
}

3) Et voici ce que j'effectue :
Je récupère la latitude au bon endroit, selon le type de trame, et je la donne à manger à ma fonction degMinSecToDecimalDeg() qui me retourne la valeur en degrés décimaux. Je donne ensuite cette valeur à ma fonction Compare() qui me dit si la latitude a changé ou non.

 if (!strcmp(pointeurs[0], "\n$GPRMC")) {
    Serial.println(F("\nTrame RMC reconnue"));
    currentLat = degMinSecToDecimalDeg(pointeurs[3]);
    Serial.print("la latitude courante en degrés décimaux est : ");
    Serial.println(currentLat);
      Movingornot = Compare(currentLat, lastLat);
      lastLat = currentLat;
  }else if (!strcmp(pointeurs[0], "\n$GPGGA")){
    Serial.println(F("\nTrame GGA reconnue"));
    currentLat = degMinSecToDecimalDeg(pointeurs[2]);
    Serial.print("la latitude courante en degrés décimaux est : ");
    Serial.println(currentLat);
      Movingornot = Compare(currentLat, lastLat);
      lastLat = currentLat;
  }

4) Mon moniteur :

Trame GGA reconnue
Decimal degree value : 
xx.xxxxxxx //très belle latitude...
la latitude courante en degrés décimaux est : xx.xx
current : 
xx.xx
last : 
xx.xx //la même que current
current - last : 
0.0000000
CA BOUGE PAS TROP EN CE MOMENT...

Alors bien sûr il faut améliorer plusieurs points comme le 0.0003 approximatif, ou encore mettre lastLat qu'une seule fois à la fin des deux if. Il faudra également que je donne un sens au booléen de retour de Compare (pour l'affichage de "bouge" ou "bouge pas" , qui pour le moment est fait au sein de la fonction) et aussi que je fasse la même chose avec la longitude

Mais avant d'aller plus loin j'ai un gros soucis...

J'illustre :

Analyse de : [
$GPRMC,130231.88,V,,,,,,,310517,,,N*7E
]

Taille du tableau de pointeurs 20
 
$GPRMC
 130231.88
 V
 310517
 N*7E


J'ai reconnu 5 éléments de trames

0 
$GPRMC
1 130231.88
2 V
3 310517 //Si il manque des champs c'est la m*rde
4 N*7E



Avant de passer à la suite, regardons le contenu de pointeurs[0], pointeurs[2] et pointeurs[3] : 

$GPRMC
V
310517 // Ce qu'on s'apprête à passer à la fonction degMinSecToDecimalDeg (AIE AIE AIE !!)



Trame RMC reconnue
Decimal degree value : 
31.0869312 //Ce qu'on s'apprête à passer à la fonction Compare (AIE AIE AIE !!)
la latitude courante en degrés décimaux est : 31.09 // Au moins vous voyez pas ma latitude... xD
current : 
31.09
last : 
0.00
current - last : 
31.0869312
CA A BEAUCOUP BOUGÉ !!!! // Mytho

Du coup, il faudrait que je vérifie le nombre d'éléments de trames avant de donner l'indice du champs latitude à ma fonction ? Mais le truc c'est qu'il manque toujours un ou deux champs inutiles dans mes trames... donc le nombre de champs varie.

Il faudrait que je vérifie qu'au champ [2] (pour GGA) et [3] (pour RMC) il y a EFFECTIVEMENT une latitude avant de la donner à ma fonction.

Sinon c'est carnage :smiley:

Comment faire cela ?

Le \n il vient probablement de la trame précédente - comment faites vous les acquisitions?
Vérifiez vous que vous avez eu un fix avant de traiter la trame?

Sans voir tout votre code difficile de vous aider

Je ne pense pas que le fait de poster plus de code aide beaucoup, il est possible de voir le problème comme cela :

Les éléments de trames s'ajoutent à la suite dans le tableau, mais quand il manque des éléments dans la trame (par exemple la latitude) ça pose problème..

Un exemple de tableau qui pose problème :

$GPRMC
130231.88
V
310517
N*7E

Dans cet exemple, la trame est incomplète et du coup l'élément d'indice [3] n'est pas la latitude mais la date...

Le soucis c'est que ma fonction de conversion prend toujours l'élément d'indice [3] en paramètre...

Il faut que je m'assure que la trame soit complète ? Est-ce ce que vous entendez par vérifier que j'ai eu un fix ?

La trame n'est jamais vraiment complète. Et il ne faut pas que l'absence de "date" m'empêche de convertir "latitude".. Du coup, vérifier que la trame est complète n'est pas la meilleure solution.

Il faut plutôt que je vérifie qu'il y a bien "latitude" à l'indice [3], mais j'ignore comment...

peut-être en comparant l'indice [3] à une expression régulière comme celle-ci :

[n'importe quel suite de caractères numériques][suivie du caractère '.'][suivie de n'importe quel suite de caractères numériques]

Mais encore une fois ce n'est pas suffisant. Car dans le cas ou l'indice [3] abrite la variation magnétique ( 003.1 ) le test va passer car la variation magnétique est une expression similaire à la latitude...

Que voulez-vous réellement voir de plus dans mon code ?

L’acquisition se fait dans ma fonction readAndWriteData() :

void readAndWriteData() {
  char c = GPS.read();
  if (GPSECHO)
     if (c)   Serial.print(c);

  // if a sentence is received, we can check the checksum, parse it.
  if (GPS.newNMEAreceived()) {
    
    // This sets the newNMEAreceived() flag to false.
    if (!GPS.parse(GPS.lastNMEA())) { 
      
      // We can fail to parse a sentence in which case we should just wait for another.
      return;  
    }
    
    // Sentence parsed! 
    if (LOG_FIXONLY && !GPS.fix) {
        Serial.print("No Fix");
        return;
    }
     
    char *stringptr = GPS.lastNMEA();
    delay(3000);
    float currentLat;
    static float lastLat;
    boolean Movingornot;
    char * OneFrame = (char*) malloc(100 * sizeof(char));
    strcpy(OneFrame ,stringptr);
    char report[100];
    uint8_t stringsize = strlen(report);
    int nbPtr;
    Serial.print("\nAnalyse de : [");
    Serial.print(OneFrame);
    Serial.println("]\n");
  
    nbPtr = Split(OneFrame, pointeurs);
    Serial.print(F("\nJ'ai reconnu "));
    Serial.print(nbPtr);
    Serial.println(F(" éléments de trames\n"));
    for (int i = 0; i < nbPtr; i++) {
    Serial.print(i);
    Serial.print("\t");
    Serial.println(pointeurs[i]);

 if (!strcmp(pointeurs[0], "\n$GPRMC")) {
    Serial.println(F("\nTrame RMC reconnue !\n"));
    currentLat = degMinSecToDecimalDeg(pointeurs[3]);
      Movingornot = Compare(currentLat, lastLat);
      lastLat = currentLat;
      }else { // pareil mais avec "\n$GPGGA" et pointeurs[2]}

  }

Je ne pense pas que le fait de poster plus de code aide beaucoup,

Moi je pense que oui... le demon est souvent dans les détails que l'on ne nous dit pas...

Un exemple de tableau qui pose problème :

$GPRMC
130231.88
V
310517
N*7E

Dans cet exemple, la trame est incomplète et du coup l'élément d'indice [3] n'est pas la latitude mais la date...

la question est donc de comprendre pourquoi la trame est incomplète. soit le GPS n'a pas retourné un fix et vous ne vérifiez pas la cohérence de l'information, soit votre procédure de capture de l'info est tronquée, soit vous avez un bug quelque part qui vient modifier l'index d'écriture du buffer, bref 1001 raisons qui ne sont pas dans le bout de code que vous proposez à l'analyse...

Je vois un malloc() par exemple mais pas de free() ...

cf Snippets R Us

Si vous ne voulez pas poster tout votre code faites comme moi un petit bout de code indépendant de votre projet qui met en évidence ce qui vous chagrine

Mon module GPS fonctionne très bien, il est juste un peu lent à démarrer, et parfois (très rarement) il manque des champs dans les trames... le problème ne viens pas de mon code en soit, il faut simplement traiter le cas ou la trame est incomplète !!

Bon, voici un bout de mon automate :

  int frametype;
  const int MAXPTR = 20;
  // Pointer array
  char* pointeurs[MAXPTR];

const int INIT_GSM = 2;
const int GET_RMC_GGA = 5;
#define GPSECHO  true
#define LOG_FIXONLY false  
#include <Adafruit_GPS.h>
// Serial2 is slot B.
#define mySerial Serial2
Adafruit_GPS GPS(&mySerial);
uint8_t parseHex(char c) {
  if (c < '0')
    return 0;
  if (c <= '9')
    return c - '0';
  if (c < 'A')
    return 0;
  if (c <= 'F')
    return (c - 'A')+10;
}

boolean waitForString(const char * endMarker, unsigned long duration)
{
  //It varies when we check componants answer by comparing the received string to an expected answer.
  boolean endMarkerFound = false;
  int localBufferSize = strlen(endMarker); // we won't need an \0 at the end
  char localBuffer[localBufferSize];
  int index = 0;
  unsigned long currentTime;

  memset(localBuffer, '\0', localBufferSize); // clear buffer

  currentTime = millis();
  while (millis() - currentTime <= duration) {
    if (Serial1.available() > 0) {
      if (index == localBufferSize) index = 0;
      localBuffer[index] = (uint8_t) Serial1.read();
      Serial.print(localBuffer[index]);
      endMarkerFound = true;
      for (int i = 0; i < localBufferSize; i++) {
        if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
          endMarkerFound = false;
          break;
        }
      }
      index++;
    }
    if (endMarkerFound) break;
  }
  return endMarkerFound;
}

boolean gsmATCommand(const char * command, const char * endMarker, unsigned long duration)
{
  Serial1.println(command);
  return waitForString(endMarker, duration);
}



void setup() {
  Serial2.begin(9600);

void loop() {
  //Let it chill a bit 
  delay(500);
  //______________________________________________________________________________________________________________________________________________________switch (state)
  switch (state) {
    //__________________________________________________________________________________________________________________________________________________case WAIT_PROCESS
    case WAIT_PROCESS:
      delay(1000);
      if (Serial.available() > 0) {
        state = INIT_GPS;
      }
      break;
      
    //__________________________________________________________________________________________________________________________________________________case INIT_GPS
    case INIT_GPS:
      delay(500);
       Serial.println("\n=============================================================================");
      Serial.println("Flip&Click will initialize the GPS for RMC and GGA frame reception.");
       Serial.println("=============================================================================\n");

      //createLogFile();
      sendCommandToGps();
      //readFromGps();
      delay(200);
      //Serial.println("Reading gps answer...");
      //Serial.print(inputStringGPS);
      state = GET_RMC_GGA;
      break;

    //__________________________________________________________________________________________________________________________________________________case GET_RMC_GGA
    case GET_RMC_GGA:
      // Here we simply use readAndWriteData().
      delay(1000);
      Serial.println("\n=============================================================================");
      Serial.println("G.P.S TURN TO WORK.");
      Serial.println("=============================================================================\n");
      delay(1000);
      while (1){
        readAndWriteData();
        if(frametype == 1){
          Serial.println("\nEnd of the RMC frame");
          frametype = 0;
          break;
        }else if (frametype == 2){
          Serial.println("\nEnd of the GGA frame");
          frametype = 0;
          break;
        }
      }
      
      state = GET_RMC_GGA;
      break;

Ma fonction readAndWriteData() :

void readAndWriteData() {
  char c = GPS.read();
  if (GPSECHO)
     if (c)   Serial.print(c);

  // if a sentence is received, we can check the checksum, parse it.
  if (GPS.newNMEAreceived()) {
    
    // This sets the newNMEAreceived() flag to false.
    if (!GPS.parse(GPS.lastNMEA())) { 
      
      // We can fail to parse a sentence in which case we should just wait for another.
      return;  
    }
    
    // Sentence parsed! 
    if (LOG_FIXONLY && !GPS.fix) {
        Serial.print("No Fix");
        return;
    }
     
    char *stringptr = GPS.lastNMEA();
    delay(3000);
    float currentLat;
    static float lastLat;
    boolean Movingornot;
    char * OneFrame = (char*) malloc(100 * sizeof(char));
    strcpy(OneFrame ,stringptr);
    char report[100];
    uint8_t stringsize = strlen(report);
    int nbPtr;
    Serial.print("\nAnalyse de : [");
    Serial.print(OneFrame);
    Serial.println("]\n");
  
    nbPtr = Split(OneFrame, pointeurs);
    Serial.print(F("\nJ'ai reconnu "));
    Serial.print(nbPtr);
    Serial.println(F(" éléments de trames\n"));
    for (int i = 0; i < nbPtr; i++) {
    Serial.print(i);
    Serial.print("\t");
    Serial.println(pointeurs[i]);
  }


    
  if (!strcmp(pointeurs[0], "\n$GPRMC")) {
    Serial.println(F("\nTrame RMC reconnue !\n"));
    currentLat = degMinSecToDecimalDeg(pointeurs[3]);
      Movingornot = Compare(currentLat, lastLat);
      lastLat = currentLat;}else { //pareil mais avec "\nGPGGA" et pointeurs[2]}


//Cette partie là n'est pas encore à jour (utilisation de String):
String receivedString;
    for (int i=0;i<=strlen(stringptr);i++){
    receivedString += (String)stringptr[i];
    }
 if(frameType.equals(RMCType))
     {
     // stuff here
     frametype = 1;
}else if (frameType.equals(GGAType))
     { 
     // stuff here
     frametype = 2;}
}

Ma fonction Split() :

int Split(char frame[], char* (&ptr)[MAXPTR]){
  
  // NMEA frames delimiter
  const char delim[] = ",";
  // We will split the frame into items 
  char * item;
  // Used to move forward into the pointer array
  int PointerArrayIndex = 0;
  
  Serial.print(F("Taille du tableau : ")); 
  Serial.println(sizeof(ptr)/sizeof(ptr[0]));
  
  // Notice that this string is modified by being broken into smaller strings.
  item = strtok (frame, delim);
  // While there is items
  while (item != NULL) {
    
    //Serial.print("\t");
    //Serial.println(item);
    
    // Fil the pointer array with a new item.
    ptr[PointerArrayIndex++] = item; // pour bien faire il faudrait ici tester qu'on ne dépasse pas :)
    // We will browse the same buffer next time, but starting at the next '\0'.
    item = strtok(NULL, delim);  
  }
  
  return PointerArrayIndex;
}

Ma fonction Compare() :

boolean Compare(float current, float last){
  //If moving : 1 ; if not moving : 0
  boolean MovingOrNotMoving;
  float diff = current - last;
  Serial.print("Last decimal degree value : ");
  Serial.println(last);

  // If the distantce between 2 degree of latitide is 111km, then 111*0.0003 is around 33 meters. If the difference is higher than 33 meters we consider it moving.
  if(diff <=0.0003 && diff >= -0.0003){
    MovingOrNotMoving = 0;
    Serial.print("current - last : ");
    Serial.println(diff,7);
    Serial.println("CA BOUGE PAS TROP EN CE MOMENT...");
  }else{
    MovingOrNotMoving = 1;
    Serial.print("current - last : ");
    Serial.println(diff,7);
    Serial.println("CA A BEAUCOUP BOUGÉ !!!!");
  }
return MovingOrNotMoving;  
}

Il n'y a pas de free() mais le problème n'est pas là ! Je sent une obstination inutile dans vos propos :smiley: Les trames sont incomplètes, POINT! :stuck_out_tongue_closed_eyes: