Trace is an open-source vehicle data logger built on the ESP32-S3. It collects, timestamps, and fuses data from multiple onboard sources — GNSS, IMU, OBD2 — and writes it to a CSV file on an SD card. Recording is controlled by a physical switch with debounce logic and a dedicated status LED. A lightweight web interface served over Wi-Fi Access Point allows real-time configuration without any dedicated app.
Trace was originally designed as a replacement for a commercial device that also drove an RGB LED strip. That LED subsystem is still present and functional, but the project's focus today is squarely on data logging and analysis.
- GNSS — position, speed, course, altitude, UTC time (ATGM336H via UART + TinyGPS++)
- IMU — longitudinal/lateral acceleration, roll, pitch, slope estimate with confidence (ISM330DHCX via I2C, Madgwick 6-DOF filter)
- OBD2 — RPM, vehicle speed, engine load, throttle position, coolant temperature (Mode 01 PIDs via CAN/TWAI or UART simulator)
- Heading estimated from gyroscope integration, corrected by GNSS course over ground (complementary filter with speed- and HDOP-weighted gain)
- Yaw rate smoothing via EMA
- Filtered lat/lon position (EMA, alpha scales with speed)
- Fused vehicle speed (GNSS primary, OBD2 fallback)
- Runtime gear detection based on RPM/speed ratio with configurable hysteresis
- Per-gear calibration via a 2-second stable acquisition window
- Calibration data persisted to NVS (survives power cycles)
- Interactive calibration via serial commands
- Monotonic 64-bit microsecond clock (
esp_timer) - Soft UTC sync from GNSS fix, with EMA-filtered offset (α = 0.05)
- Per-sensor timestamps on every sample; staleness of each source logged per row
- Sync quality score (0–100) with linear decay starting 5 s after last fix, reaching 0 at 60 s
- CSV output to SD card, one row per 500 ms
- Recording started and stopped by a physical switch (GPIO with 50 ms debounce)
- Filename derived from GNSS UTC at acquisition start (
YYYYMMDD_HHMMSS.csv) - Recording only begins once a valid GNSS fix has been acquired (prevents invalid filenames)
- Automatic flush every 10 rows; system continues without logging if SD is absent
- Fields: position, GNSS quality, OBD2 data, IMU data, heading/yaw, gear estimate, monotonic and UTC timestamps, sensor ages, sync quality
A dedicated monochromatic LED (GPIO 8) provides recording feedback without requiring a serial connection:
| Switch | Condition | LED |
|---|---|---|
| OFF | — | Off |
| ON | Recording active | Solid on |
| ON | Waiting for GNSS fix | Fast blink (2 Hz) |
| ON | SD error | Slow blink (1 Hz) |
- Wi-Fi Access Point (
192.168.4.1), no infrastructure required - REST API for LED control (
/api/status,/api/color,/api/mode,/api/params) - Dark-mode, mobile-first HTML UI served directly from the device (no CDN, no external dependencies)
- UI scoped to LED configuration; telemetry is logged to SD, not streamed over HTTP
Retained for compatibility with the original hardware.
- Modes: static color, fading (7-color cycle), breathing (sinusoidal fade)
- RPM warning overlay: blinks red at 120 ms intervals when RPM exceeds a configurable threshold
- All parameters configurable at runtime via web API or serial
| Component | Role |
|---|---|
| ESP32-S3 | Main MCU |
| ATGM336H | GNSS receiver (UART) |
| ISM330DHCX | 6-DOF IMU (I2C) |
| MCP2515 / TWAI | CAN bus interface for OBD2 |
| SD card (SPI) | Data storage |
| Toggle switch | Recording control (GPIO 7, active low) |
| Monochromatic LED | Recording status indicator (GPIO 8) |
| RGB LED (common anode) | Status / legacy feature (GPIO 4, 5, 6) |
Default pin assignments are defined as #define constants at the top of each manager header and can be overridden at compile time.
- PlatformIO or Arduino IDE with ESP32 board support
- Libraries:
TinyGPSPlus,ArduinoJson,Preferences(bundled with ESP32 core)
# Clone the repo
git clone https://github.com/mdmmt05/Trace.git
cd Trace
# Build and flash (PlatformIO)
pio run -t uploadBy default the firmware compiles with the UART simulator transport (useful for development without a real vehicle). To target a real CAN bus, define OBD2_USE_TWAI in your build flags:
; platformio.ini
build_flags = -DOBD2_USE_TWAIOn startup, the system waits for a valid GNSS fix before allowing recording to begin. If the recording switch is already ON at boot, the status LED will blink at 2 Hz until the fix is acquired. After a 5-minute timeout the system transitions to running regardless, to allow indoor testing without GNSS.
Connect at 115200 baud. Commands are terminated with \n. Type help to list all available commands.
| Command | Description |
|---|---|
cal_gyro |
Gyroscope bias calibration (vehicle stationary) |
cal_acc |
Accelerometer calibration (vehicle stationary and level) |
set_mounting <roll> <pitch> |
Set sensor mounting offset in degrees |
save_cal |
Save current IMU calibration to NVS |
reset_cal |
Reset IMU calibration to defaults |
show_cal |
Print current IMU calibration parameters and time sync status |
| Command | Description |
|---|---|
gear_help |
List gear commands |
gear_show |
Show current per-gear calibration (ratio, speed ref, RPM ref) |
gear_cal <1..5> |
Start a 2-second stable acquisition window for the specified gear |
gear_save |
Persist the last completed acquisition to NVS |
gear_reset |
Clear all gear calibration data from NVS |
Calibration procedure: engage the target gear at a steady speed, run gear_cal <N>, wait 2 seconds for the acquisition window to close, then confirm with gear_save. The system checks longitudinal acceleration, speed variance, and RPM variance to reject unstable captures.
Each row contains:
timestamp_utc_str,
lat, lon, alt_m, sat, hdop,
speed_obd_kmh,
acc_lon_G, acc_lat_G,
roll_deg, pitch_deg, slope_deg, slope_confidence,
heading_deg, yawRate_dps, heading_confidence,
rpm, load_pct, throttle_pct,
estimated_gear,
t_mono_us, utc_epoch_us, utc_valid, sync_quality,
imu_t_us, gnss_t_us, obd_speed_t_us,
imu_age_ms, gnss_age_ms, obd_speed_age_ms
The *_age_ms columns record how stale each sensor's data was at log time — useful for post-processing quality filtering. A value of -1 means no sample has been received yet for that source.
├── main.cpp # Setup, loop, switch/LED logic, serial command handler
├── shared_data.h # Global VehicleData struct (all producers/consumers)
├── gnss_manager.* # GNSS UART + TinyGPS++ integration
├── imu_manager.* # ISM330DHCX driver, Madgwick filter, NVS calibration
├── obd2_manager.* # OBD2 CAN decoder (TWAI + UART simulator)
├── vehicle_fusion_manager.* # Sensor fusion: heading, position, speed
├── gear_estimator.* # RPM/speed gear detection + NVS calibration
├── time_sync_manager.* # Monotonic clock + GNSS soft UTC sync
├── sd_manager.* # SPI SD card + CSV writer
├── web_server.* # AP-mode WebServer + REST API + embedded UI
└── rgb_controller.* # RGB LED driver (legacy)
MIT