Jul 06 2010

ARDUINO + GPS: como montar seu navegador GPS

Usarei 3 peças para fazer este navegador:

  1. Arduino Duemilanovehttp://www.webtronico.com/produtos/view/5
  2. LCD Shield http://www.webtronico.com/produtos/view/34
  3. Módulo GPS (SKM53)http://www.webtronico.com/produtos/view/22

O LCD Shield possui um LCD 16×2 (16 caracteres e 2 linha) e teclado com 5 push buttons, que vou utilizar para selecionar a função que vai aparecer no display.  Este teclado é bem interessante, pois os 5 push buttons são ligados em apenas uma entrada analógica (AD). Isto significa que cada botão “envia” uma tensão diferente para o AD, e como você conhece a tensão de cada botão você consegue mapear qual botão foi pressionado. Muito fácil e muito útil pois economiza pinos I/O.

Para escrever no LCD estou utilizando a biblioteca LCD4Bit_mod, onde simplesmente você precisa utilizar a função lcd.printIn(“Escreve no LCD”);

O módulo GPS também é bastante simples. Ele possui antena embutida e é alimentado com 5V. O pino de TX é ligado no pino RX do Arduino. Quando alimentando o GPS começa a se comunicar com o Arduino automaticamente, com velocidade padrão de 9600 bauds. O GPS envia com frequência de 1Hz (1 vez por segundo) os dados de localização. Estes dados seguem um padrão chamado NMEA que é mais ou menos assim:

$GPGGA,122021.000,3023.7865,S,05120.3565,W,1,9,0.90,31.7,M,4.5,M,,*59
$GPGSA,M,3,29,30,21,10,02,12,18,05,31,,,,1.18,0.90,0.77*0A
$GPGSV,3,1,10,30,81,029,33,29,57,192,30,21,44,303,31,12,42,043,30*7B
$GPGSV,3,2,10,05,34,110,27,31,27,253,18,10,11,136,23,18,10,354,23*7B
$GPGSV,3,3,10,02,07,133,21,34,,,*48
$GPRMC,122021.000,A,3023.7865,S,05120.3565,W,0.49,268.48,060710,,,A*62
Vou descrever apenas uma linha: $GPRMC
$GPRMC,122021.000,A,3023.7865,S,05120.3565,W,0.49,268.48,060710,,,A*62

0 – ID
1- Hora (UTC)
2-validade (A-OK, V-invalido)
3-Latitude
4-Hemisfério da latitude
5-Longitude
6-Hemisfério da longitude
7-velocidade em knots (1 knots = 1.852 Km/h)
8-curso verdadeiro
9-data UT
10-variação magnética em graus
11-E ou W
12-Checksum

Tudo explicado, vamos a montagem!

A montagem é bastante simples: o GPS é conectado no LCD Shield nos seguintes pinos:


GPS LCD Shield
VCC 5V
GND GND
TX RX

Agora basta conectar o LCD Shield com GPS no Arduino

ATENÇÃO: Desconecte o LCD Shield quando for gravar o firmware no Arduino, Pois o TX do GPS conflita com o TX do PC.

Download do programa + biblioteca LCD:
webtronicogps.zip

  • Descompacte a pasta “LCD4Bit_mod” em “\arduino-0018\libraries\”
  • Abra o arquivo “WebtronicoGPS.pde” no programa de desenvolvimento do Arduino

Abaixo segue o programa comentado:


#include <LCD4Bit_mod.h>
#define DADOS_LEN 100
#define IDLEN 6
#define TEMPLEN 11

//cria objeto para controlar LCD
LCD4Bit_mod lcd = LCD4Bit_mod(2); //numero de linhas do LCD = 2
char dados[DADOS_LEN]; //buffer dos dados do GPS
byte conta=0; //variavel auxiliar para contar
char* idnmea[] = {"$GPRMC","$GPGGA"}; //IDs dos NMEA que vou utilizar
#define GPRMC 0
#define GPGGA 1
byte verificador[]= {0,0}; //variavel auxiliar para verificar ID NMEA
byte indice[12]; //Em uma linha $GPRMC contem 12 valores separados por virgulas. Esta variavel guarda a posição do caracter de inicio de cada valor.
byte contindice=0; //variavel auxiliar de controle usada na variavel indice[];
byte menu=0; // Menu do LCD: 0-botão RIGHT, 1-botão UP, 2-botao DOWN, 3-botão LEFT, 4-botão SELECT
char tempmsg[TEMPLEN]; //variavel temporaria auxiliar para guarda o valor de um dado extraido do GPS.

//teclado
int  adc_key_val[5] ={30, 150, 360, 535, 760 }; //valores do divisor de tensão do teclado do LCD Shield
#define NUM_KEYS 5 //numero de teclas do teclado
int adc_valor_in; //valor da entrada analogica do teclado
byte key=-1; //tecla pressionada
byte oldkey=-1; //tecla pressionada anteriormente

void setup(){
  lcd.init(); //inicia LCD
  lcd.clear(); //Limpa LCD
  lcd.printIn("WebTronico.com"); //Escreve no LCD
  lcd.cursorTo(2, 0); //Coloca o cursor do LCD na linha 2 e coluna 0
  lcd.printIn("Ligando GPS"); //Escreve no LCD
  limpaBuffer(); //limpa buffer do GPS (dados)
  limpaTemp(); //limpa dado do GPS (tempmsg)
  Serial.begin(9600); //Inicia UART para comunicar com módulo GPS
}
void loop(){
    while(Serial.available()){//Enquanto tiver caracteres no buffer
      dados[conta] = Serial.read(); //pega caracter do buffer da Serial
      if(dados[conta]==13){ //Se o caracter = 13 então é final de linha. Vamos interpretar esta linha que recebemos
        verificador[GPRMC]=0; //verifica idnmea[0] ($GPRMC)
        //verificador[GPGGA]=0;//verifica idnmea[1] ($GPGGA)
        for(byte i=1;i<=IDLEN;i++){ //verificando o ID NMEA da linha recebida
          if(dados[i]==idnmea[GPRMC][i-1]){ //Verifica se é $GPRMC
            verificador[GPRMC]++; //incrementa 1
          }

          //Não vou implementar $GPGGA para não ficar muito confuso
          /*if(dados[i]==idnmea[GPGGA][i-1]){ //Verifica se é $GPGGA
            verificador[GPGGA]++; //incrementa 1
          }*/
        }
        if(verificador[GPRMC]==IDLEN){ //A linha recebida é $GPRMC
          //A linha GPRMC tem 11 "," que dividem 12 dados
          //exemplo: $GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70
          //            0      1   2    3    4     5    6    7    8     9     10    11
          // nos interessa: 2-timestamp (UTC), 3-latitude, 4-Norte/Sul, 5-Longitude, 6-Leste(E)/Oeste(W),7-velocidade (em nós),9-date stamp
          contindice = 0;
          indice[contindice] = 1; //dados[] inicia no caracter 1
          contindice++;
          for(byte i=1; i<DADOS_LEN;i++){//percorre toda linha dados[] identificando onde começa cada valor do GPS
            if(dados[i]==','){//achou o final de um dado
              indice[contindice] = i+1;
              contindice++;
            }
          }
          adc_valor_in = analogRead(0); //verifica entrada analogica do teclado
          key = get_key(adc_valor_in); //interpreta valor da entrada analogica
          if (key != oldkey){ //verifica se o valor encontrado é diferente do valor anterior
            delay(50);		// faz um delay para o debounce
            adc_valor_in = analogRead(0);    // le novamente a entrada analogica para confirmar
            key = get_key(adc_valor_in);		        // interpreta
            if (key != oldkey){			//verifica se é diferente
              oldkey = key; //atualiza oldkey
              lcd.clear(); //limpa LCD
              if (key >=0){ //se alguma tecla foi pressionada
                menu = key; //atualiza o menu
              }
            }
          }

          switch(menu){//mostra no LCD o menu pressionado
            case 4: //SELECT
                lcd.cursorTo(1,0); //move o cursor do LCD para linha 1 coluna 0
                lcd.printIn("webtronico.com"); //escreve no LCD
                lcd.cursorTo(2,0); //LCD na linha 2 coluna 0
                lcd.printIn("@webtronicoBR"); //escreve
              break;
            case 3: //LEFT
               //lcd.clear();
                lcd.cursorTo(1,0);
                lcd.printIn("Lat:");
                lcd.printIn(buscaDado(3)); //ler comentario na funcao buscaDado(byte)
                lcd.printIn("  ");
                lcd.printIn(buscaDado(4));
                lcd.cursorTo(2,0);
                lcd.printIn("Lon:");
                lcd.printIn(buscaDado(5));
                lcd.printIn(" ");
                lcd.printIn(buscaDado(6));
              break;
            case 1: //CIMA
              lcd.cursorTo(1,0);
              lcd.printIn("Data:");
              lcd.printIn(buscaDado(9));
              lcd.cursorTo(2,0);
              lcd.printIn("Hora:");
              lcd.printIn(buscaDado(1));
              break;
            case 2: //BAIXO
              lcd.cursorTo(1,0);
              lcd.printIn("Veloc:");
              lcd.printIn(buscaDado(7));
              lcd.cursorTo(2,0);
              lcd.printIn("knots");
              break;

          }

        }

        conta = 0; //zera conta, ou seja, vai iniciar proxima linha do GPS e dados[conta] esta na posição 0
        limpaBuffer(); //limpa dados[]
      }else{
        conta++; //incrementa conta, ou seja, dados[conta] pula para a proxima posição
      }

    }
}
void limpaBuffer(){
  for (byte i=0;i<DADOS_LEN;i++){       // limpa variavel (buffer) que recebe dados do GPS
    dados[i]=' ';
  }
}
void limpaTemp(){
  for(byte i=0;i<TEMPLEN;i++)
    tempmsg[i]=' ';
}
char* buscaDado(byte inicio){
  /*
  Funcao que busca na variavel dados[] o valor procurado do GPS.
  Lembrando que: 2-timestamp (UTC), 3-latitude, 4-Norte/Sul, 5-Longitude, 6-Leste(E)/Oeste(W),7-velocidade (em nós),9-date stamp
  */
  limpaTemp();
  byte i;
  byte fim = indice[inicio+1]-2;
  inicio = indice[inicio];
  for(i=0;i<=(fim-inicio);i++){
    tempmsg[i] = dados[inicio+i];
  }
  tempmsg[i] = '\0';
  return tempmsg;
}
// Convert ADC value to key number
byte get_key(unsigned int input)
{
  int k=menu;
  for (byte i = 0; i < NUM_KEYS; i++){
    if (input < adc_key_val[i]){
      k=i;
      return k;
    }
  }
  return k;

}

24 Comments

  • By haggerty classic insurance, October 17, 2010 @ 4:15 am

    I am glad you said that :)

  • By Leonardo, October 24, 2010 @ 7:30 pm

    Muito legal! =)

  • By Leonardo, February 11, 2011 @ 1:27 pm

    Gostaria de saber para que serveo pino rx do modulo gps?

  • By Juliano, February 11, 2011 @ 4:01 pm

    O RX do módulo não é conectado. (existe para configuração de fabrica)

  • By Everton, June 17, 2011 @ 8:23 pm

    Olá Qual é a precisão do modulo gps??? É possivel usar em aplicações que precise de alta precisão?
    Obrigado

  • By Pedro, August 20, 2011 @ 3:53 pm

    Estou precisando controlar um robô (aparador de grama) por GPS usando minha Arduino e o receptor SKM53, porém estou encontrando dificuldades ao programar… a ideia é definir 4 pontos ou seja delimitar a área que será varrida, bem, através de um comando por botão… exemplo: levo o robô até o primeiro ponto e pressiono o botão, ele emiti um sinal sonoro, lê a coordenada atual do GPS e armazena na arduino, o mesmo ocorre com os outros 3 pontos, a partir disso libero o robô em qualquer lugar ele se localiza e se move até o ponto inicial (como referenciar o robo? como mover o robo sem que ele sai da trajetória? e se sair como corrigi-la?, é possivel trabalhar com latitude e longitude, seria possíveis identificar a posição do robo em graus para definir a frente do robo?), além disso ele terá um sensor infra para localizar objetos a frente (como criar esta interrupção?)… como podem ver estou precisando de muita ajuda!!! qualquer coisa já ajuda!!! desde já obrigado…

  • By Juliano, August 21, 2011 @ 11:41 pm

    Pedro, pelo que entendi você está com dificuldades em trabalhar com posicionamento georeferenciado. Procure entender melhor sobre LATITUDE e LONGITUDE, pois com cálculos básicos você consegue calcular distância entre um ponto e outro, converter em graus…etc. Abraço

  • By Bishop, November 2, 2011 @ 10:52 am

    Boas,
    Desde já excelente trabalho.
    Gostaria de saber se é possivel ter as coordenadas em UTM?
    Abraço

  • By admin, November 3, 2011 @ 9:21 am

    Antonio, para converter Lat/Long para UTM basta fazer alguns calculos. Segue uma página para referencia: http://www.uwgb.edu/dutchs/usefuldata/utmformulas.htm

  • By GordoGeek, December 5, 2011 @ 10:32 am

    Como faz pra ligar o Arduíno Uno com o módulo de GPS, sem utilizar o LCD Shield, apenas lendo a serial? Que pinos devem ser ligados? Obrigado.

  • By admin, December 5, 2011 @ 11:24 am

    GordoGeek, basta remover o LCD SHIELD e seguir a ligação apenas para o GPS. Como o Arduino tem apenas uma serial, você vai ter que “emular” uma segunda serial via programação para ligar o GPS. Existe uma biblioteca pronta para o Arduino que faz isso: NewSoftSerial:http://arduiniana.org/libraries/newsoftserial/

  • By baruc1967, December 29, 2011 @ 10:30 pm

    bem
    se vc tem a área mapeada(matriz)do seu gramado
    dicamos 0,0 = xx lat por yy long a 99,99 = xx1 lat por yy1 long
    por enquanto esquece obstáculos
    vc só precisa comparar a posição atual do artefato
    digamos 50,50
    com a coordenada que vc quer que ele atue
    esteja onde ele estiver(posição atual)
    quando vc definir vá para a posição zero, zero por exemplo
    seja lá qual forem as coordenadas
    sua lógica precisa calcular a diferença entre (posição atual)
    e sua posição definida, (set point)
    e a diferença vai ser exatamente a direção para o robô seguir
    ou seja vc terá dois pedidos para o robô
    vá 50 para sul e 50 para oeste
    e ele chegará ao 0,0
    não sei se ficou claro qualquer coisa manda um alô

  • By Roberto, March 23, 2012 @ 7:07 pm

    Olá, Muito bom o projeto e a descrição, porém não consigo carregar o programa no arduino. Descompactei a biblioteca na pasta libraries do arduino, mas quando tento carregar vem várias mensagens de erro de compilação, todas relacionadas com a biblioteca do lcd, algumas C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:29:57: error: WConstants.h: No such file or directory
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp: In member function ‘void LCD4Bit_mod::pulseEnablePin()’:
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:58: error: ‘LOW’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:58: error: ‘digitalWrite’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:59: error: ‘delayMicroseconds’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:61: error: ‘HIGH’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:64: error: ‘delay’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp: In member function ‘void LCD4Bit_mod::pushNibble(int)’:
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:73: error: ‘digitalWrite’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp: In member function ‘void LCD4Bit_mod::commandWriteNibble(int)’:
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:99: error: ‘LOW’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:99: error: ‘digitalWrite’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp: In member function ‘void LCD4Bit_mod::commandWrite(int)’:
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:106: error: ‘LOW’ was not declared in this scope
    C:\arduino-1.0\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:106: error: ‘digitalWrite’ was not declared in this scope. Dá pra me auxiliar ? Sou novo no mundo do arduino.

  • By Rodrigo, April 2, 2012 @ 8:17 pm

    E aí Juliano!Blza?!

    Mesmo erro do primeiro post…By Roberto, March 23, 2012

    Descompactei a biblioteca na pasta libraries do arduino, mas quando tento carregar vem várias mensagens de erro de compilação, todas relacionadas com a biblioteca do lcd,

  • By Ediley, October 12, 2012 @ 3:34 am

    Amigos, estava com os mesmos problemas de erros. Para o Arduino UNO R3.
    consegui resolver usando a versão 0023 (alpha) e a ai compilou sem erro. Valeu?

  • By Rodrigo, March 13, 2013 @ 3:27 am

    Blza Juliano?!

    Funcionou direitinho…
    É muito complicado implementar o código para que o padrão apresentado seja em coordenadas que o Google entenda e não em NMEA?

  • By admin, March 13, 2013 @ 10:39 am

    Rodrigo, na verdade o protocolo do GPS é NMEA. Mas com ele você tem acesso a LATITUDE, LONGITUDE e etc… Ou seja, são coordenadas que o Google entende. Você pode entrar no Google Maps, digitar uma latitude e longitude que ele te aponta o local.

  • By Ana Paula, April 1, 2013 @ 9:04 pm

    Olá estou com um projeto parecido, não consegui compilar esse código, fica dando erro com a biblioteca, mas ja instalei!! O que fazer?

  • By Amauri, April 9, 2013 @ 6:44 pm

    Ana Paula, tente baixar a IDE 0023 do Arduino, uma mais antiga.
    Acho que funcionará!
    Estou projetando algo muito parecido com isso.
    Muito obrigado pessoal!
    Abraços,
    Amauri

  • By NEY, January 4, 2014 @ 8:05 pm

    Prezado, muito bom o codigo. Só que preciso ainda implementar com os dados do GGA, onde vou extrair a altitude e o numero de satelites. Vi que vc descreveu uma rotina parcial para ela, mas não consegui extrair estes dados. Pode me dar uma dica? Vou deixar uma dica aqui. para que ja usa a serial do arduino, implemente outra serial da seguinte forma:
    acrescente as linhas:

    #include
    SoftwareSerial gps(2,3);

    Dentro do Void setup, inclua:
    gps.begin(9600);

    Nas linhas abaixo, troque os valores de:
    while(Serial.available()){//Enquanto tiver caracteres no buffer
    dados[conta] = Serial.read();

    por:
    while(gps.available()){//Enquanto tiver caracteres no buffer
    dados[conta] = gps.read();

    Obrigado. Auardo um retorno. Ney

  • By NEY, January 4, 2014 @ 8:07 pm

    By NEY, January 4, 2014 @ 8:05 pm

    Prezado, muito bom o codigo. Só que preciso ainda implementar com os dados do GGA, onde vou extrair a altitude e o numero de satelites. Vi que vc descreveu uma rotina parcial para ela, mas não consegui extrair estes dados. Pode me dar uma dica? Vou deixar uma dica aqui. para quem ja usa a serial do arduino, implemente outra serial da seguinte forma:
    acrescente as linhas:

    #include SoftwareSerial gps(2,3);

    Dentro do Void setup, inclua:
    gps.begin(9600);

    Nas linhas abaixo, troque os valores de:
    while(Serial.available()){//Enquanto tiver caracteres no buffer
    dados[conta] = Serial.read();

    por:
    while(gps.available()){//Enquanto tiver caracteres no buffer
    dados[conta] = gps.read();

    Obrigado. Aguardo um retorno. Ney

  • By NEY, January 4, 2014 @ 8:09 pm

    Prezado, muito bom o codigo. Só que preciso ainda implementar com os dados do GGA, onde vou extrair a altitude e o numero de satelites. Vi que vc descreveu uma rotina parcial para ela, mas não consegui extrair estes dados. Pode me dar uma dica? Vou deixar uma dica aqui. para quem ja usa a serial do arduino, implemente outra serial da seguinte forma:
    acrescente as linhas:

    #include
    SoftwareSerial gps(2,3);

    Dentro do Void setup, inclua:
    gps.begin(9600);

    Nas linhas abaixo, troque os valores de:
    while(Serial.available()){//Enquanto tiver caracteres no buffer
    dados[conta] = Serial.read();

    por:
    while(gps.available()){//Enquanto tiver caracteres no buffer
    dados[conta] = gps.read();

    Obrigado. Aguardo um retorno. Ney

  • By anderson, February 15, 2015 @ 9:32 pm

    Estou com uma duvida. Tenho uma frota e compro rastreadores para controlar a mesma. Se eu coloca-se apenas esse modulo gps ( sem a tela lcd e sem o arduino) u conseguiria rastrear e saber quantos km o carro andou através de um site?

  • By andresa, July 27, 2016 @ 2:04 pm

    Eu gostaria de saber se posso mandar os dados que estão sendo mostrados no led para um computador ou celular?

Other Links to this Post

RSS feed for comments on this post. TrackBack URI

Leave a comment