Build a Real-Time GPS Tracker with Raspberry Pi Pico & SIM800L GSM Module

Published  August 7, 2025   0
Cellular GPS Tracker with Raspberry Pi Pico

Building a real-time GPS tracker with Raspberry Pi Pico has never been more accessible thanks to affordable cellular communication modules and simplified programming libraries. This comprehensive Raspberry Pi Pico GPS tracker tutorial demonstrates how to create a professional-grade IoT tracking device using the SIM800L GSM module, Neo-6M GPS receiver, and the versatile Raspberry Pi Pico microcontroller, similar to our ESP32 GPS Tracker and Arduino GPS Tracker that we have built earlier. This DIY GPS tracking system transmits precise location data over cellular networks, enabling real-time location monitoring from anywhere worldwide with minimal hardware investment.

This comprehensive GPS tracker tutorial covers complete hardware setup, from circuit wiring and power management to real-time location data transmission. It is tailored for hobbyists, engineers, and educators looking to implement real-time location tracking in their IoT projects. Also, if you're interested in exploring more about Raspberry Pi projects, be sure to check out CircuitDigest. We have previously covered a lot of Raspberry Pi Pico and Raspberry Pi Zero Projects.

Required Components for Raspberry Pi Pico GPS Tracker

Component

Description

Quantity

Raspberry Pi Pico / Pico W

Microcontroller (RP2040)

1

SIM800L Module

GSM/GPRS module for internet access

1

Neo-6M GPS Module

For GPS data Collection

1

GPS Antenna

External GPS antenna with SMA/UFL

1

GSM Antenna

External GSM antenna

1

LED

Red, Yellow, Green

3

SIM

2G Capable SIM (Airtel)

1

Jumper Wires, Breadboard

For prototyping

as needed

Warning: SIM800L is sensitive to voltage and requires a 3.7V - 4.2V regulated supply with sufficient current (~2A peak). If your module doesn't have a built-in regulator(LDO) always use a LiPo or a separate buck converter.

How Does the Raspberry Pi Pico GPS Tracker Work?

In this project, we are building a compact and efficient GPS tracker that’s easy to implement both in hardware and software. At the core of the system is the Raspberry Pi Pico, a cost-effective microcontroller that coordinates data collection and transmission. For location tracking, we use the Neo-6M GPS module, which provides real-time latitude and longitude data via GPS satellites. To send this data to the cloud, we rely on the SIM800L GSM module, which transmits information over a cellular network using simple HTTP requests.

Pico GPS Tracker concept working demonstration

On the software side, the tracker integrates with the GeoLinker API, developed by Circuit Digest Cloud. This API allows us to easily send GPS data to a cloud server, where each location point is logged in a database and displayed on an interactive map. Once you log into your Circuit Digest Cloud account, you can view the plotted route in real time, giving you a live view of the tracker’s movements.

Pico GPS Tracker with Offline Data Sync

One of the standout features of this system is its ability to handle internet connectivity issues gracefully. When the cellular connection is lost, the tracker temporarily stores GPS data locally in a buffer. As soon as the connection is restored, the stored data is uploaded first, followed by any newly captured points. This ensures that no location data is lost, even when operating in areas with poor or intermittent network coverage. Overall, this makes the Pico GPS tracker a reliable solution for real-time location tracking with built-in fault tolerance.

Understanding SIM800L and Neo-6M GPS Modules

SIM800L GSM/GPRS Module

The SIM800L is a low-cost GSM/GPRS module designed for IoT tracking devices and embedded GPS solutions, making it ideal for DIY location monitoring projects. It operates on quad-band frequencies (850, 900, 1800, and 1900 MHz), making it compatible with 2G networks worldwide. Its small form factor and UART-based communication make it ideal for microcontroller integration.

Key Features and Capabilities

Voice, SMS, and GPRS Data: The SIM800L can make and receive voice calls, send and receive SMS messages, and connect to the internet using GPRS. This makes it versatile for remote monitoring and alert-based systems.

 TCP/UDP and HTTP Support: It supports TCP/IP stack and HTTP via AT commands, enabling interaction with web APIs or cloud databases.

 AT Command Control: Controlled entirely via AT commands over UART. It responds to common commands like AT, AT+CSQ, AT+CREG?, AT+HTTPACTION, etc.

⇒ Low Power Modes: Offers various power-saving options, including sleep and idle states, for battery-powered projects.

⇒ Network Functions: Can scan for available networks, handle automatic or manual network registration, and report signal quality.

Hardware Considerations

⇒ Voltage Sensitivity: It must be powered with a stable supply of 3.7V to 4.2V, and it can draw up to 2A of current during transmission bursts.

⇒ Antenna: Requires an external GSM antenna for proper signal strength. Antenna types include PCB, helical, or SMA-based external ones.

⇒ Logic Levels: Operates at 2.8V to 3V logic level but in our testing it was able withstand 3.3V on MCU Pins. use a level shifter if you are using a standalone SIM800L IC.

Neo-6M GPS Module

The Neo-6M is a compact and cost-effective GPS receiver module developed by u-blox, designed specifically for tracking and navigation applications. It supports only the GPS (Global Positioning System) constellation.

The module is popular for its low power consumption, high positioning accuracy, and fast time-to-first-fix (TTFF). It communicates using standard NMEA 0183 sentences over a UART interface, typically at a default baud rate of 9600 bps. Due to its reliability and affordability, it's widely used in vehicle tracking systems, drones, robotics, and DIY navigation devices.

Key Features and Capabilities

⇒ NMEA 0183 Protocol Output: Continuously transmits GPS data such as time, latitude, longitude, altitude, number of satellites, speed, and fix quality via standard NMEA sentences like $GPGGA, $GPRMC, $GPGSV, and others.

⇒ High Sensitivity Receiver: Capable of acquiring weak satellite signals (-161 dBm tracking sensitivity), making it suitable for outdoor use with minimal obstruction.

⇒ Fast Time-to-First-Fix (TTFF): Supports hot, warm, and cold starts with TTFF as low as 1 second (hot) and 27 seconds (cold).

⇒ Position Accuracy: Offers 2.5m CEP positioning accuracy under clear sky conditions.

⇒ Backup Battery Support: Can be equipped with a coin cell to retain satellite data and RTC for faster startups.

Hardware Considerations

⇒ Antenna Requirements: Works best with an active GPS antenna for improved signal acquisition. Typically includes a UFL or SMA connector.

⇒ Power Supply: IC Operates at 3.3V and draws around 45–50 mA. Avoid powering from 5V directly if your module doesn't have any power regulator(LDO).

⇒ Status LED: Blinks when the module has acquired a valid fix, providing easy visual feedback.

Raspberry Pi Pico GPS Tracker Circuit Diagram & Wiring Guide

Circuit Diagram of Cellular GPS Tracker with Raspberry Pi Pico

SIM800L Pins

Pico GPIO Pins

Description

TX

GP1 (RX)

Receive from SIM800L

RX

GP0 (TX)

Send to SIM800L

GND

GND

Common ground

VCC

Power

External battery or supply (3.7V - 4.2V)


Neo-6M Pins

Pico GPIO Pins

Description

TX

GP5 (RX)

Receive GPS data

RX

GP4 (TX)

 

GND

GND

Common ground

VCC

Power

External battery or supply (3.6V)

Note: Ensure that both the SIM800L and Neo-6M share a common ground with the Pico. Also if your SIM800L & Neo-6M Modules have an inbuilt regulator(LDO), you can connect VCC directly to the 5V or VBUS pin in Pico.

Setting Up CircuitDigest Cloud & GeoLinker API

CircuitDigest Cloud is a powerful IoT and AI integration platform designed to support developers, engineers, and makers in rapidly prototyping and deploying intelligent embedded systems. It offers a suite of web-based services and APIs that simplify the process of adding connectivity, data visualization, and intelligent automation to microcontroller and IoT projects.

Circuit Digest Cloud Features

Among its key offerings is the GeoLinker API, a cloud-based data ingestion and visualization service specifically designed for GPS/GNSS-based devices. CircuitDigest Cloud also includes additional RESTful APIs such as QR Code Scanning, Automatic Number Plate Recognition, and SMS API, enabling seamless expansion into computer vision, authentication, and notification systems. These features allow users to build comprehensive IoT ecosystems from a single, unified interface.

Visualizing GPS Data using GeoLinker

To begin using the GeoLinker API for real-time GPS data tracking and map visualization, users must first register for an account on CircuitDigest Cloud and generate a unique API Key. This API Key acts as a secure authentication token, linking your embedded device (e.g., Raspberry Pi Pico) with the cloud backend. The process is simple and requires only a few steps:

CircuitDigest Cloud Registration

GeoLinker Login UI

Step 1: Navigate to the Platform
Go to the official CircuitDigest Cloud website at https://circuitdigest.cloud . Click on the "Login" button at the top-right corner of the homepage to proceed to the authentication portal.

Step 2: Account Setup
If you already have a registered account, proceed to log in using your credentials. For new users, click on the "Register Now" or equivalent link to access the sign-up form. Complete the required fields such as email address, username, and password. Upon successful submission, your account will be created.

Step 3: Logging In
Use your newly created credentials to log in. Once authenticated, you will be redirected to the user dashboard where cloud services and account settings are managed.

API Key Generation for GeoLinker

Generating API Key Geolinker

Step 4: Accessing the API Management Console
Click on the "My Account" section located at the top-right of the navigation bar. This will take you to the API management interface.

Step 5: API Key Creation
On the API Key generation page, you will be prompted to solve a captcha for security purposes. Enter the captcha text in the input field, then click the "Generate API Key" button.

Step 6: Retrieving the API Key
If the captcha is validated successfully, your newly generated API Key will appear in a table. The table also includes metadata such as:

Info

Description

Expiration Date

Indicates when the key will expire

Usage Count

Shows the number of times the key has been used

Copy Shortcut

You can click the clipboard icon next to the key to instantly copy it to your clipboard for use in code

API Key Usage Limits

CircuitDigest Cloud enforces usage limits to maintain server performance and ensure fair access across users. As of now:

  • GeoLinker (GPS Tracking) API Keys: allow up to 10,000 GPS data points per key.
  • SMS and ANPR API Keys: are limited to 100 requests per key.

Note: Once you exhaust the allowed quota, you may return to the API management page to generate a new API Key, which refreshes your usage count. These quotas are subject to change as the platform evolves and user demand scales.

Optional: Also, if you want to check out the full documentation and payload structure used by CircuitDigest Cloud and GeoLinker, you can refer to the comprehensive article: Free IoT-based GPS Tracking Map for ESP32, NodeMCU, and Arduino. This guide includes deeper technical insights, and payload formatting examples.

Arduino IDE Setup: Installing Libraries

To get started with GeoLinker on the Raspberry Pi Pico / Pico W, follow these steps to install both the library and the board support package using the Arduino Library Manager and Boards Manager, no manual ZIP extraction required.

Step 1: Install RP2040 Board Support

Additional Board Manager URLsChoosing Raspberry Pi Pico

Step 2: Install GeoLinker Library

  • Go to Tools > Manage Libraries...
  • In the Library Manager, search for: GeoLinker
  • Find GeoLinker by Jobit Joseph and click Install.
  • Once installed, you’ll find ready-to-use examples under: File > Examples > GeoLinker
Instaling GeoLinker Raspberry Pi Pico GPS Tracker

This method ensures the latest version of the library and board support is always used, eliminates manual errors, and allows seamless updates through the IDE itself.

Troubleshooting

GeoLinker Library Not Appearing in Examples

If you don’t see the GeoLinker examples under File > Examples, try the following steps:

Restart Arduino IDE

  • After installing a new library via the Library Manager, it's a good idea to fully restart the Arduino
  • IDE to refresh its library index. 
  • Check Folder Name, Ensure the folder is named exactly GeoLinker, and that it directly contains .h and .cpp files not nested inside another subdirectory.

Confirm Successful Installation

  • Go to Tools > Manage Libraries
  • Search for GeoLinker again
  • Ensure it shows as “Installed”, and the author is Jobit Joseph, If not installed, click the Install button.

Check Board Selection

Make sure you have a compatible board selected like Raspberry Pi Pico or Pico W under Tools > Board.

Some libraries, including GeoLinker, only show their examples when supported boards are selected.

Select Compatible Board For GeoLinker

Verify Board Platform is Installed

Make sure you’ve added the RP2040 board URL and installed the "Raspberry Pi Pico / RP2040 by Earle Philhower" package from Boards Manager.

Board Manager URL: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 

Arduino Code Explanation for Pico GPS Tracker

#include <GeoLinker.h>

This line imports the GeoLinker library, which simplifies GPS and GSM-based IoT applications by internally handling GPS parsing, AT commands, modem initialization, cloud communication, error tracking, and offline storage. It allows the user to interact with complex modules like Neo-6M and SIM800L using a high-level API.

Gps Serial Configuration

#define gpsSerial Serial2  // UART1
#define GPS_RX 4
#define GPS_TX 5
#define GPS_BAUD 9600   // Standard NMEA baud rate

This block sets up the UART communication for the GPS module. It assigns Serial2 (UART1 on Raspberry Pi Pico) for GPS communication and maps the RX and TX lines to GPIO pins 4 and 5, respectively. The baud rate is set to 9600, which is standard for most NMEA-output GPS modules such as Neo-6M. This configuration ensures that raw NMEA data can be read from the GPS module for parsing.

GSM SERIAL CONFIGURATION
#define gsmSerial Serial1  // UART0
#define GSM_RX 0
#define GSM_TX 1
#define GSM_BAUD 9600   // Standard modem baud rate

This section initializes the serial communication for the GSM modem. It uses Serial1 (UART0), with GPIO 0 as RX and GPIO 1 as TX, connected to the SIM800L module. The baud rate is again set to 9600 bps, which matches the default setting of most GSM modules. This setup enables two-way communication using AT commands to control the modem.

LED DEFINITIONS
#define LED_GREEN 21   // Success indicator
#define LED_YELLOW 20  // GPS error indicator
#define LED_RED 19     // GSM error indicator

Here, three LEDs are defined for status indication. The green LED (GPIO 21) signals a successful data upload. The yellow LED (GPIO 20) lights up when a GPS-related error occurs, such as no fix or parse failure. The red LED (GPIO 19) turns on when GSM-related problems arise, like SIM issues, network errors, or modem timeouts. These LEDs are used as real-time visual feedback tools.

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

This section declares variables used to manage the status LEDs and track error timing. The greenLedOn flag keeps track of whether the green LED is currently active. Separate boolean flags (gpsErrorActive, gsmErrorActive) indicate whether a GPS or GSM error is ongoing. Timestamp variables track the last time errors occurred, and errorTimeout defines how long (8 seconds) an error should persist before auto-clearing.

Network Configuration

const char* apn = "gprs"; // Cellular APN
const char* gsmUser = nullptr;  // APN username if required
const char* gsmPass = nullptr;  // APN password if required

This part configures the network credentials for GSM connectivity. The APN (Access Point Name) is set to "gprs", which is commonly used by default on many GSM networks but should be changed according to your SIM provider. The username and password fields are left null, which is appropriate for most consumer networks that don’t require authentication.

Geo Linker Configuration

const char* apiKey = "xxxxxxxx";  // 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

This configuration block sets up the GeoLinker tracking service. The apiKey is used to authenticate this device with the cloud backend. deviceID uniquely identifies the device. The updateInterval sets how frequently (in seconds) data should be sent. Offline storage is enabled to buffer unsent data if the network is temporarily unavailable, with a limit of 20 records. Auto-reconnect is enabled (mainly for WiFi, but harmless for GSM). Time offset is set to IST (UTC+5:30) for correct timestamp alignment.

GeoLinker Instance Creation

GeoLinker geo;  //GeoLinker instance

This line creates an instance of the GeoLinker class named geo. This object will be used throughout the program to manage payloads, GPS data, modem communication, and cloud interaction.


setup() Function
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.");
}

The setup() function is where hardware and library initialization occurs. It starts by initializing the USB serial console at 115200 bps. It then configures the GPS UART with the appropriate pins and baud rate, followed by setting up the GSM UART similarly. Each LED pin is set to output mode and turned off initially. Then, the GeoLinker instance is initialized using the GPS UART, followed by setting API credentials, upload interval, debug level, offline storage settings, and local time zone. The GSM modem is initialized using its serial port, and modem response timeouts are set. After successful initialization, a message is printed to indicate completion.

Payload Preparation in loop()

unsigned long now = millis();
 // Example payload
 geo.setPayloads({
   {"temperature", 27.5},
   {"humidity", 65.3}
 });
 geo.setBatteryLevel(90);

Inside the loop() function, sensor data is simulated using hardcoded values. The setPayloads() method creates a key-value map that mimics sensor readings like temperature and humidity. The battery level is also manually set to 90%, which could be replaced by actual ADC readings in a real application.

Calling GeoLinker Main Loop

uint8_t status = geo.loop();

This is the core line that drives the library's internal logic. The geo.loop() method processes the GPS data, prepares payloads, checks modem status, uploads data if possible, and returns a status code that indicates whether the data was sent successfully or if any error occurred. This is essential for managing the system state and triggering appropriate LED responses.

Handling Success with Green LED

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;
 }

When the geo.loop() function returns STATUS_SENT, it means data was successfully transmitted to the cloud. A success message is printed to the console. The green LED is turned on briefly and a timer is started to keep it on for one second. GPS and GSM error flags are also reset since a successful transmission implies normal operation.

Turning Off Green LED After Timeout
 if (greenLedOn && (now - greenLedTimer >= 1000)) {
   digitalWrite(LED_GREEN, LOW);
   greenLedOn = false;
 }

This checks if the green LED has been on for more than 1 second. If it has, the LED is turned off and the flag is cleared. This ensures the LED only briefly blinks on success, acting as a non-intrusive status indicator.

GPS Error Detection and Yellow LED Control

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);

If the status indicates a GPS error (either no fix or a parsing problem), the gpsErrorActive flag is set and a timestamp is saved. If a later loop shows normal status (STATUS_SENT or 0), and 8 seconds have passed since the last error, the flag is cleared. Based on this flag, the yellow LED is either turned on or off to indicate current GPS health.

GSM Error Detection and Red LED Control

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);

This section evaluates whether the returned status represents a GSM-related error, including issues like modem timeout, SIM registration failure, context activation failure, or server response problems. If so, the gsmErrorActive flag is set. If these errors disappear and 8 seconds pass, the flag is cleared. The red LED reflects flag state, giving a real-time indication of GSM problems.

How GeoLinker Works Internally

The GeoLinker library is designed to abstract the complexity involved in communicating with both the SIM800L GSM module and Neo-6M GPS receiver using UART serial ports. Internally, it uses a state-driven command handler to send, receive, parse, and validate data from both modules asynchronously.

SIM800L Communication - AT Command Flow

GeoLinker uses a sequence of AT commands to manage SIM initialization, network registration, GPRS setup, and HTTP transactions. Here’s a typical command chain:

AT Commands

Description

AT

Basic attention command to verify module responsiveness

AT+CPIN?

Checks whether a SIM card is inserted and unlocked (returns +CPIN: READY)

AT+CSQ

Signal quality check (returns RSSI and BER)

AT+CREG?

Checks registration status with the GSM network

AT+CGATT?

Verifies GPRS attachment

AT+SAPBR

Establishes a bearer profile for internet connection

AT+HTTPINIT

Initializes HTTP service

AT+HTTPPARA="CID",1

Selects bearer profile 1

AT+HTTPPARA="URL","https://www.circuitdigest.cloud/geolinker"

Sets the HTTP target

AT+HTTPDATA=length,time

Prepares data for POST request

AT+HTTPACTION=1

Executes HTTP POST

AT+HTTPREAD

Reads HTTP response

AT+HTTPTERM

Ends the HTTP session

GeoLinker manages retries and checks responses for success (e.g., OK, +HTTPACTION: 1,200) before proceeding.

Neo-6M GPS Parsing Logic

The GPS module sends continuous NMEA sentences. GeoLinker listens on the GPS UART port and selectively extracts fields from:

  • $GPGGA: Extracts UTC time, fix quality, number of satellites, and altitude.
  • $GPRMC: Retrieves latitude, longitude, speed over ground, and date/time.

The library uses simple delimiters (comma-separated values) to tokenize NMEA strings and compute floating-point coordinates in decimal format.

State Machine and Watchdog

GeoLinker operates as a non-blocking state machine. Each geo.update() call evaluates current conditions (GSM ready, GPS fix, internet available) and advances the routine only when previous steps succeed. It includes built-in:

Timeout handling

Delays and retry limits for slow network responses.

Fallback handling

Detects command errors and restarts failed sequences

Watchdog resets

Ensures system recovery in case of hangs or unresponsive modules.

This architecture ensures robustness and simplicity in field deployments without deep user involvement in GSM protocol or GNSS parsing.

Users  can override or expand library methods for custom HTTP headers, SMS alerts, or alternate GPS or GNSS formats (UBX, GSV, etc.).

Power Considerations

  • Use a 3.7V LiPo battery capable of >1.5A discharge.
  • Add 470uF or higher capacitor near SIM800L VCC-GND
  • Avoid using USB 5V directly if your module doesn't have a builtin regulator, instead use a buck converter if needed.
  • Keep antenna wires away from power and data lines to reduce EMI.

Pico GPS Tracker Testing & Real-Time Tracking

After successfully uploading the code to the Raspberry Pi Pico, the GPS tracker system begins operating immediately. The Neo-6M module starts acquiring satellite signals to fetch real-time location data, while the SIM800L module handles communication with the cloud server via HTTP. As each GPS coordinate is collected, it's transmitted to the GeoLinker Cloud, where it's stored and visualized on the map in real time.

Raspberry Pi Pico GPS Tracker Demo working demonstration

To test the system's reliability, especially under unstable network conditions, we simulated internet loss during our trial run. As expected, the device intelligently detected the connectivity issue and began storing the GPS data locally in a buffer. Once the connection was restored, the stored data points were automatically pushed to the server in the correct order, followed by new incoming data.

Offline To Online GPS Tracking Map Update

This test confirmed that the offline data handling feature works seamlessly, ensuring that no location information is lost, even when operating in areas with poor or intermittent GSM coverage. The live map view on the Circuit Digest Cloud updated accordingly, accurately reflecting the full movement path during the offline and online transition.

Pico GPS Tracker Map UI

Troubleshooting

Symptom

Cause

Fix

SIM800L not responding

Undervoltage or poor GND

Use dedicated power, add capacitor near SIM800L VCC-GND

GPS data shows zeroes

No fix or poor antenna

Move outdoors, wait 1-2 mins

Until status Led Blinks at constant 1s interval

HTTP failed

Wrong APN or server

Verify APN, server URL

Module resets/reboots

Inrush current from SIM800L

Add capacitor, use better battery capable of >1.5A discharge

Debug output blank

UART pins incorrectly mapped

Confirm Serial1 & Serial2 pins

GPS Tracker Projects and Applications

GPS tracking offers more than just location logging; it powers real-world solutions in transportation, safety, asset monitoring, and more. We've explored several GPS-based projects that demonstrate how to collect, transmit, and visualize location data using affordable hardware like the Raspberry Pi Pico.

Practical Use Cases of Raspberry Pi Pico GPS Tracker

These examples highlight how GPS and GSM modules can be applied to real-world tracking systems:

  • Vehicle GPS Tracker with Cloud Logging: Sends continuous location updates to a remote server, ideal for fleet and vehicle monitoring.
  • SMS-based Location Responder: Share GPS coordinates via SMS when prompted. Great for emergency tracking without the internet.
  • Pet or Asset GPS Beacon: Periodically transmits location to a mobile app or dashboard. Useful for pets, bikes, or parcels.
  • Emergency Alert Button with Coordinates: Send a preset message and live location on button press. Perfect for SOS systems and elderly care.
  • Delivery & Fleet Tracking System: Logs real-time positions and timestamps for route tracking. Can integrate driver ID using RFID.

Whether you're building for personal use or large-scale deployments, these use cases offer practical blueprints to kickstart your GPS-powered IoT projects.

Working on this project using the Raspberry Pi Pico, the SIM800L GSM module, and the Neo-6M GPS receiver has been a rewarding journey. What really made things smoother for me was the GeoLinker library, it took care of a lot of the headaches around GPS data parsing and GSM communication. Instead of wrestling with AT commands and NMEA strings, I could focus more on the actual logic of the system.

That said, I did run into a few challenges that might help others in the community. One big thing: HTTP POST requests over the SIM800L can be slow, especially if you're in an area with weak 2G coverage. I saw delays of several seconds at times, which could definitely be a deal-breaker for time-sensitive applications.

Another thing I learned the hard way is that power stability is crucial. The SIM800L is really picky. If your power supply can't handle those quick current spikes during data transmission, the module might restart or freeze. I ended up adding extra capacitors and making sure my supply could handle peak loads, which helped a lot.

I'd love to hear how others in the community are expanding on setups like this, or if you’ve tackled similar challenges with power or network latency. Let’s share and improve together!

GitHub Repository with Arduino Code and Circuit

Code and Circuit of Raspberry Pi Pico GPS TrackerCode and Circuit of Raspberry Pi Pico GPS Tracker Zip File

Previous GPS Tracker Based Projects

From building real-time GPS loggers to integrating cloud mapping with GeoLinker, our past projects show how simple components can enable powerful location-based applications.

How to build a simple GPS Tracker using ESP32 and Visualize Data on Map

How to build a simple GPS Tracker using ESP32 and Visualize Data on Map

We will build a GPS tracker using the ESP32 microcontroller and the Neo-6M GPS module as the main hardware components, with GeoLinker as the software component.

Free IoT-based GPS Tracking Map for ESP32, NodeMCU, and Arduino

Free IoT-based GPS Tracking Map for ESP32, NodeMCU, and Arduino

Learn how to visualize GPS data with the CircuitDigest Cloud GeoLinker API. Capture coordinates, send them from embedded devices, and visualize routes with timestamps.

Build A Low Power SMS Based Vehicle Tracking System with A9G GSM+GPS Module and Arduino

Build A Low Power SMS Based Vehicle Tracking System with A9G GSM+GPS Module and Arduino

Here you will know about A9G Module and how to make a Low Power SMS Based Vehicle Tracking System with A9G GSM+GPS Module and Arduino.

Complete Project Code

#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);
}
 
Video

Have any question related to this Article?

Add New Comment

Login to Comment Sign in with Google Log in with Facebook Sign in with GitHub