Raspberry Cars – Les Technos (1/4) – Communication avec la carte GPS en Python
La documentation de la carte
Avant de manipuler la carte, il faut examiner la documentation. Celle-ci est disponible sur le site de Waveshare : https://www.waveshare.com/wiki/GSM/GPRS/GNSS_HAT
Le manuel PDF décrit :
- Les caractéristiques techniques de la carte
- Comment choisir le mode de connection : via le port USB soit via le port GPIO du Raspberry
- Des exemples de programme python qui manipule la carte, mais seulement pour la partie GSM (appels, SMS, etc)
La configuration de la carte (mode USB)
J’ai choisi le mode USB pour communiquer avec la carte car j’ai besoin des ports GPIO pour connecter l’écran tactile (seulement pour la partie tactile, l’affiche passe par le port HDMI).
Pour que la carte fonctionne en mode USB, on doit positionner correctement les jumpers (des petits connecteurs jaunes qui peuvent être fixés de différentes manières sur la carte).
- Jumpers configurés en mode USB (le notre)
- Jumpers configuré pour utiliser le GPIO
Identifier le port USB utilisé par la carte
Pour savoir quel port USB est utilisé par la carte, je dois taper la commande :
ls /dev/ttyUSB*
Elle renvoi la liste des ports USB utilisés, ici seul la carte GPS est connectée et donc visible.
Ici, on voit que la carte est connectée au port /dev/ttyUSB0.
Exemple de programmation en Python et adaptation à Python 3
En cherchant sur Internet, j’ai récupéré un exemple de code Python permettant d’écouter le signal GPS de la carte en Python :
https://www.rhydolabz.com/wiki/?p=18639
Mais la version de python utilisée était la 2, j’ai dû modifier ce code pour la version 3 :
- La syntaxe des instructions print(), avec les parenthèses
- La nécessité d’utiliser .encode(‘ascii’) et decode(‘ascii’) pour les appels à port.write et port.red (lecture écriture sur le port série / USB
Examen de l’exemple : lecture du flux de données
Voici ce que donne l’exécution du programme d’exemple dans l’IDE Python :
Examinons la première partie du code Python; le code est organisé de la manière suivante :
- Cela commence par l’importation des librairies nécessaires : serial pour les lectures et écriture USB ( import serial )
- L’ouverture du port USB connecté à la carte port = serial.Serial(… Le port indiqué ( (« /dev/ttyUSB0) est celui que nous avons trouvé dans la partie précédente
- Use série de commandes AT (ce sont les textes à
envoyer à la carte sur le port USB pour lui demander de travailler), Après
chaque envoi de commande, le programme lit la
réponse, et attend un dixième de seconde.
- AT : elle renvoie OK comme réponse
- AT+CGNSPWR=1 : elle active le module GPS
- AT+CGNSIPR=115200 : elle configure la vitesse de transmission des sonnées entre la carte et le Raspberry à 115200 bauds
- AT+CGNSTST=1 : elle demande à la carte d’écrire les données GPS en continu vers le port USB, et donc vers le Raspberry
- AT+CGNSINF : elle renvoie le statut de la carte
- Une boucle infinie qui
- lit le flux d’information envoyé par la carte
- L’imprime dans la console
- Attend une demi seconde que d’autres données arrivent
#!/usr/bin/python
import serial
import time
from decimal import *
from subprocess import call
def find(str, ch):
for i, ltr in enumerate(str):
if ltr == ch:
yield i
# Enable Serial Communication
port = serial.Serial("/dev/ttyUSB0", baudrate=115200, timeout=1)
# Transmitting AT Commands to the Modem
# '\r\n' indicates the Enter key
port.write(('AT'+'\r\n').encode('ascii'))
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSPWR=1'+'\r\n').encode('ascii'))
# to power the GPS
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSIPR=115200'+'\r\n').encode('ascii'))
# Set the baud rate of GPS
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSTST=1'+'\r\n').encode('ascii'))
# Send data received to UART
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSINF'+'\r\n').encode('ascii'))
# Print the GPS information
rcv = port.read(200).decode('ascii')
print (rcv)
time.sleep(.1)
while True:
fd = port.read(200).decode('ascii')
# Read the GPS data from UART
print ("BLOC:")
print(fd);
print("")
time.sleep(.5)
Voici le résultat dans la console python durant l’exécution :
- Les premières interactions durant la configuration :
>>> %Run gps.py
AT
OK
AT+CGNSPWR=1
OK
AT+CGNSIPR=115200
OK
+CGNSPWR: 1
AT+CGNSTST=1
OK
AT+CGNSINF
+CGNSINF: 0,,,,,,,,,,,,,,,,,,,,
OK
$PMTK011,MTKGPS*08
$PMTK010,001*2E
- Puis le flux de données GPS commence à s’afficher par blocs :
- Les lignes de positions commencent par $GNRMC.
- Initialement le GPS ne connait pas encore la position : $GNRMC,235944.539,V,,,,,0.00
- Puis le GPS la connait et la fournit $GNRMC,114452.000,A,4936.683239,N,00609.052326,E,0.15,0.00,171119,,,A*7F
- On sait que les lignes sont bonnes grâce à l’indicateur A ou V. Les bonnes lignes sont indiquées par A.
BLOC:
$PMTK011,MTKGPS*08
$PMTK010,002*2D
$GNGGA,235944.539,,,,,0,0,,,M,,M,,*54
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GLGSA,A,1,,,,,,,,,,,,,,,*02
$GPGSV,1,1,00*79
$GLGSV,1,1,00*65
$GNRMC,235944.539,V,,,,,0.00
BLOC:
,0.00,050180,,,N*5D
$GNVTG,0.00,T,,M,0.00,N,0.00,K,N*2C
$GPACCURACY,9999000.0*08
$GNGGA,235945.539,,,,,0,0,,,M,,M,,*55
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GLGSA,A,1,,,,,,,,,,,,,,,*02
$GPGSV,1,1,00*79
BLOC:
$GLGSV,1,1,00*65
$GNRMC,235945.539,V,,,,,0.00,0.00,050180,,,N*5C
$GNVTG,0.00,T,,M,0.00,N,0.00,K,N*2C
$GPACCURACY,9999000.0*08
$GNGGA,235946.538,,,,,0,0,,,M,,M,,*57
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
..... quelques blocs plus tard .....
BLOC:
,00609.052326,E,1,5,1.90,299.486,M,47.677,M,,*47
$GPGSA,A,3,07,22,19,09,,,,,,,,,2.09,1.90,0.88*07
$GLGSA,A,3,76,,,,,,,,,,,,2.09,1.90,0.88*1C
$GPGSV,2,1,07,09,86,226,33,37,31,160,,07,27,171,41,19,12
BLOC:
,234,29*78
$GPGSV,2,2,07,22,10,112,32,03,,,43,23,,,18*40
$GLGSV,1,1,01,76,31,112,33*55
$GNRMC,114452.000,A,4936.683239,N,00609.052326,E,0.15,0.00,171119,,,A*7F
$GNVTG,0.00,T,,M,0.15,N,0.27,K,A*22
BLOC:
$GPACCURACY,38.9*3A
$GNGGA,114453.000,4936.683280,N,00609.052267,E,1,5,1.89,298.667,M,47.677,M,,*44
$GPGSA,A,3,07,22,19,09,,,,,,,,,2.09,1.89,0.88*0F
$GLGSA,A,3,76,,,,,,,,,,,,2.09,1.89,0.88*14
$GP
BLOC:
GSV,2,1,07,09,86,226,33,37,31,160,,07,27,171,41,19,12,234,29*78
$GPGSV,2,2,07,22,10,112,32,03,,,42,23,,,18*41
$GLGSV,1,1,01,76,31,112,33*55
$GNRMC,114453.000,A,4936.683280,N,00609.052267,E,0.22,0.0
BLOC:
0,171119,,,A*7C
$GNVTG,0.00,T,,M,0.22,N,0.41,K,A*26
$GPACCURACY,37.5*39
$GNGGA,114454.000,4936.683205,N,00609.052184,E,1,5,1.89,299.035,M,47.677,M,,*40
$GPGSA,A,3,07,22,19,09,,,,,,,,,2.09,1.89,0.8
..... etc. .....
Examen de l’exemple : extraction des coordonnées GPS
Le code du programme d’exemple complet extrait dans la boucle infinie les coordonnées GPS contenue dans les lignes $GNRMC. Voici comment il fonctionne :
- Il commence de la même manière par toutes les initialisations de la carte,
- Puis, dans la boucle, le code décompose le texte reçu depuis la carte.
Cette boucle traite le code suivant :
- Il repère les caractères $GNRMC ,
- Il détecte si la ligne est marquée avec un A,
- Il vérifie s’il y a assez de caractères après le A qui ont été reçu (au moins 50)
if '$GNRMC' in fd: # To Extract Lattitude and
ps=fd.find('$GNRMC') # Longitude
dif=len(fd)-ps
if dif > 50:
- Il cherche dans le texte qui suit la prochaine virgule, et les deux sous chaînes Lon et Lat qui contiennent les coordonnées
p=list(find(data, ","))
lat=data[(p[2]+1):p[3]]
lon=data[(p[4]+1):p[5]]
- Puis il convertit les valeurs en nombre exploitable et les imprime
s1=lat[2:len(lat)]
s1=Decimal(s1)
s1=s1/60
s11=int(lat[0:2])
s1 = s11+s1
s2=lon[3:len(lon)]
s2=Decimal(s2)
s2=s2/60
s22=int(lon[0:3])
s2 = s22+s2
print (s1)
print (s2)
Voici le résultat affiché dans la console
$GNRMC,114603.000,A,4936.687012,N,00609.048559,E,0
49.6114502
6.150809316666666666666666667
$GNRMC,114605.000,A,4936.687012,N,00609.048559,E,0
49.6114502
6.150809316666666666666666667
$GNRMC,114606.000,A,4936.687012,N,00609.048559,E,0
49.6114502
6.150809316666666666666666667
$GNRMC,114608.000,A,4936.687012,N,00609.048559,E,0
49.6114502
6.150809316666666666666666667
Et le code du programme complet
#!/usr/bin/python
import serial
import time
from decimal import *
from subprocess import call
def find(str, ch):
for i, ltr in enumerate(str):
if ltr == ch:
yield i
# Enable Serial Communication
port = serial.Serial("/dev/ttyUSB0", baudrate=115200, timeout=1)
# Transmitting AT Commands to the Modem
# '\r\n' indicates the Enter key
port.write(('AT'+'\r\n').encode('ascii'))
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSPWR=1'+'\r\n').encode('ascii'))
# to power the GPS
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSIPR=115200'+'\r\n').encode('ascii'))
# Set the baud rate of GPS
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSTST=1'+'\r\n').encode('ascii'))
# Send data received to UART
rcv = port.read(100).decode('ascii')
print (rcv)
time.sleep(.1)
port.write(('AT+CGNSINF'+'\r\n').encode('ascii'))
# Print the GPS information
rcv = port.read(200).decode('ascii')
print (rcv)
time.sleep(.1)
while True:
fd = port.read(200).decode('ascii')
time.sleep(.5)
if '$GNRMC' in fd:
ps=fd.find('$GNRMC')
dif=len(fd)-ps
if dif > 50:
data=fd[ps:(ps+50)]
print (data)
ds=data.find('A')
if ds > 0 and ds < 20:
p=list(find(data, ","))
lat=data[(p[2]+1):p[3]]
lon=data[(p[4]+1):p[5]]
s1=lat[2:len(lat)]
s1=Decimal(s1)
s1=s1/60
s11=int(lat[0:2])
s1 = s11+s1
s2=lon[3:len(lon)]
s2=Decimal(s2)
s2=s2/60
s22=int(lon[0:3])
s2 = s22+s2
print (s1)
print (s2)