Rastreador GPS Veicular Confiável com Integração em Mapa Thinger.io em Tempo Real
Componentes e Materiais
| Arduino Leonardo | Usado para ter serial de hardware dedicada. | 1 |
| Placa de Desenvolvimento SIM900 | 1 | |
| Módulo GPS u-blox NEO-6M | 1 | |
| Antena GPS | 1 | |
| Resistor 220 Ω | 2 | |
| LED (Genérico) | 2 | |
| Placa de Solda 7x5cm | Cabe na caixa 3D para LEDs de status. Pode usar protoboard genérica ou pular se não quiser LEDs. | 1 |
| Módulo Step-Down 12V para 5V | Para alimentar pela bateria do carro (ou tomada 12V/cigarro) | 1 |
| Leitor de Cartão SD Adafruit | 1 | |
| Cartão MicroSD | 1 |
Ferramentas e Máquinas Necessárias
| Ferro de Solda (Genérico) |
Aplicativos e Serviços Online
| Plataforma Thinger.io | Serviço IoT gratuito com Dashboards! |
Sobre este Projeto
Começou como uma simples "ideia de rastreador GPS" e virou um "rastreador GPS multipropósito". Como meu primeiro projeto, a curva de aprendizado foi íngreme, e estou sempre aberto a sugestões, feedback e melhorias no design! :)
O rastreador fica no carro e tem as seguintes funções:
- Rastreia coordenadas GPS e envia última localização conhecida ao dashboard IoT Thinger.io a cada 2 minutos (exibida em mapa). Envio via requisições HTTP POST.
- Responde a comandos SMS e retorna link do Google Maps para localização atual ou última conhecida (se não houver fix atual).
- Envia notificação SMS a cada XX quilômetros (ideia: lembrar de esvaziar o tanque de óleo do cárter a cada 4000 km). Funciona como "odômetro" de software personalizável.
Durante o projeto percebi rapidamente como o Arduino é "limitado" em memória disponível e tive que aprender técnicas para reduzir overhead e fazer código eficiente. Usei bibliotecas leves para caber tudo no chip e RAM disponível.
Componentes Detalhados
- NEO-6M GPS: Popular, barato no eBay. Comunicação via serial de hardware.
- Antena GPS: Qualquer compatível serve, mas as mais baratas do eBay tiveram má recepção/baixa contagem de satélites. Tive que comprar uma de melhor qualidade para recepção estável.
- SIM900: Para conectividade GSM/GPRS. Também deve funcionar com SIM800 e compatíveis (sem garantias). Comunicação via serial de software.
- Arduino Leonardo: Escolhido para ter serial de hardware dedicada, pois precisamos de duas linhas seriais. Uno também funcionaria, mas teria que desconectar GPS para upload e não teria monitor serial para debug. Precisamos de duas seriais: uma hardware (GPS) e uma software (SIM900).
- Leitor SD (Adafruit): Compatível com 5V (facilitou conexão ao header SPI 5V). Outros módulos mais baratos podem funcionar. Atenção: certifique-se que seu leitor suporta 5V se seguir meus esquemáticos; muitos usam só 3.3V. Tensão errada danifica eletrônicos. Comunicação via SPI.
- LEDs e resistores para indicadores de status (alimentação e trava GPS).
- Cartão SIM com dados.
- Caixa 3D imprimível com arquivos STL anexos.
Bibliotecas Necessárias
- NeoGPS para rastreamento/decodificação GPS. Instale pelo Library Manager da Arduino IDE. Mais info: https://github.com/SlashDevin/NeoGPS — consome pouca RAM, essencial para estabilidade.
- Time (conversão fuso horário UTC): https://github.com/PaulStoffregen/Time
- PetitFS para leitura/escrita no cartão SD: https://github.com/greiman/PetitFS — biblioteca FAT super leve.
Por Que Não Usar a Biblioteca Oficial do Thinger.io?
Embora a biblioteca do thinger.io seja fácil e já integrada à IDE, consome ~80% do armazenamento do Leonardo, sobrando pouco ou nenhum espaço para o resto do código. É grande demais para este projeto; faremos do "jeito difícil" com requisições HTTP POST.
Comandos SMS
Comandos disponíveis (tudo maiúsculas):
- "POS" — retorna coordenadas com link Google Maps; se não houver fix, retorna última localização conhecida.
- "GETKM" — retorna distância acumulada desde último "reset".
- "RESETKM" — zera contador de distância (reset odômetro).

Configurando NeoGPS
Usamos NeoGPS por performance e uso de recursos sobre alternativas como TinyGPS++. Consome pouca RAM, necessário para evitar avisos de memória baixa e instabilidade.
Após instalar, modifique GPSPort.h no caminho de instalação da biblioteca (exemplo para macOS; no Windows o caminho é diferente).
Substitua todo conteúdo de GPSPort.h por:
#ifndef GPSport_h
#define GPSport_h
#define gpsPort Serial1
#define GPS_PORT_NAME "Serial1"
#define DEBUG_PORT Serial
#endif


Este arquivo define a linha serial para o receptor GPS. Se usar outra placa (ex: Mega), defina "Serial2", "Serial3" aqui.
Notas sobre Precisão
GPS não é o método mais preciso para medir/acumular distância, pois a posição deriva levemente mesmo parado. Teste: fique parado e observe que coordenadas mudam a cada leitura. Para esta aplicação, precisão menor é aceitável.
Tentei contabilizar pequenas derivações: o software só soma distância para movimentos >10m (abaixo assume-se parado) em 15 segundos. Lembre-se: distância é calculada em linha reta; a real depende de curvas, estradas. Taxa de amostragem: 15 segundos (pode diminuir para mais precisão).
Configurando PetitFS
Biblioteca super leve para leitura/escrita em cartões SD com FAT. Documentação praticamente inexistente e em partes errada/desatualizada. O exemplo não compila. Vem com muitas restrições:
- Não cria arquivos. Só escreve em arquivo existente.
- Não expande tamanho do arquivo.
- Não atualiza timestamp.
- Não faz append (reescreve arquivo todo).
- Só um arquivo aberto por vez.
Por que usar biblioteca tão limitada? Tamanho. Testei Arduino SD Library, SDFat, fat16lib — todas grandes demais. A SD padrão consome ~12% mais espaço. Mesmo com limitações, atende nossa necessidade: leitura/escrita simples de um valor único.


Em pffArduino.h, mude SD_CS_PIN para 10 (pino SS para SPI com cartão SD).


Em pffconf.h, desative (mude 1 para 0): _USE_DIR, _USE_LSEEK, _FS_FAT12, _FS_FAT16. Isso reduz o tamanho do compilado — o sketch final usa ~96% do armazenamento.


Na primeira importação, pode dar erro de compilação que some na segunda compilação. Para corrigir definitivamente, adicione o tipo de retorno "FRESULT" na função faltante em pff.cpp (conforme screenshot).
Fiz meu melhor para entender a biblioteca; embora tudo funcione, melhorias são bem-vindas! Compartilhe se encontrar erros ou melhorias.
Preparando o Cartão SD
Usei Micro SD. Como a biblioteca não cria arquivos, crie "dist.txt" e "settings.txt" no cartão antes de usar. Recomendo copiar os arquivos anexos "dist.txt" e "settings.txt" desta página, pois já têm formato correto e funcionam (a biblioteca é muito exigente com formato e conteúdo de texto).
Formate o cartão como FAT32 antes. Recomendo o "SD Card Formatter" oficial da SD Association: https://www.sdcard.org/downloads/formatter/.
Garantindo que o Cartão SD Funciona (Leitura/Escrita Correta)
PetitFS é muito exigente com arquivos de entrada. Se ao ligar o dispositivo nada aparece no monitor serial (em branco), provavelmente travou no "loop" tentando ler o arquivo mas não consegue (função initializeSD()). Tive inúmeros arquivos de texto que por algum motivo não lia; por isso incluí os arquivos de referência que funcionam. Coloque estes arquivos de referência no cartão SD e deve ler/escrever corretamente.


PetitFS escreve todo o comprimento do array de caracteres no arquivo, então verá espaços vazios antes do número real (a menos que o número preencha o array — o comprimento é definido no código). Esses espaços devem ser mantidos ao editar o arquivo — como PetitFS não muda tamanho do arquivo, alterar número de caracteres causa problemas.
Defina "dist.txt" como "0" para odômetro começar em "0", ou outro número para testar facilmente (ex: envie comando "GETKM" para verificar resposta SMS).
Em "settings.txt" defina a distância de gatilho da notificação SMS (em metros).
Preparando a Placa SIM900
Algumas configurações na placa SIM900 antes de usar. Detalhes em https://lastminuteengineers.com/sim900-gsm-shield-arduino-tutorial/.




Fonte de Alimentação
O Arduino não fornece potência suficiente; use fonte externa. Picos chegam a 2A — use fonte que entregue pelo menos 2A em 5V-9V DC; usa conector barril 5.5mm.
Seletor de Fonte
Ao lado do jack DC há um seletor de fonte. Para fonte externa, mova o slider como na foto.
Seletor Serial
Configure a placa para usar Software Serial alinhando os jumpers como na foto.
Trigger por Software
Em vez de pressionar botão power manualmente, pode ligar/desligar SIM900 via software. Para isso, o jumper R13 deve ser soldado. A placa liga conectando pino #9 do SIM900 ao pino #7 do Arduino (conforme esquemático). Se mantiver funcionalidade "power manual", remova a função "SIM900power()" do código.
Remova Bloqueio PIN do Chip SIM
Remova o PIN do chip SIM antes de usar. Insira num celular comum e remova o bloqueio PIN no menu de configurações.
Nota: o módulo SIM900 no esquemático pode parecer diferente da placa real, mas o layout de pinos está correto.
Versão do Firmware SIM900 (Importante!)
É muito importante ter firmware versão B10 ou superior. Um dos comandos para configurar corretamente o header HTTP POST não é suportado antes da versão B10. Significa que precisa de pelo menos B10 para comunicação HTTP funcionar. Especificamente, com firmware antigo não consegue definir "Content-type" no header HTTP. Se não for "application/json", o servidor rejeita.
Para verificar versão, use comando AT:
AT+CGMR
O chip retornará a versão no console. Adicione ao final do setup() para imprimir na inicialização:
SIM900.println( F("AT+CGMR") );
No meu caso mostrava (antes de atualizar):
Revision:1137B01SIM900M64_ST_AM
Era o firmware mais antigo possível ("B01"). Atualizei para B10: 1137B10SIM900M64_ST. Versões mais novas também funcionam.
Não cobrirei como atualizar firmware aqui; há excelente guia: "SIM900 firmware update - ACOPTEX" (processo um pouco doloroso). Não sei se isso ocorre com outros chips como SIM800, mas sendo mais novo, provavelmente já vem com isso.
Ajustes no Código
Para adaptar ao seu projeto, alguns ajustes:
- Alterar APN (provedora de rede).
- Modificar URL do thinger.io para a sua (vincula requisição ao seu "bucket" com token de acesso). Coberto no capítulo "Integração Thinger.io".
- Definir fuso horário correto.
- Definir distância de gatilho para notificação SMS.
- Definir (ou desabilitar) texto da notificação SMS.
- Definir número padrão para notificação.
APN da Provedora
void connectGPRS(){ ...
SIM900.println( F("AT+SAPBR=3,1,\"APN\",\"TeleXXX\"") );
delay(1000); updateSIM900(); ...
Na função connectGPRS() está o nome APN da sua operadora, mostrado acima como "TeleXXX". Substitua pelo seu.
AT OK
AT+CMGF=1 OK
AT+CNMI=1,2,0,0,0 OK
AT+SAPBR=3,1,"CONTYPE","GPRS" OK
AT+SAPBR=3,1,"APN","TeleXXX" OK
AT+SAPBR=1,1 OK
AT+SAPBR=2,1 +SAPBR: 1,1,"36.57.240.233" OK
Acima: saída do connectGPRS() quando conexão funciona. Todos comandos devem retornar "OK".
Fuso Horário
#define UTC_OFFSET 1 // offset fuso horário, ex: 1 = UTC+1
Na seção #define, defina conforme sua necessidade. Código está em UTC+1.
Notificação SMS
Configurei notificação a cada 4000km para esvaziar tanque de óleo. Como a maioria não tem tanque de óleo, altere para o que quiser (ou desative).
void loop() {
...
if (totalDistance > triggerDistance) {
char sms_msg[160]; char distanceTotalMsg[10];
itoa( (totalDistance / 1000) , distanceTotalMsg, 10);
sprintf(sms_msg, "Esvazie tanque! Distancia atual: %skm", distanceTotalMsg);
textMessage = ""; totalDistance = 0; number = DEFAULT_NUMBER; sendSMS(sms_msg);
}
...
}
Comente/remova se não quiser notificações. Ou altere o texto para algo útil.
Notificação dispara quando "odômetro" (distância acumulada) atinge distância configurada em "settings.txt".
Número Padrão
// numero para notificacao disparada
#define DEFAULT_NUMBER "+4712345678"
Aguardar Conexão Serial
Boa ideia descomentar a linha abaixo. Faz o Arduino aguardar conexão serial ativa (monitor serial para debug). Assim não perde mensagens de debug no início antes da serial ficar ativa.
Lembre-se de remover/comentar antes de alimentar de fonte externa, senão travará em loop infinito até conectar ao PC.
// while (!Serial); // aguarda porta serial - para ATmega32u4 (Leonardo)
Integração com Thinger.io
Não detalho setup do thinger.io — é direto: crie conta, um "bucket" para receber dados, e um "device" para conectar. "Bucket" = banco de dados. "Device" = ponto de conexão do nosso código onde decidimos o que fazer com dados (no nosso caso, popular o bucket).

Crie um "bucket" como acima com seu nome/descrição.
Crie um "HTTP Device" conforme documentação oficial: https://docs.thinger.io/quick-sart/devices/http-devices.



Use nome de device curto. O nome faz parte do algoritmo que gera a chave de autorização; nome longo gera chave longa. O problema? Chave passou de 256 caracteres do buffer usado para enviar string do Arduino. Solução simples: mantenha nome curto.
Na seção callback > settings do device, certifique-se que "write bucket" aponta para o bucket criado. Isso diz ao device para gravar dados recebidos no nosso banco.
Na seção callback > overview, anote a URL do método e o header de autorização (string longa sem a palavra "Bearer").
Para enviar dados ao thinger.io usamos "URL de autorização" no HTTP POST. Substitua URL e chave de autorização no código pelas suas.
Na função postDataThinger() está a chamada (chave de autorização embaralhada):
SIM900.println( F("AT+HTTPPARA=\"URL\",\"https://backend.thinger.io/v3/users/tom/devices/CT/callback/data?authorization=eyJhbGciOiJIUzI1NiIsInR5cdfkjowiuerdf.sdfsdf.wekrjciI6InRvcm1vZCJ9.AR1gWvIZB9KmtI-5Z12YXKuchPTGn58AkwBJSZQIoxQ\"") );
Substitua URL e chave de autorização no código pelas suas, geradas seguindo instruções no link acima.
https://backend.thinger.io/...
Por padrão, a URL de autorização gerada será https. O SIM900 NÃO suporta SSL (pelo menos não consegui fazer funcionar), então mude "https://" para "https://". A API thinger também suporta conexões não-SSL. Muito importante. Se mantiver "https", não funciona.
Quando tudo funciona, monitor serial deve dar "200 - OK" ao transmitir HTTP POST.
Após comando AT "AT+HTTPACTION=1" (enviar HTTP POST), deve receber resposta no monitor serial:
+HTTPACTION: 1,200,0
Se receber "400 - bad request" ou similar:
+HTTPACTION: 0,400,51
...provavelmente algo errado com URL (ex: "https" em vez de "http", sintaxe ruim da "chave de autorização", etc.). Com "200 - OK", dados aparecem no bucket thinger como abaixo.

Visão do bucket quando dados chegam (embaralhado por privacidade). Colunas de dados definidas pela sintaxe do HTTP POST no código; nenhum setup no thinger.io necessário.
Saída serial do HTTP POST quando tudo funciona. +HTTPACTION:1, 200, 0 indica atualização bem-sucedida.
AT+HTTPINIT OK
AT+HTTPPARA="CID",1 OK
AT+HTTPPARA="URL","..." OK
AT+HTTPPARA="CONTENT","application/json" OK
AT+HTTPDATA=84,10000 DOWNLOAD OK
AT+HTTPACTION=1 OK
+HTTPACTION:1,200,0
AT+HTTPTERM OK
O dashboard pode ser configurado no thinger com widget de mapas usando o bucket como fonte.

Mais Dados para Thinger?
Quer enviar mais que longitude, latitude e data/hora? Adicione mais campos no HTTP request:
Formato: { "field1 name" : field1, "field2 name" : field2, "field3 name" : field3 }
sprintf(httpContent, "{ \"longitude\" : %s , \"latitude\" : %s , \"date\" : \"%s %s\" }", tempstrLong, tempstrLat, date1, time1);
O sprintf acima compila a string de dados enviada ao thinger. Sintaxe é muito estrita; adicione novos campos exatamente da mesma forma. Exemplo no código (seção comentada). Boa ideia: anote a impressão do monitor serial do comando que mostra a string. Depois adicione "field4" e assim por diante.
Caixa (Enclosure)
Anexei caixa 3D imprimível completa. Projetada para caber os PCBs exatos usados. Parafusos M3 para montagem.






Projetada para placa de solda 7x5cm para o "circuito" de LEDs, não protoboard. Se usar protoboard, use cola. GPS e placa de solda ("protoboard") montam na tampa superior. Use espaçadores pequenos para melhor montagem dos PCBs na tampa superior.
Mantive pontos de montagem na tampa superior sólidos (sem furos) para impressão mais fácil sem suportes. Fure com broca 3mm após impressão.
Imprime bem em 0.2mm sem suportes.
Conexão à Bateria do Carro / Fonte de Alimentação
Provavelmente centenas de formas de fazer isso; não tenho a única ou melhor resposta. Depende da aplicação, mas descrevo minha solução rapidamente.
Queria o dispositivo ligando com o carro, não direto na bateria (consumindo energia com carro desligado). Conectei ao circuito da "tomada 12V/cigarro" que já liga/desliga com o carro. Se quer online mesmo com carro desligado, terá que ligar na bateria. Na maioria dos carros a tomada 12V desliga com o carro, mas verifique no seu. Não mostro fiação exata pois varia por carro. Pode também colocar power bank entre eles para manter dispositivo ligado por horas após carro desligado (ou roubado..).
Pode usar adaptador tipo carregador USB de celular, mas isso derrota meu propósito de escondê-lo (caso carro seja roubado). Temos duas fontes: Arduino e módulo SIM900. Usei módulo step-down "China cheap-o matic" que converte 12V→5V (faixa real 9V-20V). Provavelmente não é boa qualidade, mas funciona até agora :)
O step-down transforma 12V entrada em 5V saída para dois conectores USB fêmea. Conectei Arduino e módulo SIM900 a cada saída USB. Existem soluções mais "profissionais", mas esta foi barata e funcionou.
Meça consumo durante atividade GSM: ~110mAh, consumo bem baixo. Provavelmente consome mais em áreas com cobertura GSM ruim.




Problemas Conhecidos
Se comando SMS chega ao mesmo tempo que requisição HTTP thinger.io é processada (enquanto dados são enviados ao thinger), o comando não será captado pelo software. Não receberá resposta SMS. Envie novo comando alguns segundos depois e funcionará. Não fiz workaround pois não é grande problema. Se alguém fizer correção, compartilhe.
Também, se Arduino iniciar em área sem cobertura de rede, não reconecta quando rede fica disponível novamente, pois só conecta na inicialização. Pode modificar código futuramente para corrigir, mas por enquanto...
Código
GPS_tracker_Leonardo_v2.ino (Arduino)
#include <SoftwareSerial.h> #include <NMEAGPS.h> #include <GPSport.h> #include <TimeLib.h> #include "PF.h" #include "PetitSerial.h"
#define UTC_OFFSET 1 // offset fuso horário, ex: 1 = UTC+1
#define TXPin 8 // SIM900 Tx
#define RXPin 9 // SIM900 Rx
#define PWRPin 7 // SIM900 power por software
#define DEFAULT_NUMBER "+4712345678" // numero para notificacao
FATFS fs; // objeto filesystem - leitura cartao SD
// Variaveis GSM
String textMessage; String number = DEFAULT_NUMBER; char sms_msg[160];
// Variaveis localizacao
float Lat = 0, Long = 0; boolean valid_location = false; uint8_t num_sat;
NeoGPS::Location_t prevFix; NMEAGPS gps; gps_fix fix;
const char *googlePrefix = "https://maps.google.com/maps?q=";
const char *filename = "DIST.TXT"; const char *settings = "SETTINGS.TXT";
// Variaveis tempo
NeoGPS::time_t timeFix; char datePrint[13]; char timePrint[10];
// Rastreamento distancia
float totalDistance = 0; // em metros
float triggerDistance = 4000000; // lida do SD card no init
SoftwareSerial SIM900( TXPin, RXPin ); // SIM900 Tx/Rx nos pinos #8 e #9
void setup() {
pinMode(3, OUTPUT); pinMode(4, OUTPUT); digitalWrite(3, HIGH); // LED power ON
Serial.begin(9600);
// while (!Serial); // descomente so para debug com PC conectado
SIM900.begin(9600); gpsPort.begin(9600); // GPS no Serial1 pinos #0 e #1
initializeSD(); SIM900power();
SIM900.println( F("AT") ); delay(500);
SIM900.println( F("AT+CMGF=1") ); delay(500); // Modo texto
SIM900.println( F("AT+CNMI=1,2,0,0,0") ); delay(500); // Tratamento SMS
connectGPRS();
}
void loop() {
while (gps.available( gpsPort )) {
fix = gps.read(); num_sat = fix.satellites;
if (fix.valid.location) {
digitalWrite(4, HIGH); // LED GPS lock
Lat = fix.latitude(); Long = fix.longitude();
if (Lat != 0 && Long != 0 && !valid_location) {
valid_location = true; prevFix = fix.location;
}
}
if (fix.valid.date && fix.valid.time) {
timeFix = fix.dateTime; updateTime();
}
if (valid_location) {
// Atualiza distancia a cada 15s
static const unsigned long REFRESH_INTERVAL_UPD = 15000;
static unsigned long lastRefreshTime = millis();
if (millis() - lastRefreshTime >= REFRESH_INTERVAL_UPD) {
lastRefreshTime += REFRESH_INTERVAL_UPD;
float distanceDelta = prevFix.DistanceKm(fix.location) * 1000;
if (distanceDelta > 10 && distanceDelta < 10000) {
totalDistance += distanceDelta;
}
prevFix = fix.location;
}
// Grava no SD e envia ao thinger.io a cada 2 min
static const unsigned long REFRESH_INTERVAL_WRITE_SD = 120000UL;
static unsigned long lastRefreshTimeWriteSD = millis();
if (millis() - lastRefreshTimeWriteSD >= REFRESH_INTERVAL_WRITE_SD) {
lastRefreshTimeWriteSD += REFRESH_INTERVAL_WRITE_SD;
char buf[9]; dtostrf(totalDistance, 8, 0, buf);
if (PF.open(filename)) Serial.println( F("erro abrir arquivo") );
while (1) {
UINT nr;
if (PF.writeFile(buf, sizeof(buf), &nr)) Serial.println( F("erro escrever arquivo") );
if (nr == sizeof(buf)) { PF.writeFile(0, 0, &nr); break; }
}
// PetitFS nao tem "close"
// Envia dados ao thinger.io
char httpContent[60]; char tempstrLong[10]; char tempstrLat[10];
dtostrf(Lat, 2, 6, tempstrLat); dtostrf(Long, 2, 6, tempstrLong);
sprintf(httpContent, "{ \"longitude\": %s , \"latitude\": %s , \"date\": \"%s %s\" }", tempstrLong, tempstrLat, datePrint, timePrint);
char httpdataLen[20];
sprintf(httpdataLen, "AT+HTTPDATA=%i,10000", strlen(httpContent));
postDataThinger(httpdataLen, httpContent);
}
}
}
// Notificacao SMS se distancia excede limite
if (totalDistance > triggerDistance) {
char distanceTotalMsg[10];
itoa( (totalDistance / 1000) , distanceTotalMsg, 10);
sprintf(sms_msg, "Esvazie tanque! Distancia atual: %skm", distanceTotalMsg);
textMessage = ""; number = DEFAULT_NUMBER; totalDistance = 0; sendSMS(sms_msg);
}
updateSerial();
}
void updateSerial() {
if (SIM900.available() > 0) { textMessage = SIM900.readString(); }
if (textMessage.indexOf("POS") >= 0) {
extractSenderNumber(); textMessage = "";
char latPrint[10]; dtostrf(Lat, 5, 6, latPrint);
char LonPrint[10]; dtostrf(Long, 5, 6, LonPrint);
if (num_sat >= 3 && valid_location) {
sprintf(sms_msg, "Localizacao atual: Lat: %s, Long: %s. %s%s,+%s\n", latPrint, LonPrint, googlePrefix, latPrint, LonPrint);
} else if (num_sat < 3 && valid_location) {
sprintf(sms_msg, "Sem fix GPS. Vista por ultimo %s%sat: Lat: %s, Long: %s. %s%s,+%s\n", datePrint, timePrint, latPrint, LonPrint, googlePrefix, latPrint, LonPrint);
} else if (!valid_location) {
sprintf(sms_msg, "Nao encontrado. Talvez esteja na Coreia do Norte?");
}
sendSMS(sms_msg);
}
if (textMessage.indexOf("GETKM") >= 0 ) {
char sms_msg[32]; char distanceTotalMsg[10];
itoa( (totalDistance / 1000) , distanceTotalMsg, 10);
sprintf(sms_msg, "Distancia atual: %skm", distanceTotalMsg);
textMessage = ""; sendSMS(sms_msg);
}
if (textMessage.indexOf("RESETKM") >= 0) {
totalDistance = 0; char sms_msg[] = "Confirmado: distancia zerada";
textMessage = ""; sendSMS(sms_msg);
}
}
void SIM900power() {
pinMode(PWRPin, OUTPUT); digitalWrite(PWRPin, LOW); delay(1000);
digitalWrite(PWRPin, HIGH); delay(2000); digitalWrite(PWRPin, LOW); delay(15000);
}
void updateSIM900() { delay(500); while (SIM900.available()) { Serial.write(SIM900.read()); } }
void extractSenderNumber() {
uint8_t startPos = textMessage.indexOf("+", 6); uint8_t endPos = textMessage.indexOf(",");
number = textMessage.substring(startPos, endPos - 1);
}
void sendSMS(char *content) {
char numberChar[number.length() + 1]; number.toCharArray(numberChar, number.length() + 1);
char cmd_sms[50]; sprintf(cmd_sms, "AT+CMGS=%c%s%c", 0x22, numberChar, 0x22);
SIM900.println(cmd_sms); updateSIM900();
SIM900.print(content); updateSIM900(); SIM900.write(0x1A);
}
void connectGPRS() {
SIM900.println( F("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"") ); delay(1000); updateSIM900();
SIM900.println( F("AT+SAPBR=3,1,\"APN\",\"TeleXXX\"") ); delay(1000); updateSIM900();
SIM900.println( F("AT+SAPBR=1,1") ); delay(1000); updateSIM900();
SIM900.println( F("AT+SAPBR=2,1") ); delay(1000); updateSIM900();
}
void postDataThinger(char *httpDataLen, char* content) {
SIM900.println( F("AT+HTTPINIT") ); delay(1000); updateSIM900();
SIM900.println( F("AT+HTTPPARA=\"CID\",1") ); delay(1000); updateSIM900();
SIM900.println( F("AT+HTTPPARA=\"URL\",\"https://backend.thinger.io/v3/users/tom/devices/CT/callback/data?authorization=eyJhbGciOiJIUzI1NiIsInR5cCI6lskjdflksjdfweruiowe19DVCIsInVzciI6InRvcm1vZCJ9.AR1gWvIZB9KmtI-5Z12YXKuchPTGn58AkwBJSZQIoxQ\"") ); delay(1000); updateSIM900();
SIM900.println( F("AT+HTTPPARA=\"CONTENT\",\"application/json\"") ); delay(1000); updateSIM900();
SIM900.println(httpDataLen); delay(1000); updateSIM900();
SIM900.println(content); delay(1000); updateSIM900();
SIM900.println( F("AT+HTTPACTION=1") ); delay(10000); updateSIM900();
SIM900.println( F("AT+HTTPTERM") ); delay(1000); updateSIM900();
}
void initializeSD() {
char buf[10];
if (PF.begin(&fs)) Serial.println( F("erro iniciar arquivo") );
if (PF.open(filename)) Serial.println( F("erro abrir arquivo") );
while (1) { UINT nr; if (PF.readFile(buf, sizeof(buf), &nr)) Serial.println( F("erro ler arquivo") ); if (nr == sizeof(buf)) break; }
totalDistance = atof(buf);
char bufTrigger[10];
if (PF.open(settings)) Serial.println( F("erro abrir arquivo") );
while (1) { UINT nr; if (PF.readFile(bufTrigger, sizeof(bufTrigger), &nr)) Serial.println( F("erro ler arquivo") ); if (nr == sizeof(bufTrigger)) break; }
triggerDistance = atof(bufTrigger);
}
void updateTime() {
setTime(timeFix.hours, timeFix.minutes, timeFix.seconds, timeFix.date, timeFix.month, timeFix.year);
adjustTime(UTC_OFFSET * SECS_PER_HOUR);
sprintf(datePrint, "%02d/%02d/%04d ", day(), month(), year());
sprintf(timePrint, "%02d:%02d:%02d ", hour(), minute(), second());
}
Peças Personalizadas e Enclosures
- Tampa superior (mais espaço/alta). Furos de montagem fechados — furar após impressão (para impressão mais fácil).
- Versão compacta da tampa superior (menos espaço interno).
- Parte inferior — para SIM900 e Arduino com recortes para conectores.
- Trava placa SIM900 no lugar (sem furos de montagem na placa SIM900).
- Arquivo
dist.txt— renomeie e coloque no cartão SD. - Arquivo
settings.txt— renomeie e coloque no cartão SD.
Esquemáticos

Processo de manufatura
- Fabricação de Tábuas de Passar: Design, Materiais e Processo de Produção
- Construção e Design de Pranchas de Surf: Das Raízes Polinésias à Inovação Moderna
- Rastreamento de Ativos Externo com GPS: Tendências, Desafios e Tecnologias Emergentes
- Raspberry Pi Revela MCU RP2040 e Placa Pico de $4
- Integração de Dados de Sensores no Raspberry Pi: Guia Prático
- Dominando a Placa PSoC 6 Pioneer com Shield IoT Add-On: Guia do Desenvolvedor
- Acelere a Reutilização de Medicamentos para COVID-19 com BIOVIA Living Map
- Dominando a Função map() do Python: Sintaxe, Exemplos e Boas Práticas
- Aproveitando Sistemas de Integração Internos para Manufatura de Precisão
- Os Riscos de Dirigir com Nível Baixo de Líquido de Arrefecimento do Motor