The most reliable baseline GPS tracking device with Arduino in 2026 pairs the Arduino Nano Every with a u-blox NEO-M8N breakout. By leveraging the Nano Every’s secondary hardware UART (Serial1), you eliminate the dropped NMEA sentences and checksum failures that plague older SoftwareSerial implementations on the classic ATmega328P Nano. This guide gives you the exact parts, pinout, and compilable code to get a fix, log the data, and debug the inevitable wiring faults.
Difficulty: Intermediate (Requires basic UART debugging and Li-ion power management)
Time to Build: 90 minutes
Target Board: Arduino Nano Every (ATmega4809, 5V logic with 3.3V I/O tolerance on RX/TX)
Total BOM Cost: ~$42 USD
The GPS Module Decision Matrix
Do not buy the $6 "NEO-6M" modules found on generic marketplaces. They are almost universally counterfeit, lack proper RF shielding, and fail to lock indoors or in urban canyons. Use this decision path to select your receiver:
| Module | Accuracy | Urban Canyon Performance | Approx. Cost | Verdict |
|---|---|---|---|---|
| Generic NEO-6M Clone | 5m - 15m | Poor (frequent dropouts) | $6 - $9 | Avoid. High failure rate. |
| u-blox NEO-M8N | 2.5m (CEP) | Excellent (72-ch, multi-GNSS) | $22 - $28 | Best all-rounder. |
| u-blox ZED-F9P (RTK) | 0.01m + RTK | Exceptional | $180+ | Overkill for basic tracking. |
Decision Path:
• If you need centimeter-level RTK accuracy for rover/tractor guidance → Buy the ZED-F9P.
• If you are building a high-altitude balloon (above 50km) → Buy the SAM-M8Q (high-altitude mode enabled).
• If you are building a standard vehicle, pet, or asset tracker → Default Pick: SparkFun GPS Breakout - NEO-M8N (Part #GPS-15193).
Hardware Spec Sheet & Pin Mapping
The Arduino Nano Every runs at 5V, but its RX/TX pins are connected to the ATmega4809's USART1, which tolerates 3.3V logic from the GPS module without a level shifter. We are using hardware serial to guarantee zero buffer overruns at 9600 baud.
Bill of Materials (BOM)
- Microcontroller: Arduino Nano Every (with headers soldered) - $11.50
- GPS Module: SparkFun NEO-M8N Breakout (Qwiic/UART) - $24.95
- Antenna: Active ceramic patch antenna with SMA connector (usually included with breakout) - $5.00
- Power: Adafruit PowerBoost 1000C + 18650 Li-ion cell (3.7V) - $20.00
- Wiring: 24 AWG silicone stranded wire, heat shrink
Pin Mapping Table
| NEO-M8N Breakout Pin | Arduino Nano Every Pin | Notes |
|---|---|---|
| TX (Transmit) | D0 (RX / Serial1) | GPS sends data to MCU. 3.3V logic is safe for Nano Every RX. |
| RX (Receive) | D1 (TX / Serial1) | MCU sends UBX commands to GPS. 5V to 3.3V; M8N RX is 5V tolerant. |
| VIN | 5V Pin | Use the Nano's 5V rail; the breakout's onboard LDO drops it to 3.3V. |
| GND | GND | Common ground is mandatory for UART reference. |
The NEO-M8N breakout includes a small MS621FE rechargeable lithium cell. This powers the real-time clock (RTC) and retains the satellite ephemeris data when main power is cut. If your Time-To-First-Fix (TTFF) takes 15+ minutes every time you power on, this battery is likely dead or missing, forcing a cold start every time.
Step-by-Step Wiring & Assembly
- Prepare the Power Rail: Solder the Adafruit PowerBoost 1000C
5VandGNDoutputs to the Nano Every's5VandGNDpins. Do not use theVINpin on the Nano for the PowerBoost; bypassing the onboard regulator prevents thermal throttling. - Connect the UART Lines: Wire the GPS
TXto Nano EveryD0(RX), and GPSRXto Nano EveryD1(TX). Never cross TX to TX. - Attach the Antenna: Screw the active SMA antenna onto the breakout. Ensure the copper ground plane of the antenna is facing the sky. Active antennas require 3.3V power from the module, which the breakout supplies automatically.
- Verify Voltages: Before plugging in the USB, use a multimeter to check the voltage between the GPS breakout's
3V3out pin andGND. It must read between 3.25V and 3.35V. If it reads 5V, the onboard LDO is blown.
Complete Arduino Tracking Code (Nano Every + M8N)
This code targets the Arduino Nano Every. It uses the industry-standard SparkFun u-blox GNSS Arduino Library. Install this via the Arduino Library Manager before compiling.
#include <SparkFun_u-blox_GNSS_Arduino_Library.h>
// Pin Definitions for Arduino Nano Every Hardware Serial1
#define GPS_RX_PIN 0
#define GPS_TX_PIN 1
#define GPS_BAUD 9600
// Instantiate the u-blox object
SFE_UBLOX_GNSS myGNSS;
// Tracking state variables
float latitude = 0.0;
float longitude = 0.0;
float altitude = 0.0;
uint8_t satellites = 0;
void setup() {
// Initialize USB Serial for debugging
Serial.begin(115200);
while (!Serial) { delay(10); } // Wait for serial port to connect
Serial.println(F("Arduino GPS Tracker - Nano Every + NEO-M8N"));
// Initialize Hardware Serial1 for GPS communication
Serial1.begin(GPS_BAUD);
// Attempt to connect to the u-blox module with error handling
if (myGNSS.begin(Serial1) == false) {
Serial.println(F("ERROR: u-blox GNSS not detected at 9600 baud on Serial1."));
Serial.println(F("Check TX/RX wiring, ensure VIN is 5V, and verify module is genuine."));
// Halt execution to prevent silent failures in the field
while (1) {
// Blink built-in LED rapidly to indicate hardware fault
digitalWrite(LED_BUILTIN, HIGH); delay(100);
digitalWrite(LED_BUILTIN, LOW); delay(100);
}
}
Serial.println(F("GNSS module connected successfully."));
// Configure module for optimal tracking
myGNSS.setNavigationFrequency(1); // 1 Hz update rate (use 5Hz or 10Hz for fast-moving drones)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); // Save to flash/EEPROM
}
void loop() {
// Check if new NMEA/UBX data is available
if (myGNSS.getPVT()) {
// Extract data with explicit casting to prevent integer truncation
latitude = (float)myGNSS.getLatitude() / 10000000.0;
longitude = (float)myGNSS.getLongitude() / 10000000.0;
altitude = (float)myGNSS.getAltitudeMSL() / 1000.0; // Convert mm to meters
satellites = myGNSS.getSIV(); // Satellites in View
// Only print if we have a valid 3D fix (Fix Type 3)
if (myGNSS.getFixType() == 3) {
Serial.print(F("FIX | Lat: ")); Serial.print(latitude, 6);
Serial.print(F(" Lon: ")); Serial.print(longitude, 6);
Serial.print(F(" Alt: ")); Serial.print(altitude, 1);
Serial.print(F("m | Sats: ")); Serial.println(satellites);
} else {
Serial.print(F("SEARCHING... Sats: ")); Serial.println(satellites);
}
}
delay(250); // Pace the loop to avoid flooding the I2C/UART buffer
}
Debugging: The First Three Things to Check When It Fails
GPS modules are notorious for silent failures. If your serial monitor isn't outputting clean coordinates, follow this ranked troubleshooting path.
1. Symptom: Serial Monitor prints "ERROR: u-blox GNSS not detected..."
Exact Error String: ERROR: u-blox GNSS not detected at 9600 baud on Serial1.
- Cause A (Most Likely): TX/RX lines are swapped. The GPS
TXmust go to the ArduinoRX(D0). Swap them and reset. - Cause B: The module is stuck in I2C mode. Some breakouts default to I2C (DDC) on boot. Send a UBX-CFG-PRT command via I2C to force UART, or use the SparkFun library's I2C fallback initialization:
myGNSS.begin(Wire). - Cause C: Counterfeit module. Fake chips do not respond to the proprietary UBX protocol handshake that the SparkFun library initiates. Measure the current draw; a real M8N draws ~45mA during acquisition. Fakes often draw <15mA.
2. Symptom: Gibberish Characters or "Checksum Failed" in Raw NMEA
Exact Error String: $GPGGA,...*XX [CHECKSUM FAIL] or random ASCII garbage.
- Cause A: Baud rate mismatch. The NEO-M8N defaults to 9600 baud. If your code initializes
Serial1.begin(115200), you will read gibberish. Ensure both match. - Cause B: If you are using an older Arduino Uno with
SoftwareSerial, it cannot reliably read 9600 baud while simultaneously printing to the hardware serial monitor. Fix: Switch to the Nano Every's hardwareSerial1as shown in the code above.
3. Symptom: Module Connects, but "SEARCHING..." Never Changes to "FIX"
Observation: Sats count stays at 0 or 1, even after 20 minutes.
- Cause A: You are testing indoors. GPS L1 signals (1575.42 MHz) are completely blocked by standard roof trusses and HVAC ducting. Take the device to a window or outdoors for the first cold start.
- Cause B: Passive vs. Active Antenna mismatch. If your breakout board does not supply 3.3V to the antenna SMA center pin, and you are using an active antenna, the LNA (Low Noise Amplifier) in the antenna is unpowered. Check the breakout's jumper pads to ensure
VCC_RFis bridged.
Extending or Simplifying the Build
Once you have a stable local serial output, you need to decide how to handle the data in the field.
How to Simplify: Offline SD Card Logging
If you don't need real-time tracking, drop the cellular uplink. Add an Adafruit MicroSD Breakout wired to the Nano Every's SPI pins (D11, D12, D13, D10 for CS). In the loop(), append the latitude and longitude floats to a CSV file every 5 seconds. This drops power consumption to ~65mA total, giving you roughly 40 hours of tracking on a single 18650 cell.
How to Extend: Live Cellular Uplink (MQTT)
To view the tracker on a map in real-time, you must add a cellular modem. The SIM7600G-H 4G HAT is the current standard for global coverage.
Warning: The SIM7600 requires 2A peak current bursts during tower handshakes. The Nano Every's onboard regulator cannot supply this. You must power the SIM7600 directly from a dedicated 4A buck converter tied to your 18650 battery, sharing only the GND and UART TX/RX lines with the Nano Every. Use the u-blox M8N documentation to configure the GPS for 1Hz updates, format the payload as JSON, and push it via MQTT over the SIM7600's TCP/IP stack.






