GPS Parte 3: Localizador GPS com a BeagleBone Green

· 5 minutos de leitura
Map

Na primeira parte sobre GPS, vimos como este sistema de rastreamento por satélite funciona. Já na segunda parte, vimos uma sentença NMEA funciona e como fazemos para decodificá-la.

Neste artigo, iremos colocar tudo o que vimos em prática. Sendo assim, vamos a lista de material necessária:

No meu caso, irei utilizar uma BeagleBone Green, que foi fornecida pela FILIPEFLOP e um modulo GPS ublox NEO-6m.

Antes de mais nada, vou explicar o que vamos fazer. Basicamente, iremos utilizar a BBG para controlar o GPS e servir como servidor de página para exibir um mapa com a localização atual do GPS. Na imagem abaixo você pode observar o resultado.

GPS BBG

Para criar a parte de templates e rotas foi utilizado o micro framework Bottle. O Bottle é um WSGI para Python, muito similar ao Flask. Optei por ele pelo fato de ser distribuido em um único arquivo, facilitando a vida de quem usa sistemas embarcados.

Outra dependência é o parserd NMEA implementado na segunda parte deste tutorial (Decodificando uma sentença NMEA).

No arquivo abaixo temos o template da página do mapa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<html>
<head>
    <title>GPS Position</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
    <style>
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #map {
        height: 100%;
      }
    </style>
</head>
<body>
    <div id="map"></div>
    <script type="text/javascript">
        function initMap() {
            var map = new google.maps.Map(document.getElementById('map'), {
                zoom: 15,
                center: {
                    lat: {{ latitude }},
                    lng: {{ longitude }}
                }
            });

            var marker = new google.maps.Marker({
                position: {
                    lat: {{ latitude }},
                    lng: {{ longitude }}
                },
                animation: google.maps.Animation.DROP,
                map: map,
                title: 'Current Position'
            });
        }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key={{ api_key }}&callback=initMap" async defer></script>
</body>
</html>

Como podemos observar o código é bem simples, e toda a parte de inicialização do mapa é feito nas linhas 20 a 38. O mapa faz parte da Google Maps Javascript API, o que facilita muito o desenvolvimento.

No arquivo abaixo temos o módulo responsável por conversar com o GPS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# -*- coding: utf-8 -*-

import Adafruit_BBIO.UART as UART
import serial
import thread
import Queue
import nmea

from collections import namedtuple

Pos = namedtuple('Pos', ['latitude', 'longitude'])

q = Queue.Queue()
parser = nmea.NMEAParser()


def listen(q):
    UART.setup("UART2")

    with serial.Serial(port="/dev/ttyO2", baudrate=9600) as ser:
        print('Connected to GPS: %s' % ser.name)
        while True:
            sentence = ser.readline()
            # remove \r\n
            sentence = sentence[:-2]

            if sentence[0] == '$':
                try:
                    gps_data = parser.parse(sentence)

                    if gps_data.is_valid and gps_data.sentence_name != 'VTG':
                        q.put(gps_data)
                except:
                    pass


def start():
    thread.start_new_thread(listen, (q,))


def get_current_position(wait=False):
    try:
        gps_data = q.get(wait)
        return Pos(latitude=gps_data.latitude_degree, longitude=gps_data.longitude_degree)
    except:
        return None


if __name__ == '__main__':
    start()

    while True:
        print(get_current_position(True))

Na linha 3 é importado o módulo UART do Adafruit, se você estiver usando um Raspberry Pi lembre-se de alterar o import. O método listen é quem faz praticamente todo o trabalho. Ele configura o UART, e fica escutando o GPS na porta serial. Ao receber os dados do GPS, é feita a decodificação da sentença, e se for uma sentença válida adicionamos na fila. A sentença VTG é ignorada pois ela não possui latitude e longitude.

O método start cria uma Thread para não dar lockd na aplicação inteira enquanto a porta serial estiver em uso. Isto é necessário para iniciar o servidor WSGI, pois ele é que fica em lockd na aplicação. Por fim, o método get_current_position retorna a posição atual do GPS se disponível.

No arquivo abaixo temos o setupd do nosso WSGI.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-

from bottle import route, run, template

import gps


@route('/')
def index():
    pos = gps.get_current_position()

    if pos:
        return template('index', api_key='<GOOGLE MAPS KEY>', latitude=pos.latitude, longitude=pos.longitude)
    else:
        return template('error')


if __name__ == '__main__':
    gps.start()
    run(host='0.0.0.0', port=8081)

Esta parte não tem muitos mistérios, mas é necessário resaltar que na linha 13, é necessário colocar a sua chave da Google Api para que o mapa seja carregado no browser, isto porque o JavaScript do mapa faz acesso as APIs do Google. Não se preocupe, este serviço é gratuito. :)

Na linha 19, podemos observar que é necessário iniciar a Thread do GPS, e na linha 20, o próprio WSGI.

O projeto em si é relativamente simples, sendo a parte do GPS a com complexidade maior. O intuito desta série de artigos, era mostrar como é simples interpretar os dados recebidos do GPS. Apesar de presente em nosso dia a dia, muitos nem fazemos ideia de como esta tecnologia funciona.

Você pode conferir o código fonte na íntegra no repositório de exemplos do Buteco.

Espero que você tenha gostado deste artigo. Até a próxima.