jeudi 21 janvier 2016

Installer un GPS sur Raspberry PI / Arduino

Le gy-neo6mv2 sorti de l'emballage.
J'ai reçu mon module GPS gy-neo6mv2, vous aussi ? Alors partons ensemble pour la découverte du GPS par port série !

A l'assaut du GPS en UART !

Cela fait 24h que je m'acharne à la mise en oeuvre de ce bébé. Ne vous inquiétez pas, cela n'a pas été ma seule activité ... j'ai fait du sport, je suis allé au cinéma et j'ai invité ma copine au restaurant !
Il n'empêche que ce GPS m'a pris la tête.

Un GPS pas cher

Le GPS dans son emballage
Il existe de très très nombreux GPS sur le marché. Certains sont en kit, d'autres non, certains en USB, d'autres non, ... Pour moi l'important c'était le prix. Pour d'autres ça pourrait être la précision ou encore le poids.
Mon choix s'est donc porté sur un NEO-6M avec antenne plate, souvent utilisé pour les drones volants à cause de son poids. Il coûte environ 20€. Mais il est facile de trouver des modules exploitables pour moins cher :
  • Amazon : version coquée et câblée, livraison gratuite, 12,17€ le 21/01/2016 ;
  • Amazon : version nue, 4 entrées/sorties sans connecteur, livraison gratuite, 12,99€ le 21/01/2016 .
Collection de "pin headers" (mâles, femelles, plaqués or, ...)
Si jamais vous tombez sur une version nue, sachez que les connecteurs ("pin header" en anglais) se vendent pour une bouchée de pain sur AliExpress, de plus de nombreux articles sont vendus avec un lot de connecteurs (exemple : Arduino UNO premier prix sur AliExpress) :

  • AliExpress : deux rangées de connecteurs mâles, livraison gratuite, 0,48€ le 21/01/2016 ;
  • AliExpress : trois rangées de connecteurs femelles, livraison gratuite, 0,83€ le 21/01/2016

Un peu de soudure

Le NEO-6M prêt à recevoir ses connecteurs.
Si vous êtes dans le même cas que moi, quelques points de soudure s'imposent. J'ai décidé de mettre des connecteurs mâles droit. des coudés auraient pu faire l'affaire. Quant aux femelles ce n'est pas mon choix.
Une troisième main et c'est le pied !
Au delà du jeu de mots, je vous conseille vivement l'achat d'une troisième main.
  • Conrad : modèle sans loupe, 7,49€ le 21/01/2016 ;
  • Amazon : modèle avec loupe, livraison gratuite pour 25€ d'achat sur Amazon, 9,41€ le 21/01/2016 ;
  • AliExpress : modèle avec loupe, livraison gratuite (mais lonnggguue), 6,08€ le 21/01/2016.
N'oubliez pas de faire une mesure de continuité entre chacune de vos broches afin de vérifier que votre soudure est propre.

Culture pratique

"How GPS works" !
Le GPS, qui signifie Global Positioning System (et non pas Géo-Positionnement par Satellite), est un système de positionnement militaire créé par les Etat-Unis et composé d'une constellation de 24 satellites et 5 stations terrestres. Dans notre cas nous recevrons un signal de 1575,42MHz. Son nom est L1, de plus notre utilisation est civile, donc ce sera L1C (contrairement à L1M pour la partie militaire).
La grosse différence c'est la précision du récepteur. Si vous êtes civil votre récepteur fonctionnera uniquement en C/A, tandis que si vous êtes militaire vous aurez la possibilité d'utiliser un code nommé P(Y). Depuis les années 2000 cela n'a plus grande importance, mais il est à savoir que les américains peuvent très bien diminuer la précision du signal civil (+/- 100m).
Pourquoi cela n'a plus d'importance ? La mise en place des GPS différentiel (DGPS) permet de limiter grandement l'approximation du GPS. Nous n'en parlerons pas car notre récepteur ne le supporte pas.
Savez-vous combien de satellites sont nécessaires pour calculer la position de votre récepteur ?
Il en faut quatre, trois satellites servent au positionnement (altitude, longitude, latitude) et un quatrième est nécessaire pour vérifier l'heure du récepteur.
Mais alors savez-vous pourquoi vous recevez très bien le GPS sur votre balcon ou dans votre jardin et pas du tout lorsque vous êtes emmuré ? Alors que pourtant vous recevez magnifiquement bien la 3G/4G sur votre téléphone.
C'est lié à la propagation des ondes électromagnétiques. Je serais incapable de vous en faire un cours mais globalement les signaux en provenance des satellites GPS ont tendances à être "bloqués" par la pluie, les nuages, les immeubles ou les arbres. Bref, plus le ciel et l'horizon sont dégagés et plus il est facile de "fix" son GPS.

