Coordonnées GPS avec Arduino et la SIM808

Cet article a pour objectif l’acquisition des coordonnées GPS et la construction de liens Google Maps ou Waze en utilisant Arduino et la carte SIM808.

Projet d'acquisition des coordonnées GPS avec la SIM808 et Arduino

Le matériel nécessaire

Carte Arduino UNO Rev 3 Cordon USB de type A/B Module de développement SIM808
Carte Arduino UNO Câble USB 2.0 Type A/B Carte SIM808 EVB-V3.2
Transformateur d'alimentation 9V / 2A Antenne GPS Câbles Dupont Mâle / Femelle
Transformateur d’alimentation 5V à 26V Antenne GPS Câbles mâle/femelle

Prise en main de la carte SIM808

Présentation de la SIM808 EVB-V3.2

La carte de développement SIM808 EVB-V3.2 est une carte plutôt complète pour son prix abordable. Elle inclue en effet:

  • un modem GSM / GPRS quadribande compatible avec les fréquences 850, 900, 1800 et 1900MHz
  • un  modem GPS
  • un modem Bluetooth

La position des 3 connecteurs coaxiaux SMA des antennes sur la carte SIM808 est la suivante:

Emplacement des antennes sur la carte SIM808

Carte SIM

Pour ce projet aucune carte téléphonique SIM n’est nécessaire. Seule les fonctionnalité GPS de la carte SIM808 seront utilisées.

Démarrage de la carte SIM808

La carte SIM808 EVB-V3.2 nécessite d’être démarrée après chaque mise sous tension afin d’être utilisable. Cette procédure de démarrage n’est pas intuitive, aussi je vous invite à lire attentivement le chapitre “Démarrage de la carte SIM808” de l’article “Réception de SMS avec Arduino et la SIM808”.

Câblage de la carte SIM808 avec Arduino

La carte de développement SIM808 nécessite une puissance d’alimentation de l’ordre de 10 watts. La puissance transmise à la carte Arduino par le PC à travers le câble USB n’est pas suffisante. Dans le cadre de ce projet nous utilisons un transformateur 9V de 2A pour fournir la puissance nécessaire à la carte SIM808. Un transformateur délivrant une tension entre 5 à 26V de puissance équivalente peut également être utilisé.

La communication entre la carte Arduino et la carte SIM808, repose sur une liaison TTL. Dans ce tutoriel nous choisissons les ports 2 et 3 de l’Arduino pour les données. Ne pas oublier de relier la masse des deux cartes afin que les signaux échangés aient la même référence:

  • La broche RXD de la SIM808 est reliée à la sortie 3 de l’Arduino,
  • la broche TXD de la SIM808 est reliée à l’entrée 2 de l’Arduino.
  • Les deux masses sont mises en commun (GND).
Branchement de la carte de développement SIM808 avec Arduino

Bibliothèque de gestion de la carte de développement SIM808:

La gestion de la communication TTL avec la carte de développement SIM808 nécessite l’installation d’une bibliothèque non-standard Arduino, la bibliothèque “GSM/GPRS & GPS Shield”.

La procédure d’installation de cette librairie est expliquée pas à pas au chapitre “Bibliothèque de gestion de la carte de développement SIM808” de l’article “Réception de SMS avec Arduino et la SIM808”. Je vous invite à suivre cette procédure avant de poursuivre ce projet.

Le programme de calcul des coordonnées GPS

Comme d’habitude, nous démarrons l’écriture du logiciel par l’ossature de code Arduino suivante:

// Fonction de démarrage, s'exécute une seule fois:
void setup()
{
}
// Fonction principale du programme, s'exécute en boucle:
void loop()
{
}

Bibliothèque nécessaire

Pour cette partie du programme, seul le module de gestion GPS de la bibliothèque « GSM-GPRS-GPS-Shield-GSMSHIELD » est nécessaire. Il se déclare par les lignes:

// Déclaration des bibliothèques utilisées
#include "gps.h"

LED d’information de l’état du GPS

Nous allons utiliser la LED embarquée de la carte Arduino UNO pour connaitre l’état du modem GPS. La signification de la LED sera la suivante.

  • Éteinte: le modem GPS est éteint
  • Clignotement:  le modem GPS recherche sa position (les satellites)
  • Allumé en continu: le modem a trouve une position en 3 ou 2 dimensions (avec ou sans l’altitude).

Juste après avoir initialiser le port série pour les traces de notre programme, nous déclarons ce port en sortie et signalons que le modem GPS est éteint au démarrage:

// Fonction de démarrage, s'exécute une seule fois:
void setup()
{
  // Ouverture du port USB pour l'envoi des traces au PC
  Serial.begin(115200);

  // Positionnement sortie du port de la diode interne
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);  // La diode éteinte signale que le GPS n'est pas disponible
}

