A low-cost, open-source hardware and software pipeline for geo-tagged bike vibration analysis.
bikesensor combines an ESP32-C3 and an MPU-6050 IMU to record high-frequency road vibrations over BLE. By syncing this data with a smartphone's GPX track using a custom clock-model and GPS interpolation, it maps road quality with sub-meter accuracy. The Python-based data pipeline applies a 25Hz Butterworth low-pass filter to isolate major bumps and Short-Time Fourier Transforms (STFT) to analyze the frequency spectrum of the road texture, visualizing everything in an interactive Streamlit dashboard.
- ESP32-C3 SuperMini + MPU-6050 over I²C (SDA=GPIO 6, SCL=GPIO 7, AD0→GND). Avoid GPIO 8 (onboard LED) and GPIO 9 (BOOT button).
- Hardware includes a battery voltage divider connected to an analog pin (GPIO 0) to monitor battery percentage.
- iOS/Android phone running LightBlue for BLE log capture.
- Phone records a GPX track in parallel (any GPX recorder app).
Service 0xFFE0, characteristic 0xFFE1 (notify). Two packet types,
distinguished by first byte:
| Type | Bytes | Layout |
|---|---|---|
SYNC 0xA5 |
9 | [0xA5][u32 sample_idx LE][u16 fs LE][u8 n_axes][u8 battery_percent] |
DATA 0x5A |
6+12·N | [0x5A][u32 first_sample_idx LE][u8 N][N × 6 × int16 BE] |
IMU bytes are forwarded raw from the MPU-6050 FIFO (big-endian). Defaults: fs = 250 Hz, N = 10 samples/packet, ±4 g, ±500 °/s.
- Sample timestamps come from a linear fit
t_phone = a + b·sample_idxover all SYNC packets — kills BLE jitter (~50–200 ms) and absorbs the ESP32-vs-phone clock offset. - Position per FFT window is interpolated from the 1 Hz GPX track onto each window's center timestamp — sub-meter along-track at constant speed, even though absolute lat/lon is bounded by phone-GPS noise (3–5 m).
- STFT with 0.5 s Hann window, 0.1 s hop → 0.5 m hop spacing at 5 m/s.
pio run -d firmware # build
pio run -d firmware -t upload # flash
pio device monitor -b 115200 # serial debugSource lives in firmware/bikesensor/bikesensor.ino; firmware/platformio.ini
points at it via src_dir = bikesensor.
The primary way to process and visualize data is through the interactive Streamlit dashboard.
# 1. Flash the firmware (above).
# 2. Record a ride: GPX on phone, LightBlue logging characteristic 0xFFE1.
# 3. Export LightBlue log as CSV or TXT. Save GPX.
# 4. Launch the dashboard:
uv run streamlit run src/dashboard.pyInside the dashboard:
- Upload your recorded
.gpxtracks and.csv/.txtLightBlue logs using the sidebar file uploader. - The pipeline will automatically merge the files, calculate STFT features (like
max_bump_gand frequency bands), and extract battery metrics. - Processed data is saved to
data/imu.csv,data/windows.csv, anddata/track.csvfor fast reloading.
The dashboard features:
- Map Analysis: An interactive heatmap and clickable route. Click any point to view local time-domain and frequency-domain vibration details.
- Route Overview: Charts detailing vibration intensity (
max_bump_g,rms_g, frequency bands) and speed over the cumulative distance of the ride. - Key Metrics: Ride duration, total distance, average speed, and battery consumption tracking.