Partie Raspberry Pi

On commence par la RPi. Et ce n'est pas le plus simple.
Je vais supposer que vous utilisez Raspbian.

Configuration

Premièrement nous allons utiliser les broches 8 et 10 (aussi nommées GPIO 14 et 15, ou encore TXD0 et RXD0). Et pour que tout soit plus simple je vous invite à désactiver l'interface série si ce n'est pas déjà fait.
mode graphique
  • En mode graphique (X) : Menu>Préférences>Raspberry Pi Configuration. Puis "Serial" sur "Disable".
  • En mode en ligne : "sudo raspi-config" 9 Advanced Options>A8 Serial>Non.
Après quoi il va vous falloir éditer cmdline.txt, dans un terminal : "sudo nano /boot/cmdline.txt", modifiez :
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
pour 
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
vous avez donc enlevé console=ttyAMA0,115200 (et peut être kgdboc=ttyAMA0,115200).
Sauvegardez.

Jessie ou Wheezy ?

Jessie !
Je vous invite maintenant à ouvrir un terminal sur votre RPi et à taper "lsb_release -c" si vous ne connaissez pas votre distribution.

Wheezy

Si vous utilisez Wheezy (malheur à vous !), ouvrez un terminal et entrez "sudo nano /etc/inittab", cherchez :
#Spawn a getty on Raspberry Pi serial line
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
et commentez la deuxième ligne :
#Spawn a getty on Raspberry Pi serial line
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
Sauvegardez.

Jessie 

Si vous utilisez Jessie, ouvrez un terminal et entrez :
sudo systemctl stop serial-getty@ttyAMA0.service
sudo systemctl disable serial-getty@ttyAMA0.service

Général 

Maintenant qu'on est prêt ... éteignez la RPi ! ("sudo shutdown -h now")

Connecter le module

Module prêt à l'emploi
Munissez-vous de l'antenne et branchez la sur le circuit imprimé. Prenez également 4 câbles femelle/femelle. Les broches sont celles d'une RPi 2/B+/Zero.
Connexion à la RPi
Branchez :

  • le connecteur VCC du récepteur GPS sur la broche 1 (3,3V), 2 ou 4 (5V) de votre RPi,
  • le connecteur RX du récepteur GPS sur la broche 8 (GPIO 14) de votre RPi (facultatif si vous n'attendez qu'une réception),
  • le connecteur TX du récepteur GPS sur la broche 10 (GPIO 15) de votre RPi,
  • le connecteur GND du récepteur GPS sur la broche 6, 9, 14, 20, 25, 30, 34 ou 39 (masse).
Connexion des broches
Puis allumez votre Raspberry Pi.

Exploiter le module simplement