Fonction d’initialisation de la carte SIM808

Afin de faciliter la lecture de notre code (et sa réutilisation pour de futurs projets) nous regroupons l’initialisation de la SIM808 dans une seule fonction “initialiser_sim808”. Voici le prototype de cette fonction:

// Prototypes de fonction
void initialiser_Sim808(void);

L’initialisation de la SIM808 s’effectue après avoir positionné les ports d’entrée/sortie :

void setup()
{
  // Ouverture du port USB pour l'envoi des traces au PC
  Serial.begin(115200);

  // Positionnement sortie du port de la diode interne
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);  // La diode éteinte signale que le GPS n'est pas disponible

  // Initialisation de la communication avec la carte SIM808
  initialiser_sim808();  
}

Passons maintenant à l’écriture du contenu de la fonction “initialiser_sim808”.

La commande proposées par la bibliothèque “GSM-GPRS-GPS-Shield-GSMSHIELD” pour initialiser la carte SIM808 est la méthode “begin” de l’objet statique “gsm” . Cette méthode possède comme paramètre  la vitesse de communication série de la liaison TTL exprimée  en bauds.  (Pour rappel la carte Arduino et la carte SIM808 communique par liaison série). Étant donné les capacités limitées de la SIM808 il est nécessaire de choisir une vitesse pas trop élevée, ce sera 9600 dans notre cas.

En ajoutant l’envoie de traces par le port série, nous arrivons au code suivant pour la fonction “initialiser_sim808” :

void initialiser_sim808(void)
{
  Serial.println(F("Connexion avec la carte SIM808."));  
  while(!gsm.begin(9600))
  {
    Serial.println(F("Echec de communication avec la carte SIM808. Nouvelle tentative..."));  
  }
  Serial.println(F("La communication avec la carte SIM808 est établie."));
}

Démarrage du modem GPS

Comme pour l’initialisation de la carte SIM808, nous allons regrouper les fonctions de démarrage du modem GPS au sein d’un unique fonction que nous appellerons “demarrer_gps”. Voici sa déclaration:

void demarrer_gps(void);

Le démarrage du modem GPS s’effectue après l’initialisation de la carte SIM808:

// Fonction de démarrage, s'exécute une seule fois:
void setup()
{
  // Ouverture du port USB pour l'envoi des traces au PC
  Serial.begin(115200);

  // Positionnement sortie du port de la diode interne
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);  // La diode éteinte signale que le GPS n'est pas disponible

  // Initialisation de la communication avec la carte SIM808
  initialiser_sim808();  
  // Initialisation du modem GPS
  demarrer_gps();
}

Afin de pouvoir facilement convertir les variables d’état du modem GPS retournées par les fonctions de la bibliothèque “GSM-GPRS-GPS-Shield-GSMSHIELD” en des constantes compréhensibles,  nous déclarons un type énuméré appelé “etat_gps_t”:

// Définition des types
typedef enum
{
    PAS_DE_REPONSE = -1,
    ETEINT = 0,
    RECHERCHE = 1,
    TROUVE_2D = 2,
    TROUVE_3D = 3
} etat_gps_t;

Nous déclarons ensuite un objet pour la classe “GPSGSM”. Cette classe regroupe l’ensemble des méthodes de la bibliothèque “GSM-GPRS-GPS-Shield-GSMSHIELD” permettant de d’activer, désactiver et récupérer les informations du modem GPS:

// Déclarations globales
GPSGSM  *gestionnaire_gps;

Passons maintenant à l’écriture du contenu de la fonction “demarrer_gps”.

La commande proposées par la bibliothèque “GSM-GPRS-GPS-Shield-GSMSHIELD” pour mettre sous tension le GPS est la méthode “attachGPS” de notre objet “gestionnaire_gps”. Cette méthode retourne “true” si tout se passe bien et “false” dans le cas contraire.

La méthode de la bibliothèque “GSM-GPRS-GPS-Shield-GSMSHIELD”  qui permet de connaitre l’état du modem GPS est la méthode “getStat”. Elle retourne une valeur numérique correspondant au type énuméré “etat_gps_t” que nous avons défini précédemment.

Comme indiqué dans les caractéristiques de la carte SIM808, environ 30 secondes sont nécessaire au modem GPS pour pouvoir réaliser son premier positionnement une fois démarré. Aussi, nous resterons dans une boucle d’attente tant que le modem sera dans l’état “RECHERCHE”.

En ajoutant la mise à jour de la LED interne pour refléter l’état du modem GPS, nous obtenons la fonction  “demarrer_gps” suivante:

void demarrer_gps(void)
{
  etat_gps_t etat_gps;
    
  if(gestionnaire_gps!=NULL)
  {
    free(gestionnaire_gps);
  }
  gestionnaire_gps = new GPSGSM();
  if (!gestionnaire_gps->attachGPS())
  {
    Serial.println(F("Impossible d'activer le GPS."));
  }
  else
  {
    Serial.println(F("Initialisation du GPS."));
    do
    {
      digitalWrite(LED_BUILTIN, HIGH);  // Clignotement de la diode pour signaler la recherche GPS
      etat_gps = (etat_gps_t)gestionnaire_gps->getStat();
      Serial.print(F("."));
      delay(200);
      digitalWrite(LED_BUILTIN, LOW);  // Clignotement de la diode pour signaler la recherche GPS
    }
    while(etat_gps == RECHERCHE);
    if (etat_gps == TROUVE_2D)    
    {
      Serial.println(F("\nGPS initialisé en 2D."));
      digitalWrite(LED_BUILTIN, HIGH);  // La diode allumée signale que le GPS est prêt
    }
    else if(etat_gps == TROUVE_3D)
    {
      Serial.println(F("\nGPS initialisé en 3D."));
      digitalWrite(LED_BUILTIN, HIGH);  // La diode allumée signale que le GPS est prêt
    }
    else
    {
      Serial.println(F("\nImpossible d'initialiser le GPS."));     
    }
  }
}

Extinction du modem GPS

L’extinction du modem GPS va permettre de diminuer le courant consommé par notre montage. Cela n’a pas une grande importance pour le projet en cours puisque nous utilisons un transformateur pour alimenter notre carte SIM808, mais ce sera primordial pour un projet de balise GPS avec alimentation autonome qui sera détaillé plus tard sur ce site tropratik.fr.

Pour rester dans le prévisible, nous allons le baptisons “arreter_gps”. Voici sa déclaration:

void arreter_gps(void);

La commande d’extinction du modem GPS est la méthode “deattachGPS” de notre objet “gestionnaire_gps”. Cette méthode retourne “true” si tout se passe bien et “false” dans le cas contraire.

Nous en déduisons la fonction “deattachGPS” suivante:

void arreter_gps(void)
{
  if(gestionnaire_gps==NULL)
  {
    Serial.println(F("Le GPS est déjà éteint."));
  }
  else
  {
    if (!gestionnaire_gps->deattachGPS())
    {
      Serial.println(F("Impossible d'éteindre le modem GPS."));
    }
    else
    {
      digitalWrite(LED_BUILTIN, LOW);  // La diode éteinte signale que le GPS est arrêtée
      Serial.println(F("\nGPS éteint."));
    }
  }
}

Lecture des information GPS

Pour la lecture des informations GPS du modem, nous allons utiliser la méthode “getPar” de l’objet “gestionnaire_gps”. Cette méthode “getPar”  prend en paramètre des chaines de caractères dans lesquelles seront stockées les valeurs d’acquisitions du modem GPS, c’est à dire:

  • la latitude,
  • la longitude,
  • l’altitude,
  • l’heure,
  • et la vitesse.

Avant de lire ces paramètres nous testerons la valeur de retour qui est attendue à “true” si tout s’est bien déroulée (sera à “false” dans le cas contraire).

Les valeurs fournies par le modem GPS sont des valeurs “brutes”, c’est à dire qu’il est nécessaire de les convertir afin de les rendres compréhensibles. Pour ce projet, les dimensions que nous choisissons d’exploiter sont la latitude et la longitude. Le format habituel pour ces coordonnées est le format sexagésimal.

Nous commençons par créer le type “sexagesimale_t” qui n’existe pas nativement dans Arduino:

typedef struct
{
  bool positif;
  uint8_t degree;
  uint8_t minute;
  float seconde;
} sexagesimale_t;

Puis nous déclarons le prototype de la fonctions de lecture des information GPS “lire_informations_gps” :

bool lire_informations_gps(sexagesimale_t*, sexagesimale_t*);

Et le prototype de la fonction baptisée “gps_brute_vers_sexagesimal” réalisant la conversion d’un texte brut de latitude / longitude fourni par le GPS en une valeur sexagésimale :

sexagesimale_t gps_brute_vers_sexagesimal(char*);

Concernant le corps de la fonction “lire_informations_gps”, celle-ci va effectuer les 3 opérations suivantes:

  1. Le GPS a-t-il une position en 2 ou 3 dimensions ? Si oui on continue, si non une nouvelle demande de démarrage est envoyée au module GPS.
  2. Récupération des valeurs brutes du GPS.
  3. Conversion des données brutes de latitude et longitudes obtenues en valeurs sexagésimales.

Les valeurs de latitudes et longitudes seront transmises à l’appelant par pointeur de paramètre. La valeur de retour de notre fonction indiquera le succès ou l’échec de l’opération.

