Projeto Prático: Utilizando o Módulo GPS GY-NEO6MV2 com a Franzininho C0

Este post faz parte da série Franzininho C0 com STM32CubeIDE

Introdução

A integração de módulos GPS com microcontroladores é uma habilidade essencial para desenvolvedores de sistemas embarcados, permitindo a criação de projetos com funcionalidades avançadas de localização e navegação. Neste artigo, vamos explorar a utilização do módulo GPS GY-NEO6MV2 em conjunto com a placa Franzininho C0, que possui um microcontrolador baseado na série STM32C0.

Vamos iniciar com uma introdução ao funcionamento do GPS, explicando como ele utiliza uma rede de satélites para determinar a posição precisa na Terra. Em seguida, descreveremos as características e especificações técnicas do módulo GPS GY-NEO6MV2, que inclui uma antena integrada para facilitar sua implementação.

A seguir, abordaremos o protocolo NMEA, amplamente utilizado por receptores GPS para transmitir dados de posicionamento. Explicaremos como integrar o módulo GPS à Franzininho C0 utilizando a interface USART, incluindo a configuração do ambiente de desenvolvimento com STM32CubeMX e a geração de código para comunicação serial.

Por fim, detalharemos a recepção e processamento dos dados GPS, demonstrando como decodificar a sentença $GPGGA para extrair informações de latitude, longitude e altitude. Com este conhecimento, você estará preparado para implementar e expandir projetos de localização utilizando o módulo GPS GY-NEO6MV2 com a Franzininho C0.

Funcionamento GPS

O Sistema de Posicionamento Global (GPS) é composto por uma rede de mais de 30 satélites de navegação que orbitam a Terra. Esses satélites transmitem continuamente informações sobre suas posições e o horário atual para a Terra por meio de sinais de rádio.

Um receptor GPS capta esses sinais. Ao calcular a distância de pelo menos três satélites GPS, o receptor pode determinar sua localização precisa na Terra. Esse processo é conhecido como trilateração.

Módulo GPS GY-NEO6MV2

O NEO-6M é um módulo compacto e eficiente capaz de se comunicar com o sistema de posicionamento global (GPS). Este módulo pode fornecer informações precisas sobre localização, velocidade, horário (GMT) e outras métricas essenciais. Para mais detalhes técnicos, consulte o datasheet do NEO-6M.

O módulo GY-NEO6MV2, imagem abaixo, é uma versão baseada no NEO-6M, já incluindo uma antena integrada, o que facilita sua utilização em diversos projetos de localização e navegação.

Especificações do Módulo GPS GY-NEO6MV2

  • Tipo de Receptor: 50 canais, GPS L1 (1575,42 MHz)
  • Precisão da Posição Horizontal: 2,5 metros
  • Precisão da Velocidade: 0,1 m/s ou 0,36 km/h
  • Ângulo de Orientação: 0,5º
  • Taxa de Atualização de Navegação: 1Hz ~ 5Hz (novo dado entre 0,2 e 1 segundo)
  • Sensibilidade de Rastreamento/Navegação: -161dBm
  • Protocolo de Comunicação: Binário NMEA (padrão) / UBX
  • Taxa de Transmissão Serial: 4800, 9600 (padrão), 19200, 38400, 57600, 115200, 230400
  • Temperatura de Operação: -40°C ~ 85°C
  • Tensão Operacional: 2,7V ~ 5,0V (O NEO-6M em si trabalha com até 3,3V, mas o GY-NEO6MV2 possui um regulador de tensão para 3,3V, então é possível alimentá-lo com 5V)
  • Corrente Operacional: 45mA

Pinout GY-NEO6MV2

NMEA Protocol

NMEA é um acrônimo para National Marine Electronics Association. Este é um formato de mensagem padrão para quase todos os receptores GPS, inclusive para o GY-NEO6MV2

O padrão NMEA é formatado em linhas de dados chamadas sentenças. Cada frase possui campos de dados separados por vírgula para facilitar a análise por computadores e microcontroladores. 