Attention, votre antenne doit être en extérieur et de préférence dans un milieu avec un ciel dégagé.
Attention, votre module a sans doute une led. Si celle-ci clignote régulièrement (environ toutes les 10 secondes) c'est que votre GPS est "fix" et c'est bon signe. Sans quoi il va vous falloir attendre.
A la première connexion de votre module vous devrez attendre la mise à jour des éphémérides satellites, selon la propagation (nuages, immeubles, ...) cela peut mettre 45 minutes ! (chaque satellite donne ces informations toutes les 12,5min).
Nous effectuerons ces manipulations en ligne de commande uniquement. Ouvrez donc un terminal sur votre RPi.
Supposons que vous n'avez jamais exploité l'UART du RPi. Entrez cette commande :
sudo stty -F /dev/ttyAMA0 raw 9600 cs8 clocal -cstopb -echo
 Un "man stty" nous apprend que la commande permet de modifier des informations et d'imprimer des lignes dans un terminal. le "-F /dev/ttyAMA0" (AMA"ZERO" et pas AMA"O") cible le périphérique UART de la RPi. "raw" permet d'avoir une source non modifiée, "9600" c'est la vitesse du périphérique en bauds (à changer si votre GPS fait 4800 ou autre), "cs8" signifie un code à 8 bits, "clocal" signifie non contrôlé par modem, "-cstopb" signifie 1 bit de stop tandis que "-echo" nous enlèvera les messages superflus.
Maintenant je veux voir les trames NMEA, faîtes un "sudo cat /dev/ttyAMA0".
Ça fonctionne !!
Si vous n'avez pas autant d'informations, mais plutôt des virgules de partout et pas de "N", "S", "E" ou "O", c'est sans doute que vous n'avez pas de "fix" et qu'il va soit falloir attendre plus longtemps (montée des éphémérides) soit dégager un peu le ciel (n'essayez pas de souffler sur les nuages ça ne fonctionnera pas).
Si vous trouvez de nombreux "$GPTXT,01,01,01,NMEA unknown msg*58", la solution, selon U-blox, viendrait d'une proximité des broches TX et RX. Je vous conseille donc d'enlever le connecteur "RX" du module GPS (vous pouvez le faire à chaud sans problème) et de réessayer.
Pour tout autre problème n'hésitez pas à regarder les "sources" et/ou à laisser un commentaire !

Comprendre (un peu) le NMEA 0183

Chouette, pleins de ligne qui commencent par $ et GP machin chose ... On voit des positions mais google ne les prend pas. On voit des chiffres mais ça n'est pas compréhensible ... pas cool !
Premièrement le standard NMEA 0183 (pour National Marine Electronics Association) se décortique facilement avec l'aide qui va bien. Et deuxièmement il faut bien comprendre que toutes les positions (latitude/longitude) ne sont pas "lues" de la même façon.
Voici deux éléments importants à savoir interpréter :
  • La ligne $GPGGA permet de savoir si votre GPS est fixé ainsi que le nombre de satellites qu'il écoute. Exemple :
    • $GPGGA, : identifiant de la trame ;
    • 130402, : heure de la trame en UTC ;
    • 00,4300.00000,N,00500.00000,E, : position en latitude et longitude ;
    • 1, : état du "fix" (0 signifie non fixé et 1 signifie fixé) ;
    • 08, : nombre de satellites en vue ;
    • 1.16,33.1,M,47.6,M,,*6C : reste de la ligne.
  • La ligne $GPRMC est la phrase minimale pour la lecture de la position. Exemple :
    • $GPRMC, : identifiant de la trame ;
    • 135946.00, : heure de la trame en UTC ;
    • A, : ou peu importe ;
    • 4312.34567,N, : 43° et 12,34567 minutes au Nord ;
    • 00512.34567,E, : 005° et 12,34567 minutes à l'Est ;
    • 0.732,,210116,,,A*7D : reste de la ligne.