Voici le corps de la fonction “lire_informations_gps”:

bool lire_informations_gps(sexagesimale_t* latitude_p, sexagesimale_t* longitude_p)
{
  etat_gps_t etat_gps;

  bool informations_gps_lues = false;
  char longitude_brute[15];
  char latitude_brute[15];
  char altitude_brute[15];
  char heure_brute[20];
  char vitesse_brute[15];

  etat_gps = (etat_gps_t)gestionnaire_gps->getStat();
  if((etat_gps != TROUVE_2D)&&(etat_gps != TROUVE_3D))
  {
    demarrer_gps();  
  }
  
  Serial.println(F("Lecture des information gps"));
  informations_gps_lues = gestionnaire_gps->getPar(latitude_brute, longitude_brute, altitude_brute, heure_brute, vitesse_brute);

  if(informations_gps_lues)
  {
    Serial.println(F("Informations gps lues"));
    *latitude_p = gps_brute_vers_sexagesimal(latitude_brute);
    *longitude_p = gps_brute_vers_sexagesimal(longitude_brute);
  }
  
  return(informations_gps_lues);
}

Il n’y a pas grand chose à dire sur la fonction de conversion “gps_brute_vers_sexagesimal”, si ce n’est qu’elle commence par transformer la données brute fournie sous forme d’une chaine de caractère en un nombre flottant à l’aide de la fonction atof avant de réaliser les différentes opérations mathématiques de conversion.  

sexagesimale_t gps_brute_vers_sexagesimal(char* coordonnee_brute)
{
  sexagesimale_t sexagecimale;
  double coordonnee_dble;
  
  coordonnee_dble = atof(coordonnee_brute);
  
  // Détermination du signe +/- de la coordonnée
  sexagecimale.positif = (coordonnee_dble>=0);
  coordonnee_dble = fabs(coordonnee_dble);
  
  // Calcul des degrés
  sexagecimale.degree = ((int)coordonnee_dble) / 100;
  coordonnee_dble = coordonnee_dble - sexagecimale.degree * 100;
  
  // Calcul des minutes
  sexagecimale.minute = (int)coordonnee_dble;
  coordonnee_dble = coordonnee_dble - sexagecimale.minute;
  
  // Calcul des secondes
  sexagecimale.seconde = 60 * coordonnee_dble;
  
  return(sexagecimale);
}

Coordonnées géographiques sexagésimales

Les coordonnées géographiques que nous avons l’habitude d’utiliser sur une cartes sont au format
xx° xx’ xx” N/S, xx° xx’ xx” E/O. Ainsi la tour Eiffel possède les coordonnées 48° 51′ 30″ N, 2° 17′ 40″ E et le Machu Picchu les coordonnées 13° 09′ 50″ S, 72° 32′ 45″ O.

Pour les afficher simplement sous ce format, nous allons créer une fonction de mise en forme des cordonnées sexagésimales (Celles que nous avons obtenues au paragraphe précédent). Le nom de cette fonction de mise en forme est “donner_coordonees_en_texte”.

  • Le premier paramètre de cette fonction est un paramètre de sortie, plus précisément l’adresse de la chaine de caractère dans laquelle sera écrit le résultat obtenu.
  • Les 2ème et 3ème paramètres de la fonction servent à transmettre les coordonnées sexagésimales à convertir .

La conversion d’un nombre flottant en texte n’est pas immédiat en langage Arduino, aussi dans cette fonction et dans la suite ce cet article, nous utiliserons la fonction “modff” qui permet de séparer la partie entière d’un nombre flottant de sa partie décimale .

void donner_coordonees_en_texte(char* position_texte, sexagesimale_t latitude, sexagesimale_t longitude)
{
  char signe_latitude;
  char signe_longitude;
  float partie_entiere;

  if(latitude.positif)
  {
    signe_latitude = 'N';
  }
  else
  {
    signe_latitude = 'S';
  }
  if(longitude.positif)
  {
    signe_longitude = 'E';
  }
  else
  {
    signe_longitude = 'O';
  }
  sprintf(position_texte, "%d°%d'%02d.%02d\"%c, %d°%d'%02d.%02d\"%c", latitude.degree, latitude.minute , int(latitude.seconde),  int(modff(latitude.seconde, &partie_entiere)*100), signe_latitude,
                                                                     longitude.degree, longitude.minute , int(longitude.seconde),  int(modff(longitude.seconde, &partie_entiere)*100), signe_longitude);    
}

Lien Google Maps

Un acteur majeur de la localisation GPS aujourd’hui est “Google Maps”. Les lien Google Maps qui permettent d’afficher une carte en vue cartographique sur un navigateur internet sont très pratique. Dans la suite de ce paragraphe nous allons écrire les fonctions qui vont nous permettent de générer ces liens à partir des coordonnées sexagésimales que nous avons obtenues précédemment.

Google met à disposition la documentation complète pour la création des liens Google Maps à l’adresse suivante: https://developers.google.com/maps/documentation/urls/get-started.

A la lecture de cette documentation, on remarque que Google Maps utilise des coordonnées GPS écrites au format degrés décimaux. Pour notre fonction  de conversion de coordonnées sexagésimales en coordonnées à degrés décimaux, nous nous appuyons sur l’algorithme de conversion fourni par Wikipédia au lien suivant lien et obtenons la fonction “sexagesimale_vers_degres_decimaux suivante” :

float sexagesimale_vers_degres_decimaux(sexagesimale_t coordonnee)
{
  float valeur;
  
  valeur = float(coordonnee.degree) + float(coordonnee.minute) / 60.0 + float(coordonnee.seconde) / 3600.0 ;
  if(!coordonnee.positif)
  {
    valeur = valeur * -1;
  }
  return(valeur);
}

Comme évoqué précédemment la conversion d’un nombre flottant en texte n’est pas immédiat en langage Arduino, aussi nous créons une fonction “flottant_vers_texte” pour la  conversion en texte de nos coordonnées en degrés décimaux.

void flottant_vers_texte(char* texte, float valeur)
{
  float partie_entiere, partie_fractionnaire;

  partie_fractionnaire = modff(valeur, &partie_entiere);
  sprintf(texte,"%d.%ld", (int)partie_entiere, (long)abs(partie_fractionnaire*pow(10, 7)));
}

Revenons maintenant à la documentation des liens Google Maps. Il est possibles de générer 4 types de liens:

Type de lien Première parti du lien:
Lien de recherche d’un emplacement https://www.google.com/maps/search/?api=1&
Lien de navigation GPS (pour se rendre à l’endroit choisi) https://www.google.com/maps/dir/?api=1&
Lien d’affichage d’une carte en vue cartographique centrée sur l’emplacement choisi https://www.google.com/maps/@?api=1&map_action=map&
Lien de navigation dans une rue en mode “Google street” (affichage en vue réelle). https://www.google.com/maps/@?api=1&map_action=pano&

 

Dans cet article, nous choisissons l’affichage en vue cartographique pour Google Maps libre à vous de choisir l’affichage d’un autre type de carte pour ce programme. Notre lien web débute donc par “https://www.google.com/maps/@?api=1&map_action=map&”.

Un dernier point important dans les lien Google Maps, est le choix du zoom d’affichage. Cela s’effectue en ajoutant le paramètre “&zoom=” en fin de lien dynamique. Dans la fonction ci-dessous la zoom est à la valeur 18.

void donner_lien_google_map(char* lien, sexagesimale_t latitude, sexagesimale_t longitude)
{
  char texte_latitude[15], texte_longitude[15];

  flottant_vers_texte(texte_latitude, sexagesimale_vers_degres_decimaux(latitude));
  flottant_vers_texte(texte_longitude, sexagesimale_vers_degres_decimaux(longitude)); 
  sprintf(lien, "https://www.google.com/maps/@?api=1&map_action=map&center=%s%s%s&zoom=18", texte_latitude, ",", texte_longitude);
}

Lien Waze

L’application de navigation que j’utilise lorsque je veux me rendre dans un lieu inconnu est l’application “Waze” que je trouve plutôt bien faite. L’idée est de construire un lien qui, lorsqu’il sera ouvert sur notre smartphone ou notre tablette Android, va directement lancer l’application Waze et lui demander de calculer l’itinéraire qui nous permettra de nous rendre à la position GPS fournie par la carte SIM808

La documentation pour la construction des liens Waze est disponible à l’adresse suivante: https://developers.google.com/waze/deeplinks?hl=fr.

A la lecture de cette documentation, on constate rapidement que la construction d’un lien Waze est proche de la construction d’un lien “Google Maps”. Cela s’explique par le fait que Waze appartient également à Google (rachat de la société en 2013).

Pour lancer la navigation Waze vers un lieu, le lien web doit débuter par “https://waze.com/ul?”

Et comme pour Google Maps, cette documentation nous indique que le paramètre “&zoom=” en fin de lien dynamique permet de définir le zoom d’affichage. 

void donner_lien_waze(char* lien, sexagesimale_t latitude, sexagesimale_t longitude)
{
  char texte_latitude[15], texte_longitude[15];
  
  flottant_vers_texte(texte_latitude, sexagesimale_vers_degres_decimaux(latitude));
  flottant_vers_texte(texte_longitude, sexagesimale_vers_degres_decimaux(longitude));
  sprintf(lien, "https://www.waze.com/ul?ll=%s%s%s&navigate=yes&zoom=18", texte_latitude, "%2C", texte_longitude);        
}

Affichage des coordonnées et liens GPS

Maintenant que nous avons écrit notre boite à outils de fonction GPS, nous allons pouvoir les tester.

Le choix est fait d’appeler les fonctions créées précédemment en boucle et d’afficher leur résultat sur le port série selon les étapes suivantes :

  1. Lecture des coordonnées GPS à partir du modem de la carte SIM808
  2. Coordonnées géographiques au format cartographique.
  3. Lien Google Maps.
  4. Lien Waze

Nous aboutissons à la fonction “loop” du programme Arduino :

// Fonction principale du programme, s'exécute en boucle:
void loop()
{
  sexagesimale_t sexa_latitude, sexa_longitude;
  char tampon_message[90];

  Serial.print('\n'); // Saut d'une ligne pour une meilleur visibilité
  if(lire_informations_gps(&sexa_latitude, &sexa_longitude))
  {
    // Calcul des coordonnées géographiques:
    donner_coordonees_en_texte(tampon_message, sexa_latitude, sexa_longitude);
    Serial.print(F("\nCoordonnées géographiques: "));    
    Serial.println(tampon_message);

    // Calcul du lien Google Map:
    donner_lien_google_map(tampon_message, sexa_latitude, sexa_longitude);
    Serial.print(F("Lien google Map: "));    
    Serial.println(tampon_message);

    // Calcul du lien Waze:
    donner_lien_waze(tampon_message, sexa_latitude, sexa_longitude);
    Serial.print(F("Lien Waze: "));    
    Serial.println(tampon_message);

    // Pause de  10 secondes
    delay(5000);
  }
  else
  {
    Serial.println(F("Erreur de lecture GPS"));  
  }
}

Le programme complet :

Voici le moment de la synthèse !

Si l’on assemble tout les pièces du puzzle, on obtient le programme complet suivant:

// Déclaration des bibliothèques utilisées
#include "gps.h"

// Définition des types
typedef enum
{
    PAS_DE_REPONSE = -1,
    ETEINT = 0,
    RECHERCHE = 1,
    TROUVE_2D = 2,
    TROUVE_3D = 3
} etat_gps_t;

typedef struct
{
  bool positif;
  uint8_t degree;
  uint8_t minute;
  float seconde;
} sexagesimale_t;

// Prototypes de fonction
void initialiser_Sim808(void);
void demarrer_gps(void);
void arreter_gps(void);
bool lire_informations_gps(sexagesimale_t*, sexagesimale_t*);
sexagesimale_t gps_brute_vers_sexagesimal(char*);
void donner_coordonees_en_texte(char*, sexagesimale_t, sexagesimale_t);
void donner_lien_google_map(char*, sexagesimale_t, sexagesimale_t);
void donner_lien_waze(char*, sexagesimale_t, sexagesimale_t);
float sexagesimale_vers_degres_decimaux(sexagesimale_t);
void flottant_vers_texte(char*, float);

// Déclarations globales
GPSGSM  *gestionnaire_gps;

// Fonction de démarrage, s'exécute une seule fois:
void setup()
{
  // Ouverture du port USB pour l'envoi des traces au PC
  Serial.begin(115200);

  // Positionnement sortie du port de la diode interne
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);  // La diode éteinte signale que le GPS n'est pas disponible

  // Initialisation de la communication avec la carte SIM808
  initialiser_sim808();  
  // Initialisation du modem GPS
  demarrer_gps();
}

// Fonction principale du programme, s'exécute en boucle:
void loop()
{
  sexagesimale_t sexa_latitude, sexa_longitude;
  char tampon_message[90];

  Serial.print('\n'); // Saut d'une ligne pour une meilleur visibilité
  if(lire_informations_gps(&sexa_latitude, &sexa_longitude))
  {
    // Calcul des coordonnées géographiques:
    donner_coordonees_en_texte(tampon_message, sexa_latitude, sexa_longitude);
    Serial.print(F("\nCoordonnées géographiques: "));    
    Serial.println(tampon_message);

    // Calcul du lien Google Map:
    donner_lien_google_map(tampon_message, sexa_latitude, sexa_longitude);
    Serial.print(F("Lien google Map: "));    
    Serial.println(tampon_message);

    // Calcul du lien Waze:
    donner_lien_waze(tampon_message, sexa_latitude, sexa_longitude);
    Serial.print(F("Lien Waze: "));    
    Serial.println(tampon_message);

    // Pause de  10 secondes
    delay(5000);
  }
  else
  {
    Serial.println(F("Erreur de lecture GPS"));  
  }
}

void initialiser_sim808(void)
{
  Serial.println(F("Connexion avec la carte SIM808."));  
  while(!gsm.begin(9600))
  {
    Serial.println(F("Echec de communication avec la carte SIM808. Nouvelle tentative..."));  
  }
  Serial.println(F("La communication avec la carte SIM808 est établie."));
}

void demarrer_gps(void)
{
  etat_gps_t etat_gps;
    
  if(gestionnaire_gps!=NULL)
  {
    free(gestionnaire_gps);
  }
  gestionnaire_gps = new GPSGSM();
  if (!gestionnaire_gps->attachGPS())
  {
    Serial.println(F("Impossible d'activer le GPS."));
  }
  else
  {
    Serial.println(F("Initialisation du GPS."));
    do
    {
      digitalWrite(LED_BUILTIN, HIGH);  // Clignotement de la diode pour signaler la recherche GPS
      etat_gps = (etat_gps_t)gestionnaire_gps->getStat();
      Serial.print(F("."));
      delay(200);
      digitalWrite(LED_BUILTIN, LOW);  // Clignotement de la diode pour signaler la recherche GPS
    }
    while(etat_gps == RECHERCHE);
    if (etat_gps == TROUVE_2D)    
    {
      Serial.println(F("\nGPS initialisé en 2D."));
      digitalWrite(LED_BUILTIN, HIGH);  // La diode allumée signale que le GPS est prêt
    }
    else if(etat_gps == TROUVE_3D)
    {
      Serial.println(F("\nGPS initialisé en 3D."));
      digitalWrite(LED_BUILTIN, HIGH);  // La diode allumée signale que le GPS est prêt
    }
    else
    {
      Serial.println(F("\nImpossible d'initialiser le GPS."));     
    }
  }
}

void arreter_gps(void)
{   
  if(gestionnaire_gps==NULL)
  {
    Serial.println(F("Le GPS est déjà éteint."));
  }
  else
  {
    if (!gestionnaire_gps->deattachGPS())
    {
      Serial.println(F("Impossible d'éteindre le modem GPS."));
    }
    else
    {
      digitalWrite(LED_BUILTIN, LOW);  // La diode éteinte signale que le GPS est arrêtée
      Serial.println(F("\nGPS éteint."));
    }
  }
}

bool lire_informations_gps(sexagesimale_t* latitude_p, sexagesimale_t* longitude_p)
{
  etat_gps_t etat_gps;

  bool informations_gps_lues = false;
  char longitude_brute[15];
  char latitude_brute[15];
  char altitude_brute[15];
  char heure_brute[20];
  char vitesse_brute[15];

  etat_gps = (etat_gps_t)gestionnaire_gps->getStat();
  if((etat_gps != TROUVE_2D)&&(etat_gps != TROUVE_3D))
  {
    demarrer_gps();  
  }
  
  Serial.println(F("Lecture des information gps"));
  informations_gps_lues = gestionnaire_gps->getPar(latitude_brute, longitude_brute, altitude_brute, heure_brute, vitesse_brute);

  if(informations_gps_lues)
  {
    Serial.println(F("Informations gps lues"));
    *latitude_p = gps_brute_vers_sexagesimal(latitude_brute);
    *longitude_p = gps_brute_vers_sexagesimal(longitude_brute);
  }
  
  return(informations_gps_lues);
}

sexagesimale_t gps_brute_vers_sexagesimal(char* coordonnee_brute)
{
  sexagesimale_t sexagecimale;
  double coordonnee_dble;
  
  coordonnee_dble = atof(coordonnee_brute);
  
  // Détermination du signe +/- de la coordonnée
  sexagecimale.positif = (coordonnee_dble>=0);
  coordonnee_dble = fabs(coordonnee_dble);
  
  // Calcul des degrés
  sexagecimale.degree = ((int)coordonnee_dble) / 100;
  coordonnee_dble = coordonnee_dble - sexagecimale.degree * 100;
  
  // Calcul des minutes
  sexagecimale.minute = (int)coordonnee_dble;
  coordonnee_dble = coordonnee_dble - sexagecimale.minute;
  
  // Calcul des secondes
  sexagecimale.seconde = 60 * coordonnee_dble;
  
  return(sexagecimale);
}


void donner_coordonees_en_texte(char* position_texte, sexagesimale_t latitude, sexagesimale_t longitude)
{
  char signe_latitude;
  char signe_longitude;
  float partie_entiere;

  if(latitude.positif)
  {
    signe_latitude = 'N';
  }
  else
  {
    signe_latitude = 'S';
  }
  if(longitude.positif)
  {
    signe_longitude = 'E';
  }
  else
  {
    signe_longitude = 'O';
  }
  sprintf(position_texte, "%d°%d'%02d.%02d\"%c, %d°%d'%02d.%02d\"%c", latitude.degree, latitude.minute , int(latitude.seconde),  int(modff(latitude.seconde, &partie_entiere)*100), signe_latitude,
                                                                     longitude.degree, longitude.minute , int(longitude.seconde),  int(modff(longitude.seconde, &partie_entiere)*100), signe_longitude);    
}

float sexagesimale_vers_degres_decimaux(sexagesimale_t coordonnee)
{
  float valeur;
  
  valeur = float(coordonnee.degree) + float(coordonnee.minute) / 60.0 + float(coordonnee.seconde) / 3600.0 ;
  if(!coordonnee.positif)
  {
    valeur = valeur * -1;
  }
  return(valeur);
}

void flottant_vers_texte(char* texte, float valeur)
{
  float partie_entiere, partie_fractionnaire;

  partie_fractionnaire = modff(valeur, &partie_entiere);
  sprintf(texte,"%d.%ld", (int)partie_entiere, (long)abs(partie_fractionnaire*pow(10, 7)));
}

void donner_lien_google_map(char* lien, sexagesimale_t latitude, sexagesimale_t longitude)
{
  char texte_latitude[15], texte_longitude[15];

  flottant_vers_texte(texte_latitude, sexagesimale_vers_degres_decimaux(latitude));
  flottant_vers_texte(texte_longitude, sexagesimale_vers_degres_decimaux(longitude)); 
  sprintf(lien, "https://www.google.com/maps/@?api=1&map_action=map&center=%s%s%s&zoom=18", texte_latitude, ",", texte_longitude);
}

void donner_lien_waze(char* lien, sexagesimale_t latitude, sexagesimale_t longitude)
{
  char texte_latitude[15], texte_longitude[15];
  
  flottant_vers_texte(texte_latitude, sexagesimale_vers_degres_decimaux(latitude));
  flottant_vers_texte(texte_longitude, sexagesimale_vers_degres_decimaux(longitude));
  sprintf(lien, "https://www.waze.com/ul?ll=%s%s%s&navigate=yes&zoom=18", texte_latitude, "%2C", texte_longitude);        
}

Compilation et test:

Pour tester notre programme, nous réalisons les opérations suivantes :

  1. Compiler puis transférer ce programme dans le microcontrôleur Arduino
  2. Démarrer la carte SIM808
  3. Effectuer un redémarrage de votre Arduino Uno en appuyant sur le bouton “Reset”.
  4. Ouvrir une console série à 115200 bauds.

Qu’observons nous ?

Dans un premier temps va s’afficher la ligne:

Connexion avec la carte SIM808.

puis au bout d’une trentaine de secondes:

La communication avec la carte SIM808 est établie.

Si vous n’arrivez pas à obtenir cette dernière ligne, je vous invite à vérifier :

  • Le câblage entre votre carte Arduino Uno et votre carte SIM808.
  • L’alimentation de votre carte SIM808

Ensuite, la carte Arduino demande à la SIM808 de démarrer son modem GPS:

Initialisation du GPS

. . . . . . . . .

La LED embarquée de la carte Arduino se met à clignoter. Cela peut prendre jusqu’à 2 minutes avant d’obtenir la confirmation :

GPS initialisé en 2D.

Si vous n’obtenez pas cette confirmation, je vous invite à vérifier:

  • Que l’antenne GPS est bien vissée sur le connecteur SMA correspondant.
  • Que l’antenne GPS est correctement positionnée (face plate en direction du sol) dans un lieu perméable aux ondes GPS.

Une fois initialisé, la LED embarquée s’allume en continue et la console série affiche les coordonnées GPS calculée par Arduino. Par exemple:

Coordonnées géographiques: 47°28’55.59″N, 1°6’22.50″O
Lien google Map: https://www.google.com/maps/@?api=1&map_action=map&center=47.4821129,-1.1062500&zoom=18
Lien Waze: https://www.waze.com/ul?ll=47.4821129%2C-1.1062500&navigate=yes&zoom=18

Un copié / collé du lien lien Google Maps dans un navigateur internet ouvre une carte Google Maps centrée sur cette position:

Ouverture du lien Google Maps généré par Arduino

Quant au lien Waze, si vous l’envoyez par mail sur un smartphone possédant l’application, ce lien va directement lancer une recherche de navigation Waze vers cette position. 

Ouverture du lienWaze généré par Arduino

Voici un résumé du fonctionnement de notre application en vidéo:

Voilà, maintenant nous savon utiliser le modem GPS de la carte SIM808 avec Arduino.

J’espère que cet article vous a plu et qu’il vous donnera plein d’idées pour vos projets personnels. Pour ma part, le but est d’intégrer ces fonctionnalités de localisation dans un futur projet de “balise GPS autonome”. N’hésitez pas à me partager vos propres idées de réalisation.

Pour continuer:

Pour continuer, voici 2 autres projets qui pourraient vous intéresser:

Laisser un commentaire