Per començar | Elements d'entrada | Programació CircuitPython | Recursos CITCEA | |
Elements no electrònics | Elements de sortida | Programació Arduino | ||
Projectes | Elements de control | Dades pràctiques | Inici |
El sensor GPS ens permet conèixer la nostra posició geogràfica actual però també ens facilita tota una sèrie de dades addicionals com l'hora actual, l'orientació (angle respecte al nord), l'altitud i la velocitat de desplaçament.
El Sensor GPS es comunica amb el microcontrolador amb una connexiò sèrie (la mateixa que fa servir el monitor sèrie). Cal connectar, a més de l'alimentació, la pota TX del sensor amb la RX del microcontrolador i la pota RX del sensor amb la TX del microcontrolador. La figura següent mostra com connectar-lo:
Cal tenir present que el sensor GPS requereix rebre informació des dels satèl·lits del sistema GPS. Per tant cal que estigui a l'exterior o al costat d'una finestra des de la que pugui veure un nombre suficient de satèl·lits.
Els punts de connexió de la placa del sensor GPS són els següents:
Pota | Utilització | Comentaris |
3.3 V | Positiu de l'alimentació | |
GND | Negatiu de l'alimentació | |
TX | Transmissió de dades | Comunicaciò sèrie |
RX | Recepció de dades | |
FIX | Connectat als satèl·lits | La sortida està desactivada (GND) quan hi ha connexió i està intermitent si no n'hi ha. La placa porta un LED connectat a aquesta pota. |
BAT | Positiu de la bateria | Possibilitat de connectar-hi una bateria d'entre 3 i 3,3 V |
La funció de la bateria és alimentar el sensor quan no està alimentat el microcontrolador. Amb la bateria el sensor pot mantenir informació de l'hora exacta. Mentre el sensor s'alimenta per la pota BAT només funciona el seu rellotge intern i no es comunica ni amb els satèl·lits ni amb el microcontrolador.
La pota fix la podríem connectar a una entrada del microcontrolador i així ens podríem estalviar de comunicar-nos amb el sensor mentre no està connectat amb els satèl·lits.
En el programa que presentem a continuació podem provar la comunicació del microcontrolador amb el sensor i analitzar les dades que rebem. El probrama obre dos canals sèrie i mira per quin dels dos es reben dades. Quan detecta que un dels canals rep dades les llegeix i les envia cap a l'altre (que suposa que és el monitor sèrie).
void setup() { while (!Serial); { // Esperem a que hi hagi la connexió sèrie connectada } // Obrim dues connexions sèrie, una per al GPS i una amb l'ordinador // però no sabem quina és quina Serial.begin(9600); // Primera comunicació sèrie Serial1.begin(9600); // Segona comunicació sèrie }
void loop() { if (Serial.available()) { // Mira si estan arribant dades per aquesta connexió char dades = Serial.read(); // Llegeix les dades i les guarda a una variable Serial1.write(dades); // I les mostra per l'altra connexió, // que correspondrà a l'ordinador } if (Serial1.available()) { // Mira si estan arribant dades per aquesta connexió char dades = Serial1.read(); // Llegeix les dades i les guarda a una variable Serial.write(dades); // I les mostra per l'altra connexió, // que correspondrà a l'ordinador } }
Un programa similar en CircuitPython seria el següent
import board import busio import digitalio
# LED de la placa led = digitalio.DigitalInOut(board.D13) led.direction = digitalio.Direction.OUTPUT # Comunicacio serie serie = busio.UART(board.TX, board.RX, baudrate=9600)
while True: # Llegim les dades lectura = serie.read(32) # Llegim fins a 32 bytes if lectura is not None: led.value = True # Convertim a una cadena de caracters lectura_text = ''.join([chr(b) for b in lectura]) print(lectura_text, end="") led.value = False
Per exemple, posant el sensor al costat de la finestra del meu despatx vaig rebre, entre altres, les següents dades:
$GPRMC,141159.000,A,4123.0481,N,00206.9632,E,0.16,295.02,181214,,,D*65 $GPGGA,141159.000,4123.0481,N,00206.9632,E,2,09,1.14,88.8,M,51.3,M,0000,0000*57
La primera línia ($GPRMC) correspon a les dades mínimes recomanades i la segona ens aporta informació addicional. Anem a analitzar la informació rebuda a la primera línia:
Observem com convertir les dades rebudes en informació útil. L'hora actual té les hores, els minuts i els segons abans del punt i les mil·lèsimes de segon després del punt. L'hora indicada és la del meridià de Greenwich o hora GMT que correspon a la nostra hora solar. La nostra hora oficial la podem obtenir sumant 1 a l'hora GMT a l'hivern i sumant-hi 2 a l'estiu.
Si ens surt una A després de l'hora és que les dades són vàlides. En cas contrari surt una V.
La latitud és l'angle del punt considerat respecte a l'equador. Una latitud zero correspon a l'equador, les latituds positives a punts situats al nord de l'equador i les negatives a punt situats al sud. Els angles de 90 graus (positius o negatius) corresponen als pols. La latitud no pot ser més gran de +90° ni més petita de -90°. A la latitud les dues primeres xifres són els graus i les següents són els minuts. Si la latitud és positiva (nord) surt una N i si és negativa (sud) una S.
La longitud correspon a l'angle, dibuixat a l'equador, del punt considerat respecte al meridià de Greenwich. A la longitud les tres primeres xifres són els graus i les següents són els minuts. Si la longitud és positiva (est) surt una E i si és negativa (oest) una W.
Les coordenades que rebem directament del sensor no ens serveixen per a la majoria d'aplicacions (per exemple Google Maps) i cal convertir-les. Per exemple, Google Maps no entendrà les coordenades així:
4123.0481 N, 00206.9632 E
Però sí que les entén d'aquesta altra manera:
41° 23.0481, 002° 06.9632
Segons el que volguem fer, ens caldrà passar les coordenades a unes unitats diferents. Per fer comparacions ens serà útil tenir-les en una única unitat (graus, minuts o segons). Per fer càlculs, normalment, serà preferible passar-les a radians que és com treballen les funcions trigonomètriques disponibles als microcontroladors.
Si necessitem calcular la distància entre dos punts podem fer servir la fórmula del haversine.
La velocitat de desplaçament es mesura en nusos (knot). Cal multiplicar el valor per 1,852 per obtenir la velocitat en km/h.
L'orientació ens diu l'angle (en graus) respecte al nord.
Finalment tenim la data amb l'any indicat amb només dues xifres.
Una part de la informació de la segona línia és redundant.
A més hi podem trobar la qualitat del senyal rebut, el nombre de satèl·lits detectats i l'altitud en metres.
Per fer servir el sensor en l'entorn Arduino ens convindrà la Adafruit-GPS-Library que ens facilita molt la feina ja que ens dóna les dades per separat. Si és necessari, podeu consultar la instal·lació de biblioteques. Un cop ja tinguem la biblioteca, podem fer els nostres programes. Les diferents dades que podem demanar (suposant que hem definit el sensor amb el nom sensorGPS) són:
Tipus | Comanda | Utilitat |
Configuració | sensorGPS.begin(velocitat) | Inicialitza el sensor GPS i defineix la velocitat de comunicació |
sensorGPS.sendCommand(comanda) | Envia una comanda de configuració | |
sensorGPS.lastNMEA() | Dades del sensor en brut | |
Cobertura | sensorGPS.parse(sensorGPS.lastNMEA()) | Torna true si les dades són útils |
sensorGPS.fix | Dóna 0 si no ha trobat prou satèl·lits | |
sensorGPS.fixquality | Qualitat del senyal | |
sensorGPS.satellites | Nombre de satèl·lits | |
Hora GMT | sensorGPS.hour | Hora |
sensorGPS.minute | Minuts | |
sensorGPS.seconds | Segons | |
sensorGPS.milliseconds | Mil·lisegons | |
Data | sensorGPS.day | Dia |
sensorGPS.month | Mes | |
sensorGPS.year | Any (dues xifres) | |
Coordenades | sensorGPS.latitude | Latitud en el format del sensor |
sensorGPS.lat | Sentit de la latitud (N o S) | |
sensorGPS.longitude | Longitud en el format del sensor | |
sensorGPS.lon | Sentit de la longitud (E o W) | |
Altres dades | sensorGPS.speed | Velocitat (en nusos) |
sensorGPS.angle | Orientació (en graus) | |
sensorGPS.altitude | Altitud (en metres) |
A continuació tenim un programa bàsic que llegeix les dades del sensor i les envia al monitor sèrie.
Inicialment inclou les biblioteques del sensor GPS i de la comunicació sèrie. Després defineix un objecte corresponent al sensor (en el que hem d'indicar la comunicació sèrie que farà servir), una variable per controlar el temps i una per indicar si farem servir interrupcions que definirem com a false perquè no les farem servir.
En el setup obrim una comunicació sèrie amb l'ordinador, inicialitzem i configurem el sensor GPS i esperem un segon a que el sensor es reconfiguri.
En el bucle loop llegim el sensor i mirem si han arribat dades correctes. Si no han arribat dades tornem a començar el loop fins que arribin. Per no saturar la comunicació, només tractem les dades cada dos segons. Això ho fem guardant el valor inicial de la funció millis en una variable i comparant-los. Quan la diferència sigui més gran que 2000 vol dir que ja han passat dos segons. També vigilem que millis no s'hagi reinicialitzat i si és el cas actualitzem la variable.
Cada dos segons mostrem les dades llegides. Primer mostrem les dades tal com han arribat i seguidament hi ha aquelles que es poden obtenir amb pocs satèl·lits detectats i després aquelles que requereixen més satèl·lits i que només mostrarem si SensorGPS.fix està a 1 que vol dir que hem pogut connectar amb el mínim nombre de satèl·lits necessàris.
#include <Adafruit_GPS.h> #include <SoftwareSerial.h>
Adafruit_GPS SensorGPS(&Serial1); // Definim el sensor i li diem que faci servir la connexió Serial1 boolean usingInterrupt = false; // No farem servir interrupcions // La funció millis() ens va donant el temps transcorregut en mil·lisegons // Quan el valor ja no hi cap (al cap d'uns 50 dies) es reinicialitza (torna a zero) uint32_t timer = millis(); // Variable que guarda informació del temps
void setup() { Serial.begin(115200); // Comunicació amb l'ordinador SensorGPS.begin(9600); // Comunicació amb el sensor GPS SensorGPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); // Configuració del sensor SensorGPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // El sensor envia dades un cop cada segon (1 Hz) delay(1000); // Espera un segon a que el GPS s'inicialitzi amb la configuració }
void loop() { char dades = SensorGPS.read(); // Llegim dades del Sensor GPS if (SensorGPS.newNMEAreceived()) { // Si han arribat dades del sensor Serial.println(SensorGPS.lastNMEA()); // Envia a l'ordinador les dades llegides if (!SensorGPS.parse(SensorGPS.lastNMEA())) { // Mira si les dades tenen informació útil return; // Si no, torna a començar el loop per llegir dades noves } } // Si timer és més gran que millis vol dir que millis s'ha reiniciat // Llavors tornem a inicialitzar timer if (timer > millis()) { timer = millis(); } if (millis() - timer > 2000) { // Si la diferència és més gran que 2000 vol dir // que han passat més de 2 s timer = millis(); // Reiniciem timer Serial.println(); // Una línia en blanc // Enviem dades de la connexió Serial.print("Connectat: "); Serial.println((int)SensorGPS.fix); // Serà 0 si no ha trobat prou satèl·lits Serial.print("Qualitat del senyal: "); Serial.println((int)SensorGPS.fixquality); // Enviem l'hora Serial.print("Hora actual: "); Serial.print(SensorGPS.hour, DEC); Serial.print("."); Serial.print(SensorGPS.minute, DEC); Serial.print("."); Serial.print(SensorGPS.seconds, DEC); Serial.print(","); Serial.print(SensorGPS.milliseconds); // Acabada l'hora, fem un salt de línia Serial.println(" GMT"); // Enviem la data Serial.print("Data actual: "); Serial.print(SensorGPS.day, DEC); Serial.print("-"); Serial.print(SensorGPS.month, DEC); Serial.print("-20"); // Envia l'any amb dues xifres Serial.println(SensorGPS.year, DEC); // Acabada la data, fem un salt de línia if (SensorGPS.fix) { // El que segueix no té sentit si no ha trobat prou satèl·lits Serial.print("Sats: "); // Nombre de satèl·lits Serial.println((int)SensorGPS.satellites); // Dades de posició Serial.print("Coordenades: "); Serial.print(SensorGPS.latitude, 4); // Angle latitud amb quatre decimals Serial.print(SensorGPS.lat); // Lletra latitud Serial.print(", "); Serial.print(SensorGPS.longitude, 4); // Angle longitud amb quatre decimals Serial.println(SensorGPS.lon); // Lletra longitud // Altres dades Serial.print("Velocitat: "); Serial.print(SensorGPS.speed); Serial.println(" nusos"); Serial.print("Velocitat: "); Serial.print(1.852*SensorGPS.speed); Serial.println(" km/h"); Serial.print("Angle respecte al nord: "); Serial.print(SensorGPS.angle); Serial.println(" graus"); Serial.print("Altitud: "); Serial.print(SensorGPS.altitude); Serial.println(" m"); } } }
A continuació hi ha un exemple del que s'ha mostrat pel monitor sèrie a l'executar aquest programa:
Connectat: 1 Qualitat del senyal: 2 Hora actual: 14.11.59,0 GMT Data actual: 18-12-2014 Sats: 9 Coordenades: 4123.0483N, 206.9632E Velocitat: 0.16 nusos Velocitat: 0.30 km/h Angle respecte al nord: 295.02 graus Altitud: 88.80 m
Per fer servir el sensor en CircuitPython ens convindrà la biblioteca adafruit_gps.mpy que ens facilita molt la feina ja que ens dóna les dades per separat. Si és necessari, podeu consultar la instal·lació de biblioteques. Un cop ja tinguem la biblioteca, podem fer els nostres programes. Les diferents dades que podem demanar (suposant que hem definit el sensor amb el nom gps) són:
Tipus | Comanda | Utilitat |
Configuració | gps.send_command(comanda) | Envia una comanda de configuració |
gps.update() | Llegeix les dades del sensor | |
Cobertura | gps.has_fix | Dóna 0 si no ha trobat prou satèl·lits |
gps.fix_quality | Qualitat del senyal | |
gps.satellites | Nombre de satèl·lits | |
Hora GMT | gps.timestamp_utc.tm_hour | Hora |
gps.timestamp_utc.tm_min | Minuts | |
gps.timestamp_utc.tm_sec | Segons | |
Data | gps.timestamp_utc.tm_mday | Dia |
gps.timestamp_utc.tm_mon | Mes | |
gps.timestamp_utc.tm_year | Any (dues xifres) | |
Coordenades | gps.latitude | Latitud en el format del sensor |
gps.longitude | Longitud en el format del sensor | |
Altres dades | gps.speed_knots | Velocitat (en nusos) |
gps.altitude_m | Altitud (en metres) |
import time import board import busio import adafruit_gps
# Crea un canal serie per comunicar amb el sensor # Posem un timeout raonable per llegir cada segon uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=3000) # Crea un objecte GPS gps = adafruit_gps.GPS(uart)
# Configura la comunicacio amb el sensor en el format mes habitual gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') # I llegint valors cada segon (1000 ms) gps.send_command(b'PMTK220,1000') darrer_valor = time.monotonic() while True: # Llegim el sensor # La lectura nomes sera diferent si ha passat mes d'un segon # ja que hem dit que llegeixi cada segon gps.update() # Mirem la marca de temps de la darrera lectura actual = time.monotonic() if actual - darrer_valor >= 1.0: # Ha passat 1 s? darrer_valor = actual if not gps.has_fix: # Encara no tenim un senyal estable print('Esperant un valor estable...') else: # Tenim un senyal estable print('') # Linia de separacio print('Qualitat del senyal: ', gps.fix_quality) print('Hora actual: {:02}.{:02}.{:02} GMT'.format( gps.timestamp_utc.tm_hour, gps.timestamp_utc.tm_min, gps.timestamp_utc.tm_sec)) print('Data actual: {}-{}-{}'.format( gps.timestamp_utc.tm_mday, gps.timestamp_utc.tm_mon, gps.timestamp_utc.tm_year)) if gps.satellites is not None: print('Sats: ', gps.satellites) print('Latitud: ', gps.latitude, ' graus') print('Longitud: ', gps.longitude, ' graus') if gps.speed_knots is not None: print('Velocitat: ', gps.speed_knots, ' nusos') if gps.speed_knots is not None: print('Velocitat: ', 1.852*gps.speed_knots, ' km/h') if gps.altitude_m is not None: print('Altitud: ', gps.altitude_m, ' m')
A continuació hi ha un exemple del que s'ha mostrat pel monitor sèrie a l'executar aquest programa:
Qualitat del senyal: 1 Hora actual: 14.11.59 GMT Data actual: 12-07-2018 Sats: 6 Latitud: 41.2305 graus Longitud: 2.06963 graus Velocitat: 0.0 nusos Velocitat: 0.0 km/h Altitud: 88.8 m
En aquest web, les fotografies marcades amb [AF] són del web d'Adafruit, les marcades amb [SF] del web d'Sparkfun i les marcades amb [AU] del web d'Arduino.
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.