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
- Abra STM32Cube , crie um novo projeto e selecione o microcontrolador de destino “STM32C011F6P6”.
- 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.
- Em System Core > Sys habilite Pin PA9 e Pin PA10.
- 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.
- 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.
- Em seguida, na USART2, clique em NVIC Settings e habilite a interrupção.
- Por fim, gere o código clicando em “Project” > “Generate Code”.
Código para Comunicação com o GEO6M
- Abra o arquivo em Core > Src > main.c.
- Adicione as seguintes bibliotecas:
#include "string.h"
#include "stdio.h"
#include "stdint.h"- 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.
- 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)
{
}
}
- 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
}
}
- 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.
- 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.
- 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.
- 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
}
}
- 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.







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?