Most people rely on their phones for GPS, but when you're traveling off-grid, "good enough" usually isn't. As we started slowing down to notice the details of our trips, we realized that having the camper itself know exactly where it is—independent of our phones—was a game-changer.
After ditching RV app clutter and centralizing our systems into Home Assistant, the next logical step was feeding that system better data. We wanted accurate location updates not just to see where we are on a map, but to trigger automations based on our environment. Whether it's adjusting our solar expectations or knowing the local weather at camp, it all starts with precise, dedicated data.
Why I Needed a Dedicated GPS for the Camper
At first, I was using the GPS built into the Quectel EM12-G modem of my LTE router (a MikroTik WAP R AC), but every time the GPS was active, the LTE interface would act up. This would sometimes cause the GPS to go offline, so I had to write a script on the MikroTik to check the GPS status and restart it if it was dead — but that didn’t always work. In some cases, the GPS would stop reporting latitude, longitude, speed, and other positioning data, yet still show the data age as current, which is what I was triggering the script on. From the router’s point of view, everything looked fine, even though no usable location data was coming in. I needed a solution that gave me reliable GPS data without interfering with our internet connection, so I decided to build a dedicated GPS for the camper.
Building a Standalone GPS for the Camper
First, I flashed the ESP32 with a basic ESPHome config that didn’t include GPS. I did this so I could update the firmware over Wi-Fi remotely, without having to be inside the camper. Once that was working, I added a static IP and configured the GPS to report latitude, longitude, speed, altitude, and course. I also added logic to only update the stored coordinates when we moved more than 50 meters — this prevents small GPS drift from constantly updating Home Assistant when the camper isn’t really moving.
![]() |
| Beyond 'good enough': Integrating a dedicated GPS sensor to provide the TenFootStripes dashboard with real-time, off-grid location data. |
Here’s the full YAML I use. I keep this as a reference for myself, you are more than welcome to use it on your own build, and it also shows exactly how the GPS is configured:
esphome:
name: esphome-web-3bb178
friendly_name: camper-gps
min_version: 2025.11.0
name_add_mac_suffix: false
esp32:
variant: esp32
framework:
type: esp-idf
logger:
api:
ota:
- platform: esphome
wifi:
ssid: "!secret"
password: "!secret"
manual_ip:
static_ip: 10.0.10.206
gateway: 10.0.10.1
subnet: 255.255.255.0
uart:
id: uart_gps
tx_pin: GPIO17
rx_pin: GPIO16
baud_rate: 9600
globals:
- id: last_lat
type: float
restore_value: yes
initial_value: '0.0'
- id: last_lon
type: float
restore_value: yes
initial_value: '0.0'
- id: has_fix
type: bool
restore_value: yes
initial_value: 'false'
gps:
uart_id: uart_gps
update_interval: 20s
latitude:
id: gps_lat
internal: true
longitude:
id: gps_lon
internal: true
interval:
- interval: 20s
then:
lambda: |-
if (!id(gps_lat).has_state() || !id(gps_lon).has_state()) {
return;
}
// First GPS fix
if (!id(has_fix)) {
id(last_lat) = id(gps_lat).state;
id(last_lon) = id(gps_lon).state;
id(has_fix) = true;
return;
}
// Distance calculation
const float R = 6371000.0;
float lat1 = id(last_lat) * (M_PI / 180.0);
float lon1 = id(last_lon) * (M_PI / 180.0);
float lat2 = id(gps_lat).state * (M_PI / 180.0);
float lon2 = id(gps_lon).state * (M_PI / 180.0);
float dlat = lat2 - lat1;
float dlon = lon2 - lon1;
float a = sin(dlat / 2) * sin(dlat / 2) +
cos(lat1) * cos(lat2) *
sin(dlon / 2) * sin(dlon / 2);
float distance = R * 2 * atan2(sqrt(a), sqrt(1 - a));
// Update both coordinates if moved ≥50 m
if (distance >= 50) {
id(last_lat) = id(gps_lat).state;
id(last_lon) = id(gps_lon).state;
}
sensor:
- platform: template
id: latitude_sensor
name: "Latitude"
unit_of_measurement: "°"
accuracy_decimals: 6
icon: mdi:latitude
lambda: |-
return id(last_lat);
- platform: template
id: longitude_sensor
name: "Longitude"
unit_of_measurement: "°"
accuracy_decimals: 6
icon: mdi:longitude
lambda: |-
return id(last_lon);
With this setup, the GPS feeds directly into Home Assistant and updates automatically while driving, without spamming location changes when we’re stopped as GPS data tends to "dance around" as satellites come and go and the GPS gets a lock on different ones.
Hardware Setup
The NEO-6M GPS module is connected to the ESP32 over UART, using GPIO17 for TX and GPIO16 for RX (be sure to reverse TX and RCV on the GPS module ie Tx to RCV and RCV to TX). The module uses a very small ceramic antenna that plugs directly into the GPS board, keeping the overall footprint compact.
The GPS module is powered from the 3.3V pin on the ESP32, while the ESP32 itself is powered through its USB port using a very small buck converter tied into the USB power lines. This lets me run the entire setup directly off the camper’s 12V system without needing any extra power adapters.
With the GPS separated from the router, the LTE modem is much more stable now, and the ESP32 integrates cleanly into Home Assistant using ESPHome’s API and OTA updates. That means I can update firmware, adjust sensors, or tweak logic from remote, without physically accessing the hardware.
Integration with Home Assistant
All GPS data flows straight into Home Assistant. Latitude and longitude are exposed as sensors, making them easy to use in dashboards and automations. Speed, altitude, course, satellite count, and GPS-provided time are also available.
Right now, the system updates the coordinates only when we’ve moved a significant distance, which keeps the data clean and intentional. Going forward, I plan to use Home Assistant itself to detect when the camper is parked — for example, by monitoring speed and assuming we’re stationary if it stays below 1 mph for a set amount of time — and then treating that last GPS update as the final parked location.
Lessons Learned
- Dedicated GPS is worth it: It removes reliability issues and keeps the LTE modem stable.
- My router-based GPS can lie: You can have “fresh” data age while latitude and longitude are no longer updating.
- ESPHome makes this easy: Template logic and internal variables work well for smoothing GPS updates.
- Keep a backup of your YAML: ESPHome’s dashboard sometimes hides the full config, so having a local copy saves headaches if you need to tweak the code at all.
- Flash a basic config first: Getting OTA working early lets you finish everything else from remote if you desire. In my case, the comfort of my living room instead of the camper. 😀
- Reuse what you have: I already had the ESP32, which made this solution simple and cost-effective. And having a 3d Printer allows me to make a housing for the project when I'm ready
- I still need to make it so that the GPS does one final update once the camper comes to rest for more than 10 minutes or so. Currently it can be up to 49 meters off if it updates right before I park.
How Are you Doing GPS?
How do you get GPS coordinates into your mobile Home Assistant setup? Are you running a router module, a standalone board, or some trick I haven’t discovered yet? Drop your setup below — I’m always curious how other campers keep their coordinates in check!


Thanks for stopping by! All comments are moderated for spam and clarity.