GPS avec fichier GPX - version c-string

je me sens comme le professeur :slight_smile:

bravo pour le boulot !!

PS: vous pourriez remplacer l'écriture un peu pénible des données (et idem pour les autres)

minut[0] = latlong[3];
    minut[1] = latlong[4];
    minut[2] = latlong[5];
    minut[3] = latlong[6];
    minut[4] = latlong[7];
    minut[5] = latlong[8];
    minut[6] = latlong[9];
    minut[7] = latlong[10];
    minut[9] = '\0';

par un memcpy() dans minut des octets de latlong

attention cependant vous mettez le caractère nul dans la case 9 et la case 8 n'a pas été affectée.

Vous êtes non seulement le professeur mais également une référence, mon mentor …
Je ne fais pas de compliments facilement, c'est dire le respect que j'ai pour vous.

J'ai rectifié.

Je vais le faire mais avant, il faut que je repeigne la façade de ma maison.

Merci encore.

C’est effectivement un peu plus long que de modifier le code !

Bonne peinture!

Bonjour @J-M-L ,

Est-ce que c'est ça que vous me demandez ?
Pour la latitude :

memcpy ( &minut, &latlong[2], 8 );

Pour la longitude :

memcpy ( &minut, &latlong[3], 8 );

ça à l'air plus simple que ce que je pensais si c'est ça.
Merci

PS : aujourd'hui il fait pas beau - pas de peinture.

Au final,

la fonction conversionDecimal devient :

//=======================================Retoune la latitude ou la longitude en degres decimal
char* conversionDecimal (char *latlong, boolean lati)  {
  char degres[5];
  char  minut [10];


  if (lati) {
    memcpy ( &degres, &latlong[0], 2 );
    degres[2] = '\0';
    memcpy ( &minut, &latlong[2], 8 );
    minut[8] = '\0';
  }

  else
  {
    memcpy ( &degres, &latlong[0], 3 );
    degres[3] = '\0';
    memcpy ( &minut, &latlong[3], 8 );
    minut[8] = '\0';


  }
  double resultat = ((atoi(degres)) + ((atof(minut) / 60)));
  return lati > 0 ? dtostrf(resultat, 0, NombreChiffreAVir(resultat), latitu) : dtostrf(resultat, 0, NombreChiffreAVir(resultat), longitu);

}

idem pour :

//=======================================Formate la date et l'heure au format GPX
char timeGpx(char *lesDates, char *lesHeures ) {
  char annee[3], mois[3], jour[3], heure[3], minut[3], secon[3];
  memcpy ( &jour, &lesDates[0], 2 );
  jour[2] = '\0';
  memcpy ( &mois, &lesDates[2], 2 );
  mois[2] = '\0';
  memcpy ( &annee, &lesDates[4], 2 );
  annee[2] = '\0';
  memcpy ( &heure, &lesHeures[0], 2 );
  heure[2] = '\0';
  memcpy ( &minut, &lesHeures[2], 2 );
  minut[2] = '\0';
  memcpy ( &secon, &lesHeures[4], 2 );
  secon[2] = '\0';
  sprintf(dateHeure, "20%2s-%2s-%2sT%02s:%02s:%02sZ", annee, mois, jour, heure, minut, secon);
}

Oui, c'est une suggestion pour simplifier le code.
Le plus simple pour voir si ça fonctionne c'est d'essayer avec un petit bout de code:

char minut[10];
char latlong[] = "xxx12.34567yyyy";

void setup() {
  Serial.begin(115200); Serial.println();
  memcpy (minut, &latlong[3], 8);
  minut[9] = '\0';
  Serial.println(minut);
}

void loop() {}

si ça fonctionne comme on le veut, le moniteur série (à 115200 bauds) affichera

12.34567

(vous pouvez mettre ou pas le & devant minut , la norme dit que l'adresse d'un tableau ou le nom du tableau lui même représentent tous les deux le pointeur vers le début du tableau).

Notez qu'on peut aussi utiliser avantageusement la fonction strlcpy() qui rajoute le caractère nul à la fin et garantit qu'on ne dépassera pas la taille que l'on donne

char minut[] = "xxxxxxxxxxxxxxx"; // plein de x pour montrer que strlcpy a bien ajouté le '\0' pour nous
char latlong[] = "xxx12.34567yyyy";

void setup() {
  Serial.begin(115200); Serial.println();
  strlcpy (minut, &latlong[3], 8+1); // on veut 8 caractères plus 1 pour le caractère nul à la fin
  Serial.println(minut);
}

void loop() {}

c'est encore plus simple

enfin si vous vouliez plutôt la valeur sous format nombre, vous pouvez l'analyser directement avec https://www.cplusplus.com/reference/cstdlib/strtod/

char minut[] = "xxxxxxxxxxxxxxx"; // plein de x pour montrer que strlcpy a bien ajouté le '\0' pour nous
char latlong[] = "xxx12.34567yyyy";

void setup() {
  Serial.begin(115200); Serial.println();
  float minut = strtod(&latlong[3], nullptr);
  Serial.println(minut, 6);
}

void loop() {}

comme cela vous n'avez pas à définir à priori la longueur de la chaîne (par exemple si le nombre lu est négatif vous auriez un caractère de plus)

le second paramètre de la fonction est utile aussi pour détecter s'il y avait une erreur de format si vous voulez rendre le code plus robuste.

Merci beaucoup @J-M-L pour toutes ces nouvelles infos.
J'ai vu aussi qu'il y avait memmove pour éviter le débordement.

char minut[10];
char latlong[] = "xxx12.34567yyyy";

void setup() {
  Serial.begin(115200); Serial.println();
  memmove (minut, &latlong[3], 8);
  minut[9] = '\0';
  Serial.println(minut);
}

void loop() {
  memcpy (minut, &latlong[3], 8);
  minut[9] = '\0';
  Serial.println(minut);
  
  }

Dans le cas présent memmove() ne présente pas trop d'intérêt car vous savez que les 2 zones mémoires ne se chevauchent pas (vous avez deux buffers bien différents) et donc memcpy() est plus efficace puisqu'il n'y a pas la partie du code qui gère le chevauchement éventuel.

que ce soit avec memmove() ou memcpy(), pour éviter le débordement il faudrait mettre comme paramètre pour le nombre d'octets à traiter le min de ce que vous voulez (8 ici) et de la taille du buffer et bien sûr quand vous écrivez minut[9] = '\0'; il faut bien s'assurer que le buffer dispose d'au moins 0 octets pour l'indice 9 soit bien dans ce buffer.

Merci.

d'ailleurs avec strlcpy mettre le & génère une erreur à la compilation :

cannot convert 'char (*)[5]' to 'char*' for argument '1' to 'size_t strlcpy(char*, const char*, size_t)'

J'ai suivi vos conseils et j'utilise strlcpy , du coup ça simplifie énormément et le cheminement que j'ai suivi dans mon apprentissage m'a permis de bien comprendre ces fonctions :

//=======================================Formate la date et l'heure au format GPX
char timeGpx(char *lesDates, char *lesHeures ) {
  char annee[3], mois[3], jour[3], heure[3], minut[3], secon[3];
  strlcpy ( jour, &lesDates[0], 3 );
  strlcpy ( mois, &lesDates[2], 3 );
  strlcpy ( annee, &lesDates[4], 3 );
  strlcpy ( heure, &lesHeures[0], 3 );
  strlcpy ( minut, &lesHeures[2], 3 );
  strlcpy ( secon, &lesHeures[4], 3 );
  sprintf(dateHeure, "20%2s-%2s-%2sT%02s:%02s:%02sZ", annee, mois, jour, heure, minut, secon);
}
//=======================================Retoune la latitude ou la longitude en degres decimal
char* conversionDecimal (char *latlong, boolean lati)  {
  char degres[5];
  char  minut [10];
  if (lati) {
    strlcpy ( degres, &latlong[0], 3 );
    strlcpy ( minut, &latlong[2], 9 );
  }
  else
  {
    strlcpy ( degres, &latlong[0], 4 );
    strlcpy ( minut, &latlong[3], 9 );
  }
  double resultat = ((atoi(degres)) + ((atof(minut) / 60)));
  return lati > 0 ? dtostrf(resultat, 0, NombreChiffreAVir(resultat), latitu) : dtostrf(resultat, 0, NombreChiffreAVir(resultat), longitu);
}

Merci beaucoup.

bravo ! oui c'est bien de comprendre ce qu'il se passe au niveau des octets que l'on manipule et de l'obligation de ne pas dépasser les buffers alloués.

Attention votre fonction timeGpx() promet de retourner un char mais ne retourne rien du tout - donc il faut la déclarer comme retournant rien ➜ void

Pour rendre votre code encore plus générique si vous avez défini les buffers de la bonne taille, vous pouvez utiliser sizeof plutôt que de répéter la taille du buffer au risque de vous tromper. autant laisser le compilateur faire le boulot.

//=======================================Formate la date et l'heure au format GPX
void timeGpx(char *lesDates, char *lesHeures ) {
  char annee[3], mois[3], jour[3], heure[3], minut[3], secon[3];
  strlcpy ( jour, &lesDates[0], sizeof jour);
  strlcpy ( mois, &lesDates[2], sizeof mois );
  strlcpy ( annee, &lesDates[4], sizeof annee);
  strlcpy ( heure, &lesHeures[0], sizeof heure);
  strlcpy ( minut, &lesHeures[2], sizeof minut);
  strlcpy ( secon, &lesHeures[4], sizeof secon);
  sprintf(dateHeure, "20%2s-%2s-%2sT%02s:%02s:%02sZ", annee, mois, jour, heure, minut, secon);
}

J'ai rectifié, heureusement que vous êtes là !
C'est qu'au début j'ai fait des essais et c'était resté comme ça :wink:

Et oui c'est bien plus simple...

notez aussi puisque vous utilisez sprintf() avec des tailles pour les chaînes et le 02 n'est pas applicable pour le format %s. vous pouvez préciser le nombre d'octets à prendre en faisant %.2s cependant. Il n'y a pas de padding

Enfin, vous pourriez directement aller lire les caractères dans lesDates et lesHeures et vous passer de la copie intermédiaire

char date[] = "230422"; 
char heure[] = "121524";

char dateHeure[50];

void timeGpx(char *lesDates, char *lesHeures ) {
  sprintf(dateHeure, "20%.2s-%.2s-%.2sT%.2s:%.2s:%.2sZ", &lesDates[4], &lesDates[2], &lesDates[0], &lesHeures[0], &lesHeures[2], &lesHeures[4]);
}

void setup() {
  Serial.begin(115200); Serial.println();
  timeGpx(date, heure);
  Serial.print("Le résultat est : ");
  Serial.println(dateHeure);
}

void loop() {}

et là on est arrivé à une expression assez simple de votre fonction

Hâtez-vous lentement, et sans perdre courage,
Vingt fois sur le métier remettez votre ouvrage,
Polissez-le sans cesse, et le repolissez,
Ajoutez quelquefois, et souvent effacez
( Nicolas Boileau)

... :slight_smile:

Oupssss
Bravo !
Et merci mille fois ...

J'ai rectifié aussi conversionDecimal :

char* conversionDecimal (char *latlong, boolean lati)  {
  char degres[4];
  char  minut [9];
  if (lati) {
    strlcpy ( degres, &latlong[0],  3);
    strlcpy ( minut, &latlong[2], sizeof minut );
  }
  else
  {
    strlcpy ( degres, &latlong[0],sizeof degres );
    strlcpy ( minut, &latlong[3], sizeof minut );
  }
  double resultat = ((atoi(degres)) + ((atof(minut) / 60)));
  return lati > 0 ? dtostrf(resultat, 0, NombreChiffreAVir(resultat), latitu) : dtostrf(resultat, 0, NombreChiffreAVir(resultat), longitu);
}

Bon le temps s'est levé, il faut que je retourne à la peinture après mangé :cry:

ça change les idées ! (moi je vais tondre et m'occuper du jardin)
bon ap'

Merci.

Bonjour JML,
Ce matin j'ai travaillé un peu et j'ai intégré le code nécessaire à l'écriture des données dans un fichier GPX. Suivant vos conseils, j'ai privilégié l'utilisation des print à l'usage de la fonction :

/// @brief Active les modèles d’impression de style C++
template<class T> inline Print &operator <<(Print &str, T arg) {
  str.print(arg);
  return str;
}

Il ne me reste plus qu'à traiter l'état de réception effective des données. Cette fois, j'envisage d'utiliser la pin pps du gps qui en principe est activée après initialisation et verrouillage des satellites. L'alternance de l'état haut et bas de cette pin déterminera l'état de la réception.

Sinon, j'ai fait un test en voiture et tout fonctionne parfaitement. Voici le code :


/* Selon le travail de JML https://forum.arduino.cc/t/gps-avec-fichier-gpx-version-c-string/978295/31
   et de Philipp Biedenkopf https://github.com/PBiedenkopf/ES-Arduino-GPX-datalogger?msclkid=c74329f6a6ad11ecb926df952f2de52d

   
*/
#include <SD.h>

const byte RXD2 = 16;
const byte TXD2 = 17;

const byte tailleMessageMax = 82;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
const char marqueurDeFin = '\n';

char *variablesGpx[83];
char latitu [18], longitu[18];
char alti[18], sate[18], hdopss[18];
char dateHeure[25];
boolean reception = false;


//  carte sd
File gpx;
int fileID = 0;
char GPXFILE[13];


// Timer - rythme l'ajout des Trackpoints
unsigned long t_start;   // timer for sampling
const int PROGMEM sample = 1000; // sample time [ms]

// objets pour le bouton et la led
const uint8_t buttonPin = 14;
const uint8_t ledPin = LED_BUILTIN;
bool b_gpx_tracking = false; // flag for starting the gpx logging
bool b_gpx_init = false; // flag that indicates if gpx file is initialized
const uint8_t chipSelect = 5; // Lecteur SD


// Déclaration des fonctions et procédures
void initGPX(void); //Initialisation du fichier GPX
void addTrackpt(void); // ajout d'un point de cheminement
void completeGPX(void); // fin de la structure GPX
bool messageRecu();
bool phraseOK(const char *unePhrase);
void traiterMessage();
void extraire(char *message, int types);
void timeGpx(char *lesDates, char *lesHeures );
char* conversionDecimal (char *latlong, boolean lati);
int NombreChiffreAVir (double ChiffreDec);
//}


void setup() {
  Serial.begin(115200); Serial.println();
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // initialise la liaison RX-TX sur espressif WROOM 32
  // initialize SD card
  Serial.print(F("Initialisation carte SD ..."));
  if (!SD.begin(chipSelect)) {
    Serial.println(F("Echec Initialisation carte"));
    while (1);
  }
  Serial.println("Carte OK");

  // initialise  boutton start et ledPin
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  // lance le timer pour la première fois
  t_start = millis();
}

void loop() {
     // traiterMessage();
  if (digitalRead(buttonPin) == LOW && Serial2.available() > 0) {
    
    if (!b_gpx_tracking) {
      traiterMessage();
      // Défini un nom de fichier non existant
      while (1) {
        sprintf(GPXFILE, "/track%02d.gpx", fileID);
        if (!SD.exists(GPXFILE)) {
          break;
        }
        else {
          fileID++;
        }
      }
      Serial.print(F("Current gpx file: ")); Serial.print(GPXFILE);Serial.print("\n");
      initGPX(); // initialise l'entête du fichier GPX
      b_gpx_tracking = true;
      b_gpx_init = true; // le fichier GPX est initialisé
      digitalWrite(ledPin, HIGH);
      //delay(300);
    }
    else {
      b_gpx_tracking = false;
      digitalWrite(ledPin, LOW);
      //delay(300);
    }

    while (digitalRead(buttonPin) == LOW) {
      delay(100);
    }

  }

  // Si les données data sont disponibles via la connection serie
  if (Serial2.available() > 0) {
    traiterMessage(); // lit les données du GPS

    // Enregistre uniquement si le délais est atteint
    if (millis() - t_start > sample) {
      // enregistre un nouveau Trackpt
      if (b_gpx_tracking) {
        //if (reception) {
          addTrackpt();
        //}
      }
      //Complete le fichier GPX si l'enregistrement est arrêté aprés avoir été executé une fois
      if (b_gpx_init && !b_gpx_tracking) {
        completeGPX();
        b_gpx_init = false; // le fichier actuel est terminé et n’est plus initialisé
        digitalWrite(ledPin, LOW); // éteint la led
      }

      // le timer est réinitialisé pour une prochaine itération
      t_start = millis();
    }
  }
}



//=========================================== Initialise la structure du fichier gpx
void initGPX() {
  Serial.println(F("Initializing GPX file ..."));
  gpx = SD.open(GPXFILE, FILE_WRITE);

  if (gpx) {
    // header
    gpx.print(F("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"));

    // start of gpx file body
    gpx.print(F("<gpx version=\"1.1\" creator=\"P. Biedenkopf\" xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"));

    // metadata
    gpx.print(F("  <metadata>\n"));
    gpx.print("    <name>"); gpx.print(GPXFILE); gpx.print("</name>\n");
    gpx.print(F("    <desc>WROOM 32 avec RX2 TX2</desc>\n"));
    //gpx.print(F("    <author>\n"));
    //gpx.print(F("      <name>P. Biedenkopf</name>\n"));
    //gpx.print(F("      <email>ph.bied13@gmail.com</email>\n"));
    //gpx.print(F("    </author>\n"));
    gpx.print(F("  </metadata>\n"));

    // start track and track segment (only single segmented tracks possible)
    gpx.print(F("  <trk>\n"));
    gpx.print ("    <name>"); gpx.print(GPXFILE); gpx.print("</name>\n");
    gpx.print(F("    <desc>WROOM 32 avec RX2 TX2</desc>\n"));
    gpx.print(F("    <trkseg>\n"));

    gpx.close();
  }
  else {
    Serial.println(F("Error opening gpx-file"));
  }
}

//================================================ Création d'un trackpoint dans la syntaxe gpx
void addTrackpt() {
  Serial.println(F("Ajoute un  Trackpoint ..."));
  gpx = SD.open(GPXFILE, FILE_APPEND);

  if (gpx) {
    gpx.print(F("      <trkpt lat=\""));
    gpx.print(latitu);
    gpx.print(F("\" lon=\""));
    gpx.print(longitu);
    gpx.println("\">");
    gpx.print("        <ele>"); gpx.print(alti); gpx.print("</ele>\n");
    Serial.println(dateHeure); // test
    gpx.print("        <time>");gpx.print(dateHeure);gpx.print("</time>\n");
    gpx.print("        <sat>"); gpx.print(sate); gpx.print("</sat>\n");
    gpx.print("        <hdop>"); gpx.print(hdopss);gpx.print( "</hdop>\n");
    gpx.print("      </trkpt>\n");
    gpx.close();
  }
  else {
    Serial.println(F("Erreur ouverture fichier gpx"));
  }
}
//======================================================= Fin de la structure GPX
void completeGPX() {
  Serial.println(F("Completing GPX file ..."));
  gpx = SD.open(GPXFILE, FILE_APPEND);

  if (gpx) {
    // Ferme le parcours enregistré, le segment et le corps du fichier gpx
    gpx.print(F("    </trkseg>\n"));
    gpx.print(F("  </trk>\n"));
    gpx.print(F("</gpx>\n"));
    gpx.close();
  }
  else {
    Serial.println(F("Error opening gpx-file"));
  }
}
//====================================================================================
bool messageRecu() {
  static byte indexMessage = 0;
  int r = Serial2.read();
  if (r != -1) { // on a reçu un caractère
    if (r == marqueurDeFin) {
      message[indexMessage] = '\0'; // on termine la c-string
      indexMessage = 0; // on se remet au début pour la prochaine fois
      return true;
    } else {
      if ((r != '\r') && (indexMessage < tailleMessageMax))
        message[indexMessage++] = (char) r; // on stocke le caractère et on passe à la case suivante
    }
  }
  return false;
}
//================================================
bool phraseOK(const char *unePhrase) {
  byte cksum = 0;

  if (*unePhrase != '$') return false;          // une phrase commence toujours par $
  const char* starPtr = strchr(unePhrase, '*');
  if (starPtr != nullptr) {      // on a une étoile donc un CKSUM
    const char *ptr = unePhrase + 1;
    while (*ptr != '*') cksum ^= *ptr++;
    char *endPtr;
    unsigned long ckControl = strtoul (starPtr + 1, &endPtr, 16);
    return (*endPtr == '\0') && (cksum == ckControl);
  }
  return false; // pas d'étoile, donc pas de checksum à vérifier (ce qui est OK dans certains cas mais ici toutes nos phrases sont attendues avec un check sum)
}
//========================================================================
void traiterMessage() {
  if (messageRecu() && phraseOK(message) ) {

    if (strncmp(message, "$GPRMC", 6) == 0) {
      extraire(message, 13);
    }
    else if (strncmp(message, "$GPGGA", 6) == 0) {
      extraire(message, 15);
    }
    else if (strncmp(message, "$GPGSA", 6) == 0) {
      extraire(message, 18);
    }

  }
}
//============================================================================
void extraire(char *message, int types) { // extrait les champs utiles des phrases GPRMC - GPGGA - GPGSA

  int nbVirgule = 0;
  char *valide;
  char *heures, *dates;
  char *latit, *longit;
  char *alt, *sat, *hdops;
  char* champNmea;


  for (int i = 0; i < types; i++) {
    champNmea = strsep(&message, ",");
    variablesGpx[nbVirgule++] = champNmea;
  }
  variablesGpx[nbVirgule] = '\0';

  if (types == 13) { //GPRMC
    heures = variablesGpx[1];
    valide = variablesGpx[2];
    latit = variablesGpx[3];
    longit = variablesGpx[5];
    dates = variablesGpx[9];

    if (*valide == 'A') { // Statut:  A = données valides, V = données non valides - 3eme champ des phrases NMEA de type GPRMC
      if (*heures != '\0' && *dates != '\0') { // on ne transmet pas de champs vides à la fonction timeGpx
        timeGpx(dates, heures);
      }
      if (*latit != '\0' && *longit != '\0') { // on ne transmet pas de champs vides à la fonction conversionDecimal
        conversionDecimal(latit, 1);
        conversionDecimal(longit, 0);
      }
      else {
        reception = false;

      }
    } // si statut vaut A

  }
  if (types == 15) { //GPGGA
    sat = variablesGpx[7];
    alt = variablesGpx[9];
    if (*sat != '\0' && atof(sat) != 0) sprintf(sate, "%s", sat); // on ne traîte pas de champs vides et de valeur égale à 0
    if (*alt != '\0' && atof(alt) != 0)  sprintf(alti, "%s", alt); // on ne traîte pas de champs vides et de valeur égale à 00

  }

  if (types == 18) { //GPGSA
    hdops = variablesGpx[16];
    if (*hdops != '\0' && atof(hdops) != 99.99 && atof(hdops) != 0) sprintf(hdopss, "%s", hdops); // on ne traîte pas un champ vide et de valeur égale à 0 et de  valeur = 99.99

  }

}
//=======================================Formate la date et l'heure au format GPX
void timeGpx(char *lesDates, char *lesHeures ) {
  sprintf(dateHeure, "20%.2s-%.2s-%.2sT%.2s:%.2s:%.2sZ", &lesDates[4], &lesDates[2], &lesDates[0], &lesHeures[0], &lesHeures[2], &lesHeures[4]);
}
//=======================================Retoune la latitude ou la longitude en degres decimal
char* conversionDecimal (char *latlong, boolean lati)  {
  char degres[4];
  char  minut [9];
  if (lati) {
    strlcpy ( degres, &latlong[0],  3);
    strlcpy ( minut, &latlong[2], sizeof minut );
  }
  else
  {
    strlcpy ( degres, &latlong[0], sizeof degres );
    strlcpy ( minut, &latlong[3], sizeof minut );
  }
  double resultat = ((atoi(degres)) + ((atof(minut) / 60)));
  return lati > 0 ? dtostrf(resultat, 0, NombreChiffreAVir(resultat), latitu) : dtostrf(resultat, 0, NombreChiffreAVir(resultat), longitu);
}

//=======================================supprime les '0' après la virigule lors de la conversion de la  latitude ou de la longitude en degres decimal, retourne le nombre de chiffres après la virgule
int NombreChiffreAVir (double ChiffreDec) { // retourne le nombre de chiffres après la vrigule d'un nombre décimal - précision sur 13 chiffres
  int nb = 0;
  char tmpBuffer[21];

  char *recherchePoint = strchr(dtostrf(ChiffreDec, 0, 13, tmpBuffer), '.');
  if (recherchePoint == nullptr) return 0;

  for (int f = strlen(tmpBuffer) - 1; f >= 0; --f)
    if (tmpBuffer[f] == '0') nb++;
    else break;
  return 13 - nb;
}

Dés que j'aurai mis au point le problème de réception, je reviendrai sur ce fil de discussion pour y déposer le code final. Bien sur, je ne m'attribue pas le code de cette version c-string qui sans vous ne serait pas.

Bonne journée.

Juste une remarque
JML c'est le pseudo
jackson c'est une espèce de "grade" qui est donné en fonction de l'activité sur le forum.

Désolé, @fdufnews je ne savais pas. J'imagine que Jackson est le grade le plus haut ...
Bonne journée.

C'est pas grave.

Vous êtes leader, ce doit être pas mal non plus car vous aussi vous êtes doué, très doué.

EDIT : mais tout ça est en rapport avec l'activité sur le forum soit, mais les plus actifs sont souvent les plus compétents et il y en a beaucoup sur ce forum.