As sentenças mais comuns são:

  • $GPRMC fornece hora, data, latitude, longitude, altitude e velocidade estimada.
  • $ GPGGA fornece dados de correção essenciais de localização 3D e com precisão.

Para mais informações deste protocolo acesse: Especificação do protocolo 

Integrando o GPS com a Franzininho C0

Neste exemplo simples, vamos configurar a Franzininho C0 para receber dados do módulo GPS GY-NEO6MV2 pela interface USART e mostrá-los na Serial. Além disso, vamos usar como base a biblioteca stm32-nmea-gps-hal para criar nosso próprio interpretador de dados GPS, focando na sentença GPGGA.

Circuito

Lembre-se que a conexão para comunicação USART é cruzada, dessa forma, TX conecta em RX e RX em TX. Logo, durante o código, o PA2 é configurado como TX e o PA3 como RX. 

Configuração do CubeMX

  1. Abra STM32Cube , crie um novo projeto e selecione o microcontrolador de destino “STM32C011F6P6”. 
  1. Vá para a página de configurações de relógio e em HCLK digite 48 MHz para a frequência de saída desejada do sistema. Pressione a tecla “Enter”, depois volte para a página de configuração dos pinos, selecione “Trace and Debug” e  habilite “Serial Wire.
  1. Em System Core > Sys habilite Pin PA9 e Pin PA10.
  1. Vamos utilizar os pinos PA9 e PA10 para exibir as informações na Serial. Logo, selecione os pinos PA9 e PA10, respectivamente como USART1_TX e USART1_RX. Depois, em Connectivity > USART1 habilite o modo assíncrono e mantenha as demais configurações.  
  1. Para receber os dados do GPS vamos configurar os pinos PA2 e PA3, respectivamente como USART2_TX e USART2_RX. Depois, em Connectivity > USART2 habilite o modo assíncrono e mude o Baund Rate para 9600, as demais configurações mantenha. 
  1. Em seguida, na USART2, clique em NVIC Settings e habilite a interrupção. 
  1. Por fim, gere o código clicando em “Project” > “Generate Code”.

Código para Comunicação com o GEO6M

  1. Abra o arquivo em Core > Src > main.c.
  1. Adicione as seguintes bibliotecas:
#include "string.h"
#include "stdio.h"
#include "stdint.h"
  1. Defina a seguintes constantes e variáveis globais:
#define BUFFER_SIZE 256
uint8_t bufferRecepcao[BUFFER_SIZE]; // Buffer para armazenar dados recebidos
uint8_t dadoRecebido; // Variável para armazenar um único byte recebido
uint16_t indiceBuffer = 0; // �?ndice para o buffer de recepção
char bufferTransmissao[BUFFER_SIZE]; // Buffer para armazenar dados a serem transmitidos
typedef struct {
   uint8_t latitudeInteira; // Parte inteira da latitude
   uint8_t latitudeDecimal; // Parte decimal da latitude
   uint8_t longitudeInteira; // Parte inteira da longitude
   uint8_t longitudeDecimal; // Parte decimal da longitude
   char altitude[8]; // Altitude (tamanho ajustado para suportar a string de altitude)
} DadosGPS;
  • BUFFER_SIZE: define o tamanho dos buffers de recepção e transmissão. O valor 256 é um tamanho arbitrário escolhido para garantir que temos espaço suficiente.
  • bufferRecepcao: é um array de bytes (uint8_t) que armazena os dados recebidos
  • dadoRecebido: é uma variável que armazena temporariamente um único byte recebido antes de ser processado e armazenado no bufferRecepcao.
  • indiceBuffer: é um índice que nos ajuda a rastrear a posição atual dentro do bufferRecepcao onde o próximo byte recebido será armazenado.
  • bufferTransmissao: é um array de caracteres (char) utilizado para armazenar os dados que serão enviados. 
  • DadosGPS: é uma estrutura (struct) que armazenará dados de GPS. 
  1. Nesse projeto, para recepção de dados do GPS via USART utilizaremos o método de interrupção, portanto, você precisa configurar a função de recepção assíncrona HAL_UART_Receive_IT na UART2 no main do seu código. 