C'est bien beau, mais comment convertir tout ça en "google map". C'est très simple, si vous êtes au Nord vous commencez par +, au Sud vous commencez par -, puis vous écrivez les deux premiers chiffres suivi du symbole degrés ("°") puis les 2 chiffres suivants, vous marquez un point (".") et rajoutez le reste de la latitude avec une apostrophe à la fin (symbole des minutes "'"), vous enchaînez avec un + si vous êtes à l'Est et un - si vous êtes à l'Ouest, suivi des 3 premiers chiffres de la longitude, le symbole degrés ("°"), les 2 chiffres suivants, un point (".") et les chiffres restants avec une apostrophe à la fin (symbole des minutes "'"). Dans notre exemple : +43°12.34567'+005°12.34567' .
(C'est un exemple, ne pensez pas que j'habite dans l'eau).

Exploiter le module GPS avec gpsd

Dans un terminal, entrez :
sudo apt-get update
sudo apt-get install gpsd gpsd-clients python-gps
python-gps sera utile plus tard. Pour le reste on installe le démon gpsd.
Maintenant deux possibilités :
  • Soit vous voulez utiliser le démon à chaque démarrage, vous devrez d'abord modifier le fichier de configuration grâce à "sudo nano /etc/default/gpsd", afin qu'il ressemble à ça :
  • # Default settings for the gpsd init script and the hotplug wrapper.
    
    # Start the gpsd daemon automatically at boot time
    START_DAEMON="true"
    
    # Use USB hotplugging to add new USB devices automatically to the daemon
    USBAUTO="false"
    
    # Devices gpsd should collect to at boot time.
    # They need to be read/writeable, either by user gpsd or the group dialout.
    DEVICES="/dev/ttyAMA0"
    
    # Other options you want to pass to gpsd
    GPSD_OPTIONS=""
    
    #GPSD_SOCKET="/var/run/gpsd.sock"
    
    Puis vous lancez le démon : "sudo service gpsd start".
  •  Soit vous voulez vous amuser aujourd'hui et faire différemment demain, dans ce cas lancez "sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock".
Plusieurs interfaces de lecture des données GPS sont disponibles grâce à gpsd-clients :
  • gpsmon, pour l'utiliser ouvrez un terminal et tapez "sudo gpsmon" ;
  • cgps, pour l'utiliser ouvrez un terminal et tapez "sudo cgps -s" ;
  • xgps, pour l'utiliser ouvrez un terminal en mode graphique (X) et tapez "sudo xgps".
A vous de décider !

Problèmes ?!

Ca ne fonctionne pas ?
C'est possible ... mais il faut identifier le problème :
  • Vous ne recevez pas les trames :
    • Avez-vous bien branché les câbles ? Si vous n'avez branché que 3 câbles, j'ose espérer que ce sont les VCC, TX et GND du module GPS (soit 3,3V/5V, RX et masse de la RPi) ;
    • Avez-vous correctement soudé les connecteurs au circuit imprimé ?
    • Il y a une odeur de chaud ? Un composant mal soudé ? Si la carte vous semble défectueuse, soit vous la renvoyez, soit vous en essayez une autre.
  • Vous recevez les trames mais pas comme il faut :
    • Il y a pleins de "$GPTXT,01,01,01,NMEA unknown msg*58". Essayez de débrancher le câble TX (côté module GPS)/RX (côté RPi) ;
    • Vous avez plein d'interlignes ? Bah c'est pas grave ... mais vous pouvez relancer une commande du type "sudo stty -F /dev/ttyAMA0 raw" ou "sudo stty -F /dev/ttyAMA0 -echo". Mais gpsd sait éviter les interlignes ;
    • Vous n'avez quasiment que des virgules et pas de données ?
      • Si votre antenne est à l'intérieur, mettez la dehors (en la laissant branchée !) ;
      • S'il y a un ciel tout noir ... attendant le printemps ;
      • Si vous venez de la mettre dehors ... laissez tourner le module une petite heure, levez-vous, étirez-vous, soufflez et allez embrasser votre copine/copain/maman en lui disant combien vous l'aimez, c'est mieux que de s'énerver !
  • Les paquets de gpsd(-clients) ne veulent pas vous écouter ?
    • Vous avez un timeout à chaque fois avec cgps ? Vérifiez que votre récepteur est "fix" grâce aux trames NMEA et aux lignes $GPGGA, si ce n'est pas le cas ... soyez patients !
    • Vous avez été patient ? Alors tentez un "sudo killall gpsd" et relancez gpsd. Ca marche mieux ?
    • Non ? bon alors essayez à nouveau un "sudo killall gpsd" et faîtes "sudo gpsmon /dev/ttyAMA0". Si vous voyez ce qui suit passez au point d'après :
    gpsmon:ERROR: /dev/ttyAMA0 already opened by another process
    gpsmon: activation of device /dev/ttyAMA0 failed, errno=22 (Invalid argument)
    
    • Votre périphérique est "already open" (déjà ouvert) ?
      • Faîtes un "sudo killall gpsd" et réessayez ;
      • Non ? Avez-vous bien désactivé la gestion du port série par Raspbian ? ("sudo raspi-config" partie 9 puis 8A) ;
      • Un autre processus peut utiliser le périphérique, vous pouvez le vérifier grâce à un "lsof -n | grep /dev/ttyAMA0" puis en tuant le processus.
  • Si vous avez une autre erreur, n'hésitez pas à consulter la partie source et/ou écrire un commentaire en bas de l'article !

Python

Il est parfois compliqué de trouver comment fonctionne une librairie, mais une fois que c'est fait on est plutôt content. Surtout qu'en fait "gps3" n'est pas très compliquée.
Ouvrez un terminal, lancez python, puis écrivez :
from gps import *
On importe la bibliothèque. Pour que vous la compreniez on va lancer une session et voir plusieurs fois une interaction simple.
session = gps(mode=WATCH_ENABLE)
# Pour afficher le résultat de cette fonction
session.next()
Je vous invite à utiliser "session.next()" autant de fois que nécessaire pour que vous remarquiez ...

... que ce sont les trames que vous recevez de votre périphérique !
Dès qu'une trame type GGA ou GLL est disponible nous allons pouvoir récupérer des informations telles que la latitude, la longitude ou les satellites disponibles.
Essayez "session.fix.latitude", si ça retourne 0.0 vous n'avez pas encore passé une trame GGA ou GLL, faîtes "session.next()", si c'est la bonne trame réessayez.

Pour que vous ayez un maximum de clefs en main nous allons faire un "cgps-like", en moins poussé.
Puisque l'on est capable de savoir quand mettre à jour les informations affichées, et comment afficher les informations ça va être très simple !

from gps import *
import os

# Créons une fonction
def mon_cgps():
 # On ouvre une session
 session = gps(mode=WATCH_ENABLE)
 # On prévoit les soucis
 try:
  # Et on envoie une boucle infinie
  while True:
   # On envoie les éléments de next() dans une variable
   donnees = session.next()
   # Ce qui nous permet de vérifier lorsque les "fix" sont
   # actualisés (on utilise "class" parce qu'elle est toujours
   # présente)
   if donnees['class'] == "TPV":
    # Pour afficher le résultat
    # On commence par tout effacer
    os.system('clear')
    # Puis on s'amuse !
    print
    print ' Information GPS'
    print '----------------------------------------'
    print 'latitude      ' , session.fix.latitude
    print 'longitude     ' , session.fix.longitude
    print 'temps utc     ' , session.utc
    print 'altitude (m)  ' , session.fix.altitude
    print 'vitesse (m/s) ' , session.fix.speed
    print
    print 'satellites    ' , session.satellites
 # En cas de problème
 except KeyError:
  pass
 except KeyboardInterrupt:
  print
  print "Ferme par l'utilisateur"
 except StopIteration:
  print
  print "GPSD s'est arrete"
 finally:
  session = None

C'est pas mal non ? Il ne reste plus qu'à lancer la fonction grâce à "mon_cgps()".
Pour ceux qui ont envie d'aller plus loin, de nombreux tutoriels existent sur internet, les plus évolués utilise un thread (un processus parallèle pour les néophytes) afin d'exploiter les données au moment opportun.
Au moins vous ne partez pas les mains vides !

Partie Arduino

Vous allez voir ça va être extrêmement compliqué !! (blague)
Chacun ses moyens

Branchement du module

  • le connecteur VCC du récepteur GPS sur une broche 3,3V ou 5V de l'Arduino,
  • le connecteur GND du récepteur GPS sur une masse de l'Arduino.
Pourquoi ne pas connecter le reste maintenant ? C'est simple, nous allons utiliser la voie série par défaut de l'Arduino ... c'est aussi celle qui permet à votre sketch d'être téléversé, donc si on branche un câble on va créer un problème que l'on ne pourra pas gérer de suite.

Création du sketch (simple)

La simplicité même :

void setup() {
  // On envoie la commande de démarrage de la voie série
  Serial.begin(9600);
}

void loop() {
  // Lorsque la voie est disponible en écriture
  if ( Serial.available()) {
    // On écrit ce qu'on lit !
    Serial.write( Serial.read() );
  }
}

Téléversez (CTRL+U) votre sketch dans votre Arduino, puis branchez le connecteur RX du récepteur GPS sur la broche D0 de l'Arduino, et uniquement le RX du GPS (pas le TX).
Maintenant vous avez vos trames dans le moniteur série (CTRL+MAJ+M) !!
Huuumm le NMEA !
Pour ceux qui n'ont pas lu la partie RPi, je vous invite à y faire un tour pour comprendre la norme NMEA 0183.

Création du sketch (avec une librairie)

C'est un peu dommage d'utiliser la liaison série matérielle alors qu'on peut en simuler une logiciellement. Dorénavant changez votre branchement, le connecteur TX du GPS va se brancher sur la prise D2 de l'Arduino, et le connecteur RX du GPS va se brancher sur la prise D3 de l'Arduino (facultatif à nouveau).
Voici le sketch :

// On invoque la librairie SoftwareSerial
#include <softwareserial.h>
// On définit nos broches (autres que 0 et 1)
#define GPS_RX 2
#define GPS_TX 3
// On définit notre liaison série logique
SoftwareSerial ma_liaison_GPS(GPS_RX,GPS_TX);

void setup() {
  // On démarre les liaisons série !
  Serial.begin(9600);
  ma_liaison_GPS.begin(9600);
}

void loop() {
  // Si la liaison avec le GPS envoie a des éléments à
  // envoyer
  if ( ma_liaison_GPS.available()) {
     // Alors on lui envoie ce qu'on lit sur la liaison GPS
    Serial.write( ma_liaison_GPS.read() );
  }
}

Vous pouvez le téléverser. Qu'est-ce qui a changé ? Rien en apparence, mais au moins plus de tracas avec la liaison série matérielle !

Et après ?

Je ne vais pas vous faire essayer d'autres bibliothèques sur Arduino. Par contre je vous invite à aller voir du côté de TinyGPS si vous avez envie d'aller plus loin.

Un GPS en USB !

Jusqu'à présent nous n'avons fait que de l'UART et aucune mise en pratique par USB. Certes pour le cas de l'Arduino c'est un peu comme une conversion des données GPS vers un canal USB. Mais nous ne savons même pas l'exploiter !
Je vous propose d'en voir les détails après la réception de mes convertisseurs CH340 et CP2102

Sources

Générales

  • gps.gov : super poster pour comprendre le fonctionnement du GPS ;
  • gpsinformation : tout connaître sur le tramage NMEA ;
  • Wikipedia :
    • TTFF : Time To First Fix, où savoir attendre un Fix ;
    • UART : Universal Asynchronous Receiver Transmitter ;
  • Breakout Board de chez SparkFun, si vous n'avez que le module U-blox ;

RPi

Arduino

2 commentaires:

  1. Bon blog. Ce serait cool si tu avait un bout de code arduino qui ferait le tri dans toutes les trames MNEA pour n'envoyer que les infos utiles (latitude, longitude. ...) sur le port série.

    RépondreSupprimer
    Réponses
    1. Actuellement j'ai quelques autres projets en tête (hors bricolage électronique), mais je regarderais ça dès que possible :-)

      Supprimer