Community project

Um Projeto De Mini Gps Com Tela Redonda

ESP32
Photo of Um Projeto De Mini Gps Com Tela Redonda
Generated with AI

wigorlancer

Last updated August 11, 2026

This project builds a portable GPS navigator with a round 1.28-inch display, perfect for tracking location and heading in real-time. The ESP32 microcontroller reads position data from a NEO-6M GPS module and renders an interactive map on the GC9A01 circular TFT screen, with audio alerts from the buzzer for proximity warnings.

The guide provides a complete wiring diagram showing how to connect the round display, GPS receiver, and buzzer to the ESP32, along with a full parts list and step-by-step assembly instructions. Firmware based on TinyGPS++ and Adafruit graphics libraries is included, featuring map rendering, heading display, and alert system integration via WiFi configuration.

Wiring diagram

Wiring diagram for Um Projeto De Mini Gps Com Tela Redonda

Gather all the parts

QtyComponent
1

GC9A01 Round TFT LCD 1.28 inch 240x240

1.28 in, 240×240

1.28 inch round IPS TFT LCD display module with GC9A01/GC9A01A driver IC. 240x240 RGB resolution, 4-wire SPI interface, and 3.3V/5V module input. Module-side labels are VCC, GND, DIN, CLK, CS, DC, RST, and BL/BLK. The display is write-only over SPI, so no MISO line is required for the LCD. Supported by Adafruit GC9A01A and TFT_eSPI libraries in Arduino-compatible firmware projects.

1

NEO-6M GPS Module

u-blox NEO-6M

u-blox NEO-6M based GPS receiver module. Outputs NMEA sentences (GGA, RMC, etc.) over UART at 9600 baud by default. Provides latitude, longitude, altitude, speed, and time. The NEO-6M module itself is a 3.3V-class device; many GY-NEO6MV2 breakout boards accept 5V on their VCC header through an onboard regulator, but UART I/O remains 3.3V-domain and must not be driven above 3.6V. Features an on-board patch antenna footprint and an SMA/IPEX connector for external active antenna (preferred for faster lock acquisition). Supply current ~45 mA in acquisition, ~11 mA in tracking.

1

Buzzer

Piezo 3.3 V

Piezo buzzer for sound output

Assemble it in 5 steps

1. Desligue tudo e prepare a alimentação USB

Com o ESP32 desconectado, posicione o ESP32 DevKit v1, a tela GC9A01, o NEO-6M e o buzzer. A alimentação continua sendo pela porta USB do ESP32.

  • Use jumpers curtos para a tela SPI.
  • Todos os módulos devem compartilhar o mesmo GND.
  • Não conecte 5 V aos pinos de sinal do GPS ou da tela; o ESP32 usa sinais de 3,3 V.

2. Conecte a tela redonda

Ligue VCC e BL/BLK da tela a 3V3, e GND ao GND. Ligue SCK/CLK ao GPIO18, MOSI/DIN ao GPIO23, CS ao GPIO19, DC ao GPIO26 e RST ao GPIO25.

  • MOSI pode estar marcado como DIN ou SDA; SCK pode estar marcado como CLK ou SCL.
  • Confira a serigrafia do seu módulo antes de energizar.
  • O pino SCL/CLK da GC9A01 é clock SPI e deve ir ao GPIO18, não ao GPIO22.

3. Conecte o receptor GPS

Ligue VCC do NEO-6M a 3V3 e GND ao GND. Ligue TX do GPS ao GPIO16 do ESP32 e RX do GPS ao GPIO17.

  • A serial é cruzada: TX de um equipamento vai ao RX do outro.
  • Para obter sinal inicial, deixe a antena cerâmica voltada para o céu em uma área aberta.
  • Se o seu breakout especificar somente alimentação de 5 V, confirme a documentação do modelo antes de alterar esta ligação.

4. Conecte o buzzer

Ligue SIGNAL do buzzer ao GPIO4 e GND ao GND do ESP32.

  • Use um buzzer piezo de 3,3 V.
  • Não conecte um buzzer de alta potência diretamente ao GPIO; use um driver se ele exceder a corrente suportada pelo pino.

5. Use a ponte Wi-Fi do mapa

Após usar Deploy, conecte o ESP32 à rede Wi-Fi pelo portal MiniGPS-Setup. No telefone, abra o endereço IP do ESP32 na mesma rede. A página permite enviar latitude, longitude, direção e linhas de ruas; o dispositivo desenha as linhas em preto e branco e mantém a seta no centro.

  • O formato das ruas é lat,lon|lat,lon para cada rua, separando ruas com ponto e vírgula.
  • Uma automação ou aplicativo complementar do telefone pode enviar POST para /map com os campos lat, lon, heading e roads.
  • O Google Maps não oferece a tela nem sua geometria de ruas diretamente ao ESP32. Esta ponte recebe dados que uma automação/app autorizado no telefone obtiver e simplificar.
  • Use o mapa apenas como apoio visual e mantenha atenção total ao trânsito.

Review all connections

1. Connections between "display_1" and "ESP32"

Functiondisplay_1ESP32
powerVCC3V3
groundGNDGND
spiSCKGPIO 18
spiMOSIGPIO 23
digitalDCGPIO 26
digitalRSTGPIO 25
powerBL3V3
spiCSGPIO 19

2. Connections between "gps_1" and "ESP32"

Functiongps_1ESP32
powerVCC3V3
groundGNDGND
uartTXGPIO 16
uartRXGPIO 17

3. Connections between "buzzer_1" and "ESP32"

Functionbuzzer_1ESP32
groundGNDGND
digitalSIGNALGPIO 4

Deploy the firmware

#include <Arduino.h>
#include <SPI.h>
#include <WiFi.h>
#include <WebServer.h>
#include <WiFiManager.h>
#include <TinyGPS++.h>
#include <Adafruit_GFX.h>
#include <Adafruit_GC9A01A.h>


// Hoisted type definitions
struct Geo { double lat,lon; };

struct Road { Geo p[MAX_POINTS]; uint8_t n; };

struct Alert { double lat,lon; uint16_t radius; const char *label; };


// Forward declarations
bool parsePoint(String s,Geo &p);
void parseRoads(String data);
void project(Geo p,int &x,int &y);
void arrow(float h);
void drawMap();
const Alert* nearest(const Alert *points,size_t n,double &d);
void alerts();
String page();
void receiveMap();

constexpr int TFT_SCK=18,TFT_MOSI=23,TFT_CS=19,TFT_DC=26,TFT_RST=25;
constexpr int GPS_RX_PIN=16,GPS_TX_PIN=17,BUZZER_PIN=4;
constexpr uint16_t BLACK=0x0000,WHITE=0xFFFF,GRAY=0x8410,DARK_GRAY=0x4208;
constexpr uint8_t MAX_ROADS=16,MAX_POINTS=12;



const Alert radars[1]={{0,0,0,"RADAR"}}, dangers[1]={{0,0,0,"PERIGO"}};
constexpr size_t RADAR_COUNT=0,DANGER_COUNT=0;
Adafruit_GC9A01A tft(TFT_CS,TFT_DC,TFT_RST);
TinyGPSPlus gps; HardwareSerial gpsSerial(2); WebServer server(80);
Road roads[MAX_ROADS]; uint8_t roadCount=0;
double mapLat=0,mapLon=0; float mapHeading=0; bool phoneMap=false,redraw=true,alarmOn=false; uint32_t lastDraw=0;