int main(void)
{
 HAL_Init();
 SystemClock_Config();
 MX_GPIO_Init();
 MX_USART1_UART_Init();
 MX_USART2_UART_Init();
 /* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &dadoRecebido, 1);
 /* USER CODE END 2 */
 while (1)
 {
  
 }
 }
  1. Adicione a função de callback no seu código. Nessa etapa, esta função tratará a recepção de dados via UART2 e transmitirá os dados recebidos via UART1 após o recebimento de uma linha completa.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
   if (huart->Instance == USART2) {
       if (indiceBuffer < BUFFER_SIZE - 1) {
           if (dadoRecebido != '\n') { // Verifica fim de linha
               bufferRecepcao[indiceBuffer++] = dadoRecebido;
           } else {
               bufferRecepcao[indiceBuffer++] = '\r'; // Adiciona retorno de carro
               bufferRecepcao[indiceBuffer++] = '\n'; // Adiciona nova linha
               bufferRecepcao[indiceBuffer] = '\0';
               indiceBuffer = 0; // Reseta o índice do buffer
               HAL_UART_Transmit(&huart1, bufferRecepcao, strlen((char*)bufferRecepcao), 1000); // Transmite a linha completa via UART
           }
       }
       // Recomeça a recepção de dados
       HAL_UART_Receive_IT(&huart2, &dadoRecebido, 1); // Substitua pelo seu handler
   }
}
  1. Realize a gravação do código, aguarde até que o GPS indique que encontrou satélites. Quando o LED estiver piscando, conecte um cabo USB e abra o monitor serial para verificar os dados recebidos pelo GPS.

Após a recepção dos dados GPS, precisamos tratá-los. Conforme explicado durante este artigo, o módulo GPS GY-NEO6MV2 utiliza o protocolo NMEA, que é formatado em linhas de dados chamadas de sentenças. Cada sentença NMEA é composta por diferentes campos de dados separados por vírgulas. Vamos fazer a decodificação da sentença $GPGGA, que já foi explicada anteriormente no artigo. Para realizar essa decodificação, adicionaremos algumas funções ao código.

  1. Função analisarGPGGA: responsável por analisar a sentença $GPGGA do GPS e preencher uma estrutura de dados com as informações extraídas. Aqui está o código da função:
/* Função para analisar a sentença GPGGA do GPS e preencher a estrutura de dados */
void analisarGPGGA(char* sentenca, DadosGPS* dados) {
   char* token;
   int indiceCampo = 0;
   token = strtok(sentenca, ",");
   while (token != NULL) {
       switch (indiceCampo) {
           case 2:
               // Convertendo latitude para graus decimais
               dados->latitudeInteira = atoi(token) / 100;
               dados->latitudeDecimal = atoi(token) % 100;
               break;
           case 4:
               // Convertendo longitude para graus decimais
               dados->longitudeInteira = atoi(token) / 100;
               dados->longitudeDecimal = atoi(token) % 100;
               break;
           case 9:
               strncpy(dados->altitude, token, sizeof(dados->altitude) - 1);
               dados->altitude[sizeof(dados->altitude) - 1] = '\0'; // Assegura terminação nula
               break;
       }
       token = strtok(NULL, ",");
       indiceCampo++;
   }
}
  • token: utilizamos a função strtok para dividir a sentença NMEA em campos separados por vírgulas.
  • indiceCampo: um contador para identificar a posição de cada campo na sentença.
  • switch case: para cada índice de campo relevante (2 para latitude, 4 para longitude, 9 para altitude), processamos os dados conforme necessário:
    • Campo 2 (latitude): convertemos a latitude para um formato de graus decimais.
    • Campo 4 (longitude): convertemos a longitude para um formato de graus decimais.
    • Campo 9 (altitude): copiamos a altitude diretamente para a estrutura de dados.
  1. Função processarDadosGPS: verifica se a sentença recebida é do tipo $GPGGA e, se for, chama a função analisarGPGGA para processar os dados e transmitir via USART 1.
