Featured Products
Loading...
Tracking a Bike Ride with the SAM-M10Q GPS Module
Eager to dip your toe into GPS tracking? We’ve got you covered. Wanting to catch every last bit of summer, we recently spent some time logging bike rides with the u-blox SAM-M10Q GPS module. It’s fascinating to see the location data captured from the ride, but even more satisfying to analyze the data for speed and other ride performance metrics. While we used it for bikes, the setup can easily be replicated for many other projects and activities such as hiking, walking, or any other activity you like to partake in the sun. We’ll walk you through, step-by-step, how to get up and running (or biking) with GPS tracking.
The Versatility of the SAM-M10Q
The SAM-M10Q GPS Receiver Breakout Board does the heavy lifting in this project: It’s a small solution but still packs an integrated antenna, it offers excellent accuracy for the low power it consumes, it supports up to four GNSS constellations so you’re never worried about getting a satellite fix, and it includes out-band jamming immunity while offering high RF sensitivity.
Once the code is implemented, all you need to get started is power. It’s an excellent GPS Receiver solution for a mobile application like this one.
Logging a Ride
We’re going to use a Teensy 4.1 (with its built-in SD card slot) for this build, along with the SAM-M10Q GPS Receiver Breakout Board of course. This guide uses I2C for communication with the SAM-M10Q, though it also supports UART if you prefer. For power, we’re going to use a conveniently-sized spare battery pack to supply both the Teensy and the SAM-M10Q through the Teensy’s micro-USB port. Here’s the setup:
- Teensy pin 19 to SAM-M10Q SCL
- Teensy pin 18 to SAM-M10Q SDA
- Teensy 3V3 Output to SAM-M10Q VCC
- Teensy GND to SAM-M10Q GND
Code Example
Over at the STRDC SDK, we’ve created an example to record data from our SAM-M10Q Receiver. This will get us started quickly. We’ll use a refresh time of 5Hz (the max for tracking four GNSS constellations), this can be done with the gnss_set_nav_rate() function.
After confirming everything works at the desk, we removed the serial stream and text outputs (as we won’t be accessing the serial stream for debugging on the ride) from the example. In particular, we’ll want to remove this line so the program can proceed after initialization:
while (!Serial) delay(10);
Once outside, we plugged in the battery pack and started riding.
Results
Here’s a snippet from one of our rides:
timestamp_ms,gps_datetime,latitude,longitude,altitude_mm,velocity_mps,siv
293986,2025-08-27T22:48:30Z,338574288,-1184002291,6818,5.756,6
294417,2025-08-27T22:48:30Z,338574417,-1184002153,7482,5.798,6
294800,2025-08-27T22:48:31Z,338574565,-1184002128,8975,5.744,6
295180,2025-08-27T22:48:31Z,338574727,-1184002149,10527,5.784,6
295611,2025-08-27T22:48:31Z,338574895,-1184002186,11944,5.821,6
We can already do some fun things like calculating our climb and how many satellites we had in view at each data point, or even see our speed: 13mph!
Let’s get a better view of our position data by plotting it on a map.
Plotting the Ride with Python
We can write a quick Python script using Pandas and Folium to overlay the data on a map to visualize the ride:
import pandas as pd
import folium
# Load the CSV file
csv_file = 'tracker_2.csv' # File to load
df = pd.read_csv(csv_file)
def find_column(possibilities, columns):
for p in possibilities:
for c in columns:
if p.lower() in c.lower():
return c
return None
lat_col = find_column(['lat'], df.columns) # Get latitude column
lon_col = find_column(['lon'], df.columns) # Get longitude column
if not lat_col or not lon_col: # Check if columns exist
raise ValueError('Could not find latitude or longitude columns in CSV.')
if df[lat_col].abs().max() > 90:
df[lat_col] = df[lat_col] / 1e7
df[lon_col] = df[lon_col] / 1e7
track = df.dropna(subset=[lat_col, lon_col])
track = track[(track[lat_col] != 0) & (track[lon_col] != 0)]
track = track.reset_index(drop=True)
if len(track) == 0:
raise ValueError('No valid GPS points found in file!')
center = [track.iloc[0][lat_col], track.iloc[0][lon_col]]
m = folium.Map(location=center, zoom_start=16)
folium.PolyLine(track[[lat_col, lon_col]].values, color='blue', weight=3, opacity=0.7).add_to(m)
folium.Marker([track.iloc[0][lat_col], track.iloc[0][lon_col]], popup='Start', icon=folium.Icon(color='green')).add_to(m)
folium.Marker([track.iloc[-1][lat_col], track.iloc[-1][lon_col]], popup='End', icon=folium.Icon(color='red')).add_to(m)
m.save('tracker_2_map.html')
print('Map saved to tracker_2_map.html')
And there we are, we have the entire map of the ride - must have been a good day to ride along the strand.
Conclusion
With the SAM-M10Q and STRDC GNSS-UBLOX Library, integration is easy and getting started is quick. While tracking data is nice, there’s plenty of opportunity to add more to this project:
- A display to show speed and distance would be helpful, allowing us to track in real-time.
- Adding an atmospheric sensor or an IMU would would enhance our tracking accuracy and provide some additional data.
- For hiking or snowboarding, plotting GPS data onto a 3D terrain map would be an excellent way to visualize hikes and lines, as well as logging speed.
The hardware and code mentioned here are great starting points for all kinds of outdoor tracking and data-logging ideas, so feel free to experiment and build on it. If you try something new or have questions, we’d love to hear about your project!
