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.