/* Função para processar os dados do GPS */
void processarDadosGPS(char* dadosGPS) {
   if (strncmp(dadosGPS, "$GPGGA", 6) == 0) {
       DadosGPS dados;
       analisarGPGGA(dadosGPS, &dados);
       // Formata os dados para o buffer de transmissão
       sprintf(bufferTransmissao, "Latitude: %d.%d, Longitude: %d.%d, Altitude: %s\r\n",
                dados.latitudeInteira, dados.latitudeDecimal, dados.longitudeInteira, dados.longitudeDecimal, dados.altitude);
       // Transmite os dados formatados via UART
       HAL_UART_Transmit(&huart1, (uint8_t *)bufferTransmissao, strlen(bufferTransmissao), 1000);
   }
}
  • Verificação da sentença: utilizamos strncmp para verificar se a sentença recebida começa com $GPGGA.
  • Chamada da função analisarGPGGA: se a sentença for do tipo $GPGGA, chamamos a função analisarGPGGA para decodificar os dados.
  • Formatação dos dados: formatamos os dados decodificados em uma string adequada para transmissão.
  • Transmissão via UART 1: utilizamos HAL_UART_Transmit para enviar os dados formatados através da interface UART.
  1. Por fim, voltemos à nossa função callback e modifique-a, chame a função para processar os dados GPS. 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
   if (huart->Instance == USART2) {
       if (indiceBuffer < BUFFER_SIZE - 1) {
           if (dadoRecebido != '\n') { // Verifica fim de linha
               bufferRecepcao[indiceBuffer++] = dadoRecebido;
           } else {
               bufferRecepcao[indiceBuffer] = '\0';
               indiceBuffer = 0; // Reseta o índice do buffer
               processarDadosGPS((char*)bufferRecepcao);
           }
       }
       // Recomeça a recepção de dados
       HAL_UART_Receive_IT(&huart2, &dadoRecebido, 1); // Substitua pelo seu handler
   }
}
  1. Realize a gravação do código utilizando ST-LINK ou a USB/Serial via STM32CubeProgrammer como explicado no seguinte tutorial: gravar-franzininho-c0-via-stm32cubeprogrammer 

Funcionamento

Abra novamente o monitor serial e o esperado é a exibição da latitude, longitude e altitude como na imagem abaixo. 

Desafio

Que tal tentar implementar a decodificação para outras sentenças do protocolo NMEA? 

Conclusão

Neste artigo, foi explicado como utilizar o módulo GPS GY-NEO6MV2 com a Franzininho C0 para obter dados de localização precisos. Aprendemos sobre o protocolo NMEA e a estrutura das sentenças que ele utiliza, focando na sentença $GPGGA. Implementamos funções em C para decodificar essas sentenças e extrair informações como latitude, longitude e altitude. Com esse conhecimento, você pode integrar dados GPS em diversas aplicações que usam o GPS como navegação, rastreamento de veículos, monitoramento de condições ambientais e demais outros projetos que você pensar.

Franzininho C0 com STM32CubeIDE

Entendendo o Controle PWM e Aplicando no Franzininho C0 Projeto Iluminação Automatizada com LDR e Franzininho C0
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
Arthur Hernandez
Arthur Hernandez
18/06/2024 19:32

Trabalhei num projeto com esse módulo de GPS porém na minha opinião era muito sensível, qualquer mínima mudança no posicionamento(mover 1cm com o dedo), já perdia conexão com o mesmo. Alguma sugestão para evitar esse tipo de problema?

Home » Software » Firmware » Projeto Prático: Utilizando o Módulo GPS GY-NEO6MV2 com a Franzininho C0

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: