Raspberry Pi Pico GPS Tracker - Complete Walkthrough

by ElectroScope Archive in Circuits > Raspberry Pi

141 Views, 1 Favorites, 0 Comments

Raspberry Pi Pico GPS Tracker - Complete Walkthrough

Pico-GPS-Tracker-concept.gif

So, want to know where your car, bike, or even your pet is at all times? Cool, because in this build we’re making a real-time GPS tracker with a Raspberry Pi Pico. Thanks to cheap GPS modules and GSM modules, you don’t need NASA-level tech to track stuff.

We’ll be using:

  1. Raspberry Pi Pico (the RP2040 board we all love)
  2. SIM800L GSM module (to send location over 2G)
  3. Neo-6M GPS module (to figure out where we are)

By the end, you’ll have a working GPS tracker that can log your location on a map in real time. Not bad for a weekend Raspberry Pi Pico project, right?

Here's an overview of how it works:

  1. GPS module (Neo-6M) → Grabs your latitude and longitude from satellites.
  2. Pico → Reads that data and prepares it for sending.
  3. SIM800L GSM module → Shoots it off over the cellular network using HTTP.
  4. Cloud API (GeoLinker) → Stores it and shows it on a map.

Bonus feature: if the GSM network drops, the Pico saves your location in a buffer. Once the connection comes back, it uploads the backlog first. So your pet turtle’s secret escape route won’t be lost.

Supplies

  1. Raspberry Pi Pico / Pico W – 1
  2. SIM800L Module – 1
  3. Neo-6M GPS Module – 1
  4. GPS Antenna – 1
  5. GSM Antenna – 1
  6. LEDs (Red, Yellow, Green) – 3
  7. SIM Card (2G Capable, Airtel) – 1
  8. Jumper Wires & Breadboard – As needed

Wiring It All Up

GPS-Tracker-with-Raspberry-Pi-Pico-Circuit-Diagram.png

Here’s the general hookup:

SIM800L → Pico

  1. TX → GP1 (RX)
  2. RX → GP0 (TX)
  3. GND → GND
  4. VCC → 3.7–4.2V external supply

Neo-6M → Pico

  1. TX → GP5 (RX)
  2. RX → GP4 (TX)
  3. GND → GND
  4. VCC → 3.6V (or regulated 3.3V depending on your board)

Important: Keep grounds common between all modules and the Pico.

If your SIM800L and GPS modules have built-in regulators (LDOs), you can safely power them from Pico’s 5V pin. Otherwise, don’t risk it.

Setting Up GeoLinker Cloud

GeoLinker-Login-UI.png
Generating-API-Key-Geolinker.png

We’ll be using CircuitDigest’s GeoLinker API to send and visualize GPS data.

  1. First, sign up on circuitdigest.cloud.
  2. Generate an API Key from your dashboard.
  3. That’s the magic password your Pico uses to upload data.

API limits:

  1. Up to 10,000 GPS points per key.
  2. SMS/ANPR stuff capped at 100 requests per key.

If you burn through that, just generate a new key.

Arduino IDE Setup

Additional-Board-Manager-URLs.jpg
Choosing-Raspberry-Pi-Pico.jpg
Instaling-Geolinker-Raspberry-Pi-Pico-GPS-Tracker.jpg

Yes, we’re programming the Pico with Arduino IDE. Here’s the drill:

  1. Open Arduino IDE → File > Preferences.
  2. Add this to “Additional Board Manager URLs”:

https://github.com/earlephilhower/arduinopico/releases/download/global/package_rp2040_index.json
  1. Go to Tools > Board > Boards Manager, search “RP2040,” and install “Raspberry Pi Pico / RP2040 by Earle Philhower.”
  2. Install the GeoLinker library from Library Manager (search “GeoLinker”).
  3. You’ll now see example sketches under File > Examples > GeoLinker.

Upload the Code

Here’s the full Arduino sketch for the project (drop-in ready).

#include <GeoLinker.h>
// ==================================================================
// GPS SERIAL CONFIGURATION
// ==================================================================
#define gpsSerial Serial2 // UART1
#define GPS_RX 4
#define GPS_TX 5
#define GPS_BAUD 9600 // Standard NMEA baud rate
// ==================================================================
// GSM SERIAL CONFIGURATION
// ==================================================================
#define gsmSerial Serial1 // UART0
#define GSM_RX 0
#define GSM_TX 1
#define GSM_BAUD 9600 // Standard modem baud rate
// ==============================
// LED DEFINITIONS
// ==============================
#define LED_GREEN 21 // Success indicator
#define LED_YELLOW 20 // GPS error indicator
#define LED_RED 19 // GSM error indicator
// ==============================
// TIMING AND STATE
// ==============================
bool greenLedOn = false;
unsigned long greenLedTimer = 0;
bool gpsErrorActive = false;
bool gsmErrorActive = false;
unsigned long lastGPSStatusTime = 0;
unsigned long lastGSMStatusTime = 0;
const unsigned long errorTimeout = 8000; // 8 seconds to auto-clear error
// ==============================
// NETWORK CONFIGURATION
// ==============================
const char* apn = "gprs"; // Cellular APN
const char* gsmUser = nullptr; // APN username if required
const char* gsmPass = nullptr; // APN password if required
// ==============================
// GEO LINKER CONFIGURATION
// ==============================
const char* apiKey = "xxxxxxx"; // Your GeoLinker API key
const char* deviceID = "GeoLinker_tracker_test1"; // Unique device identifier
const uint16_t updateInterval = 3; // Data upload interval (seconds)
const bool enableOfflineStorage = true; // Enable offline data storage
const uint8_t offlineBufferLimit = 20; // Max stored offline record, Keep it minimal for MCUs with less RAM
const bool enableAutoReconnect = true; // Enable auto-reconnect Only for WiFi, Ignored with GSM
const int8_t timeOffsetHours = 5; // UTC + Hours
const int8_t timeOffsetMinutes = 30; // UTC + Minutes
GeoLinker geo; //GeoLinker instance
void setup() {
Serial.begin(115200);
delay(1000);
// GPS UART
gpsSerial.setTX(GPS_RX);
gpsSerial.setRX(GPS_TX);
gpsSerial.begin(GPS_BAUD);
// GSM UART
gsmSerial.setTX(GSM_RX);
gsmSerial.setRX(GSM_TX);
gsmSerial.begin(GSM_BAUD);
// LED Setup
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_RED, OUTPUT);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_RED, LOW);
// GeoLinker Init
geo.begin(Serial2);
geo.setApiKey(apiKey);
geo.setDeviceID(deviceID);
geo.setUpdateInterval_seconds(updateInterval);
geo.setDebugLevel(DEBUG_BASIC);
geo.enableOfflineStorage(enableOfflineStorage);
geo.setOfflineBufferLimit(offlineBufferLimit);
geo.enableAutoReconnect(enableAutoReconnect);
geo.setTimeOffset(timeOffsetHours, timeOffsetMinutes);
geo.setNetworkMode(GEOLINKER_CELLULAR);
geo.setModemCredentials(apn, gsmUser, gsmPass);
geo.beginModem(Serial1);
geo.setModemTimeouts(5000, 10000);
Serial.println("GeoLinker setup complete.");
}
void loop() {
unsigned long now = millis();
// Example payload
geo.setPayloads({
{"temperature", 27.5},
{"humidity", 65.3}
});
geo.setBatteryLevel(90);
uint8_t status = geo.loop();
// =========================================
// SUCCESS: Green LED for 1 second
// =========================================
if (status == STATUS_SENT) {
Serial.println("Data sent successfully!");
if (!greenLedOn) {
digitalWrite(LED_GREEN, HIGH);
delay(500);
greenLedOn = true;
greenLedTimer = now;
}
gpsErrorActive = false;
gsmErrorActive = false;
}
if (greenLedOn && (now - greenLedTimer >= 1000)) {
digitalWrite(LED_GREEN, LOW);
greenLedOn = false;
}
// =========================================
// GPS ERROR LED Handling
// =========================================
if (status == STATUS_GPS_ERROR || status == STATUS_PARSE_ERROR) {
gpsErrorActive = true;
lastGPSStatusTime = now;
} else if (gpsErrorActive && (status == STATUS_SENT || status == 0)) {
if (now - lastGPSStatusTime > errorTimeout) {
gpsErrorActive = false;
}
}
digitalWrite(LED_YELLOW, gpsErrorActive ? HIGH : LOW);
// =========================================
// GSM ERROR LED Handling
// =========================================
bool gsmError = (
status == STATUS_NETWORK_ERROR ||
status == STATUS_CELLULAR_NOT_REGISTERED ||
status == STATUS_CELLULAR_CTX_ERROR ||
status == STATUS_CELLULAR_DATA_ERROR ||
status == STATUS_CELLULAR_TIMEOUT ||
status == STATUS_BAD_REQUEST_ERROR ||
status == STATUS_INTERNAL_SERVER_ERROR
);
if (gsmError) {
gsmErrorActive = true;
lastGSMStatusTime = now;
} else if (gsmErrorActive && (now - lastGSMStatusTime > errorTimeout)) {
gsmErrorActive = false;
}
digitalWrite(LED_RED, gsmErrorActive ? HIGH : LOW);
}

---

Testing the Tracker

RaspberryPiPico-GPSTracker-Demo.gif
  1. Insert your 2G SIM into the SIM800L.
  2. Power everything up (with a solid supply, remember).
  3. Open the Serial Monitor to check debug logs.
  4. Once it locks onto satellites, it’ll start sending coordinates to the cloud.

Now log in to CircuitDigest Cloud → check your GeoLinker dashboard → and you’ll see your tracker plotting points in real time.

Troubleshooting - What Went Wrong?

Select-Compatible-Board-For-GeoLinker.jpg

No GPS fix? Make sure the GPS antenna is outside with a clear view of the sky. Indoors won’t cut it.

SIM800L not connecting? Double-check your SIM is 2G capable. Many carriers are killing 2G, so this matters.

Random resets? Your SIM800L is probably underpowered. Feed it a LiPo or solid buck converter.

GeoLinker library missing? Restart Arduino IDE after installation and make sure you selected Raspberry Pi Pico under Tools > Board.

Wrap-Up

buzzlightyear-gif.gif

And that's all of it. You now have a Raspberry Pi Pico GPS Tracker that can log routes, recover from bad signals, and stream data to a live map.

Where to take it next? Put it on your bike, car, or drone. Here's my recommendation... hide it in your Buzz Lightyear toy to see how far to infinity and beyond really is.