bool parsePoint(String s,Geo &p){int c=s.indexOf(',');if(c<1)return false;double a=s.substring(0,c).toDouble(),b=s.substring(c+1).toDouble();if(a < -90 || a > 90 || b < -180 || b > 180)return false;p={a,b};return true;}
void parseRoads(String data){roadCount=0;int start=0;while(start<data.length()&&roadCount<MAX_ROADS){int end=data.indexOf(';',start);if(end<0)end=data.length();String line=data.substring(start,end);roads[roadCount].n=0;int ps=0;while(ps<line.length()&&roads[roadCount].n<MAX_POINTS){int pe=line.indexOf('|',ps);if(pe<0)pe=line.length();Geo p;if(parsePoint(line.substring(ps,pe),p))roads[roadCount].p[roads[roadCount].n++]=p;ps=pe+1;}if(roads[roadCount].n>=2)roadCount++;start=end+1;}}
void project(Geo p,int &x,int &y){double north=(p.lat-mapLat)*111320.0,east=(p.lon-mapLon)*111320.0*cos(mapLat*DEG_TO_RAD);x=120+(int)(east*.75);y=120-(int)(north*.75);}
void arrow(float h){float a=(h-90)*DEG_TO_RAD;int x1=120+(int)(15*cosf(a)),y1=120+(int)(15*sinf(a));int x2=120+(int)(-10*cosf(a)-8*sinf(a)),y2=120+(int)(-10*sinf(a)+8*cosf(a));int x3=120+(int)(-10*cosf(a)+8*sinf(a)),y3=120+(int)(-10*sinf(a)-8*cosf(a));tft.fillTriangle(x1,y1,x2,y2,x3,y3,WHITE);tft.drawTriangle(x1,y1,x2,y2,x3,y3,BLACK);}
void drawMap(){tft.fillScreen(BLACK);tft.drawCircle(120,120,116,WHITE);tft.drawCircle(120,120,115,WHITE);tft.setTextColor(WHITE);tft.setTextSize(1);tft.setCursor(116,9);tft.print("N");if(!phoneMap&&!gps.location.isValid()){tft.setCursor(44,116);tft.print("AGUARDANDO GPS");return;}for(uint8_t r=0;r<roadCount;r++)for(uint8_t i=1;i<roads[r].n;i++){int x1,y1,x2,y2;project(roads[r].p[i-1],x1,y1);project(roads[r].p[i],x2,y2);tft.drawLine(x1,y1,x2,y2,WHITE);tft.drawLine(x1+1,y1,x2+1,y2,DARK_GRAY);}tft.drawCircle(120,120,18,GRAY);tft.fillCircle(120,120,16,BLACK);arrow(mapHeading);tft.setCursor(8,225);tft.print(phoneMap?"MAPA DO TELEFONE":"GPS LOCAL");}
const Alert* nearest(const Alert *points,size_t n,double &d){const Alert *best=nullptr;d=1e9;for(size_t i=0;i<n;i++){double v=TinyGPSPlus::distanceBetween(mapLat,mapLon,points[i].lat,points[i].lon);if(v<d){d=v;best=&points[i];}}return best;}
void alerts(){if(!phoneMap&&!gps.location.isValid())return;double a,b;const Alert *p=nearest(dangers,DANGER_COUNT,a);if(!(p&&a<=p->radius))p=nearest(radars,RADAR_COUNT,b);bool warn=p&&((p==&dangers[0]&&a<=p->radius)||(p!=&dangers[0]&&b<=p->radius));if(warn!=alarmOn){ledcWriteTone(BUZZER_PIN,warn?2200:0);alarmOn=warn;}}
String page(){return R"HTML(<!doctype html><meta name="viewport" content="width=device-width,initial-scale=1"><body style="font-family:Arial;max-width:600px;margin:auto;padding:16px"><h2>Mini GPS - ponte de mapa</h2><p>O Google Maps nao entrega diretamente sua tela ou ruas ao ESP32. Envie a posicao, direcao e linhas de ruas de uma automacao/app do telefone para este mapa minimalista.</p><p>Formato de ruas: <code>lat,lon|lat,lon</code>; separe ruas por <code>;</code>.</p><form method="post" action="/map">Latitude <input name="lat" required><br>Longitude <input name="lon" required><br>Direcao (0=N, 90=L) <input name="heading" value="0"><br>Ruas <textarea name="roads" rows="6" style="width:100%"></textarea><br><button>Enviar mapa</button></form><p>API: POST /map com lat, lon, heading e roads. Telefone e dispositivo devem estar no mesmo Wi-Fi.</p></body>)HTML";}
void receiveMap(){if(!server.hasArg("lat")||!server.hasArg("lon")){server.send(400,"text/plain","Envie lat e lon.");return;}Geo p;if(!parsePoint(server.arg("lat")+","+server.arg("lon"),p)){server.send(400,"text/plain","Coordenadas invalidas.");return;}float h=server.hasArg("heading")?server.arg("heading").toFloat():0;if(h<0||h>=360){server.send(400,"text/plain","Direcao entre 0 e 359.");return;}mapLat=p.lat;mapLon=p.lon;mapHeading=h;if(server.hasArg("roads"))parseRoads(server.arg("roads"));phoneMap=true;redraw=true;server.send(200,"application/json","{\"ok\":true,\"roads\":"+String(roadCount)+"}");}
void setup(){Serial.begin(115200);ledcAttach(BUZZER_PIN,2000,8);ledcWriteTone(BUZZER_PIN,0);SPI.begin(TFT_SCK,-1,TFT_MOSI,TFT_CS);tft.begin();tft.setRotation(0);gpsSerial.begin(9600,SERIAL_8N1,GPS_RX_PIN,GPS_TX_PIN);WiFiManager wm;wm.setConfigPortalTimeout(180);wm.autoConnect("MiniGPS-Setup");server.on("/",HTTP_GET,[](){server.send(200,"text/html",page());});server.on("/map",HTTP_POST,receiveMap);server.on("/status",HTTP_GET,[](){server.send(200,"application/json","{\"ip\":\""+WiFi.localIP().toString()+"\",\"mapActive\":"+String(phoneMap?"true":"false")+"}");});server.begin();}
void loop(){while(gpsSerial.available())gps.encode(gpsSerial.read());server.handleClient();uint32_t now=millis();if(!phoneMap&&gps.location.isValid()){double a=gps.location.lat(),b=gps.location.lng();float h=gps.course.isValid()?gps.course.deg():mapHeading;if(fabs(a-mapLat)>.00001||fabs(b-mapLon)>.00001||fabs(h-mapHeading)>3){mapLat=a;mapLon=b;mapHeading=h;redraw=true;}}if(redraw&&now-lastDraw>=50){lastDraw=now;redraw=false;drawMap();}alerts();}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik