When makers talk about location sensors for embedded projects, they are almost exclusively referring to GNSS (Global Navigation Satellite System) receivers like the u-blox NEO-M8N or the high-precision ZED-F9P. Unlike a simple analog proximity sensor that outputs a varying voltage, a GNSS module is a complex digital radio receiver. It outputs a serial stream of parsed ephemeris data and Doppler shift calculations, yielding geographic coordinates in the WGS84 datum.
The Sensing Principle: Trilateration and Time-of-Flight
GNSS location sensors determine position by calculating the time-of-flight of radio signals transmitted from multiple orbiting satellites. The receiver's baseband processor solves a system of equations using trilateration, requiring a minimum of four satellites to resolve 3D position (latitude, longitude, altitude) and correct the receiver's internal quartz clock drift against the atomic clocks on the satellites.
Because the physical measurement relies on nanosecond-precision timing of 1.575 GHz (L1 band) RF signals, the sensor does not output an analog voltage proportional to distance. Instead, it outputs digital serial data—typically formatted as NMEA 0183 ASCII sentences or proprietary UBX binary packets—over UART or I2C interfaces. The microcontroller's job is strictly to parse this digital stream, not to read an ADC pin.
Hardware Wiring and Pinout Specifications
A common bench mistake is frying a 3.3V GNSS module by tying its RX pin directly to a 5V Arduino Uno TX pin. Modern ESP32 DevKits operate at 3.3V logic, making them ideal for native 3.3V location sensors. Always use hardware UART pins on the ESP32 (UART1 or UART2) rather than SoftwareSerial, which introduces timing jitter that drops NMEA sentences at 9600 baud.
| Module | Supply Range (VCC) | Logic Level | Interface | Acquisition Current | ESP32 Pin Mapping |
|---|---|---|---|---|---|
| u-blox NEO-M8N | 2.7V - 3.6V | 3.3V | UART / I2C | ~45 mA | TX->GPIO16, RX->GPIO17 |
| u-blox ZED-F9P (RTK) | 2.7V - 3.6V | 3.3V | UART / I2C / SPI | ~115 mA | TX->GPIO16, RX->GPIO17 |
| Beitian BN-220 | 3.3V - 5.0V* | 3.3V | UART / I2C | ~40 mA | TX->GPIO16, RX->GPIO17 |
*Note: The BN-220 includes an onboard LDO allowing 5V supply, but the TX/RX data pins remain 3.3V logic. Do not feed 5V into the RX pin.
Output Signal Math: Raw NMEA to Physical Units
The raw output of a location sensor in NMEA mode is an ASCII string. The most critical sentence for position is $GPGGA (Global Positioning System Fix Data). A typical raw string looks like this:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
The Raw-to-Unit Conversion
The raw latitude and longitude are not in decimal degrees. They are formatted as DDMM.MMMM (Degrees and Minutes). If you pass 4807.038 directly into a mapping API, your waypoint will be hundreds of miles off. You must apply the following scaling math to convert to Decimal Degrees (DD.dddddd):
- Extract Degrees: Take the integer portion before the last two digits before the decimal. (e.g.,
48) - Extract Minutes: Take the remaining value. (e.g.,
07.038) - Apply Formula:
Decimal Degrees = Degrees + (Minutes / 60)
Worked Example:
Raw: 4807.038
Degrees: 48
Minutes: 7.038
Math: 48 + (7.038 / 60) = 48 + 0.1173 = 48.1173°
For distance scaling between two points, you cannot use simple Euclidean geometry because the Earth is a sphere. You must scale the decimal degrees using the Haversine formula to calculate the great-circle distance in meters. Libraries like TinyGPSPlus handle this math internally via the distanceBetween() function.
// ESP32 Hardware Serial Parsing via TinyGPSPlus
#include <TinyGPSPlus.h>
TinyGPSPlus gps;
HardwareSerial ss(1);
void setup() {
Serial.begin(115200);
// Initialize UART1 on RX=16, TX=17 at standard 9600 baud
ss.begin(9600, SERIAL_8N1, 16, 17);
}
void loop() {
while (ss.available() > 0) {
if (gps.encode(ss.read())) {
if (gps.location.isValid()) {
// Outputs correctly scaled Decimal Degrees
Serial.println(gps.location.lat(), 6);
}
}
}
}
Calibration, Interference, and Failure Modes
Location sensors do not require analog calibration (there is no trim pot to adjust). However, they require almanac and ephemeris data to achieve a fast Time-To-First-Fix (TTFF). If a module is 'cold started' after months in a drawer, it may take up to 15 minutes to download orbital parameters from the satellites. You can bypass this by implementing Assisted GPS (A-GPS), downloading the ephemeris data via WiFi and pushing it to the module over I2C/UART.
Common Interference Sources
The L1 band (1575.42 MHz) is highly susceptible to specific bench and field interference:
- USB 3.0 Broadband Noise: This is the number one killer of bench GPS tests. u-blox integration manuals explicitly warn that unshielded USB 3.0 cables and hubs emit broadband RF noise that overlaps the GPS L1 frequency, dropping signal-to-noise ratio (SNR) to zero. Always use shielded USB 2.0 cables or move the antenna away from your PC.
- Multipath Errors: In urban canyons or indoors, RF signals bounce off buildings, causing the receiver to calculate a longer time-of-flight. This results in position 'drift' of 10-50 meters even when stationary.
- Antenna Detuning: Ceramic patch antennas require a specific ground plane size (usually 70x70mm) to tune to 1.575 GHz. Taping a GPS module to a small 3D-printed enclosure without a ground plane will detune the antenna, resulting in zero satellite locks.
Frequently Asked Questions
Why do location sensors lose lock and drift when stationary indoors?
GNSS signals are incredibly weak by the time they reach the Earth's surface (roughly -130 dBm, equivalent to a 4-watt lightbulb viewed from 2,000 miles away). Standard building materials, especially low-E glass and metal roofs, attenuate these signals below the receiver's noise floor. When indoor signals do penetrate, they suffer from severe multipath reflections, causing the trilateration math to resolve to shifting, inaccurate coordinates. For indoor location tracking, you must switch to BLE beacons or UWB (Ultra-Wideband) modules, not GNSS.
How to wire multiple location sensors to one ESP32 microcontroller?
You cannot wire multiple UART TX pins to a single ESP32 RX pin without hardware multiplexing or diode-OR logic, which is messy. Instead, use the I2C interface. Most u-blox modules support I2C (labeled as DDC on the datasheet). Connect all SDA and SCL lines to the ESP32's I2C bus (GPIO 21 and 22), and use the module's hardware configuration pins or UBX software commands to change the I2C address of each sensor (default is usually 0x42). Alternatively, use an ESP32 with multiple hardware UARTs (like the standard 30-pin DevKit which exposes UART0, UART1, and UART2).
What is the raw output format of embedded location sensors: NMEA or UBX?
By default, almost all hobbyist location sensors output NMEA 0183 ASCII sentences (like $GPGGA and $GPRMC) at 9600 baud. NMEA is human-readable and easily parsed by libraries like TinyGPS. However, NMEA is bandwidth-heavy and lacks advanced telemetry like raw carrier phase data. For high-precision RTK applications or when logging data to an SD card at high speeds, you should send a configuration command to switch the module to the u-blox proprietary UBX binary protocol, which packs more data into smaller, checksum-verified binary frames.
Can I power 5V location sensors directly from the ESP32 3V3 pin?
If the sensor breakout board specifies a 5V input (like many generic 'Neo-6M' clones on Amazon), it means the board has an onboard 5V-to-3.3V LDO voltage regulator. If you feed it 3.3V from the ESP32, the LDO will experience a voltage dropout, feeding the actual GPS silicon roughly 2.8V, which is below the 3.0V minimum operating threshold. The module will fail to boot or continuously brownout during satellite acquisition. Always power 5V-labeled breakers from the ESP32's 5V (VIN) pin, while keeping the data lines connected to the 3.3V GPIOs.






