Capture, decode, and reconstruct your car's ECU maps from the OBD2 port — using an Adafruit Feather M4 CAN, an SD card, and a browser.
OBD2 CAN Logger is an open-source hardware + firmware + web-app project that:
- Logs every CAN frame from your car's OBD2 port to an SD card in real-time (CSV + binary)
- Streams live data to a Betaflight-style web dashboard over USB Serial (Web Serial API)
- Decodes signals using standard OBD2 PIDs or custom
.dbcfiles from opendbc - Reconstructs engine maps — fuel trims, ignition advance, MAF, MAP, throttle, RPM — as a baseline for tuning
The ultimate goal is to reverse-engineer and document the factory calibration maps of any OBD2-compliant car, so they can serve as a starting point for custom ECU tuning.
| Component | Part |
|---|---|
| Microcontroller | Adafruit Feather M4 CAN Express (ATSAMD51J19A) |
| SD card + RTC | Adafruit Adalogger FeatherWing |
| CAN Transceiver | Built-in on Feather M4 CAN — TCAN1051 on CAN1 (CANH/CANL pins exposed) |
| OBD2 Interface | OBD2 DB9/breakout cable wired to CANH/CANL |
Note: The Feather M4 CAN (product 4759) has an integrated CANFD transceiver on CAN1. It is different from the plain Feather M4 Express (product 3857). If you have product 3857 you will need to add an MCP2515 FeatherWing.
Feather M4 CAN OBD2 Connector (J1962)
────────────── ──────────────────────
CANH ──────── Pin 6 (CAN High)
CANL ──────── Pin 14 (CAN Low)
GND ──────── Pin 4 or 5 (Ground)
The Adalogger FeatherWing stacks directly on top of the Feather M4 CAN — no additional wiring needed for SD or RTC.
OBD2CanLogger/
├── firmware/
│ └── OBD2CanLogger/
│ └── OBD2CanLogger.ino # Arduino sketch for Feather M4 CAN
├── webapp/
│ └── index.html # Self-contained web dashboard (no server needed)
├── dbc/
│ └── (place .dbc files here) # Download from opendbc or add your own
└── README.md
Install the following libraries via the Arduino Library Manager:
| Library | Author | Notes |
|---|---|---|
ACANFD_FeatherM4CAN |
Pierre Molinaro | CAN/CANFD driver for the ATSAMD51 built-in controller |
SD |
Arduino | SD card read/write |
RTClib |
Adafruit | Real-time clock (PCF8523 on Adalogger) |
ArduinoJson |
Benoit Blanchon | v6+ — JSON serial protocol |
Adafruit TinyUSB Library |
Adafruit | USB Mass Storage support for SD card access over USB |
- In Arduino IDE, add Adafruit's board package URL:
https://adafruit.github.io/arduino-board-index/package_adafruit_index.json - Install Adafruit SAMD Boards via Boards Manager
- Select: Adafruit Feather M4 CAN (SAMD51)
- Upload
firmware/OBD2CanLogger/OBD2CanLogger.ino
- On power-up, the firmware reads
/config.jsonfrom the SD card (if present) - Creates a timestamped session log:
session_20240101_120000.csvand.can - Logs every CAN frame to SD
- Streams decoded frames as JSON over USB Serial at 115200 baud
- Red LED blinks every 5 seconds as heartbeat
The firmware requires a FAT32-formatted SD card. Any standard microSD or SD card up to 32 GB works. Cards larger than 32 GB may be exFAT by default — reformat them to FAT32 before use.
How to format by OS:
| OS | Steps |
|---|---|
| macOS | Open Disk Utility → select your SD card → click Erase → Format: MS-DOS (FAT) → Scheme: Master Boot Record → Erase |
| Windows | Open File Explorer → right-click the SD drive → Format → File system: FAT32 → Start (for cards >32 GB use guiformat) |
| Linux | sudo mkfs.fat -F 32 /dev/sdX1 (replace /dev/sdX1 with your card's partition) |
Once formatted, insert the card into the Adalogger FeatherWing before powering on the Feather M4 CAN. The firmware will automatically create log files on the next power-up.
No card inserted? The web dashboard's Start Log and Stop Log buttons will be disabled and a red "SD: No Card" indicator will appear in the Live Data toolbar. CAN frames are still streamed live to the browser — only SD logging is unavailable.
Filling up? Use the web app's Configuration → SD Card Management → Format SD Card button to erase all logs and start fresh, or click Enter USB Storage Mode to mount the card as a USB drive on your computer and manage files directly.
| File | Description |
|---|---|
/config.json |
Device configuration (auto-created on first config command) |
/session_YYYYMMDD_HHMMSS.csv |
Human-readable log — one row per frame |
/session_YYYYMMDD_HHMMSS.can |
Binary log — 24 bytes per frame, magic header OBD2CAN\0 |
timestamp_ms,id_hex,id_dec,extended,fd,dlc,data_hex,data_bytes
0,000007E8,2024,0,0,8,04 41 0C 1A F8 00 00 00,8
4,000007E8,2024,0,0,8,04 41 0D 62 00 00 00 00,8
Send commands as JSON terminated by \n:
The device sends frames as compact JSON:
{"t":1234,"id":"0x7E8","ext":0,"fd":0,"dlc":8,"d":"04 41 0C 1A F8 00 00 00"}{
"bitrate": 500000,
"logcsv": true,
"logbin": true,
"stream": true,
"session": "mycar",
"filter": []
}Set "filter": [0x7E8, 0x7DF] to only log those IDs. Leave empty [] to capture everything.
Open webapp/index.html in Google Chrome or Microsoft Edge (Web Serial API required).
No server, no Node.js, no installation — it's a single self-contained HTML file.
| Tab | Description |
|---|---|
| 📡 Live Data | Real-time gauge dashboard + scrolling frame table. Filter by CAN ID. OBD2 PIDs auto-decoded. |
| 🗺️ Map Reconstruction | Live scatter plots: TPS/RPM, Fuel Trim/RPM, Ignition Advance/RPM, MAF/RPM, MAP/RPM, Speed/RPM. Export as JSON. |
| 📋 DBC Decoder | Load any .dbc file (drag & drop) or use built-in OBD2 PID table. Shows live decoded signal values. |
| 💾 Log Viewer | Open CSV logs from the SD card. Searchable/filterable. |
| ⚙️ Configuration | Configure bitrate, session name, log format, ID filters. Apply directly to device. |
| 🖥️ Serial Console | Raw JSON command terminal for direct device communication. |
- Plug Feather M4 CAN into your computer via USB
- Open
webapp/index.htmlin Chrome - Click Connect USB
- Select the correct COM port
- Live data begins streaming immediately
This project uses the opendbc database to decode manufacturer-specific CAN messages.
# Clone opendbc
git clone https://github.com/commaai/opendbc.git
# Find your car's DBC file
ls opendbc/opendbc/dbc/ | grep -i toyota
ls opendbc/opendbc/dbc/ | grep -i honda
# Copy to dbc/ folder in this project
cp opendbc/opendbc/dbc/toyota_nodsu_pt_generated.dbc ./dbc/Then drag the .dbc file into the DBC Decoder tab of the web app.
- Standard OBD2 PIDs (Mode 01): Built-in — RPM, speed, TPS, ECT, MAF, MAP, fuel trims, O2 sensors, ignition advance
- Manufacturer-specific CAN (via DBC): Engine maps, transmission data, ADAS data, EV battery data
- CANFD frames: Logged and streamed; DBC decoding uses the first 8 data bytes
The web app reconstructs factory maps by correlating live sensor readings:
| Map | X-Axis | Y-Axis | What It Tells You |
|---|---|---|---|
| Throttle Map | RPM | TPS % | Throttle body characterization |
| Fuel Trim Map | RPM | STFT/LTFT % | How much the ECU is correcting fueling |
| Ignition Map | RPM | Ignition Advance ° | Spark timing at different RPM |
| MAF Map | RPM | MAF g/s | Airflow at different RPM (volumetric efficiency proxy) |
| MAP Pressure | RPM | Intake MAP kPa | Manifold vacuum/boost curve |
| Speed/RPM | RPM | Speed km/h | Gear ratio estimation |
To capture representative maps:
- Cold start and idle warmup (captures idle fuel trims)
- Gentle acceleration runs 1000–6000 RPM in each gear
- Cruise at steady speeds (30, 60, 100 km/h)
- Deceleration / overrun (captures decel fuel cut)
- Wide Open Throttle run (optional — captures WOT fueling)
Export the map data JSON after the drive for offline analysis.
The binary log is optimized for post-processing. Each file starts with an 8-byte magic header followed by 24-byte records:
Magic: "OBD2CAN\0" (8 bytes)
Per-frame record (24 bytes):
Offset Size Field
0 4 timestamp_ms (uint32, ms since session start)
4 4 id (uint32, CAN arbitration ID)
8 1 dlc (data length code)
9 1 flags (bit0=FD, bit1=BRS, bit2=Extended ID)
10 2 padding
12 8 data (first 8 bytes; FD frames truncated to 8)
Parse with Python:
import struct
with open('session.can', 'rb') as f:
magic = f.read(8)
assert magic == b'OBD2CAN\x00'
while True:
rec = f.read(24)
if len(rec) < 24: break
ts, id_, dlc, flags, _, _, *data = struct.unpack('<IIBB2B8B', rec)
print(f"t={ts}ms id=0x{id_:X} dlc={dlc} data={bytes(data[:dlc]).hex()}")- OBD2 PID poller (auto-query Mode 01 PIDs at configurable rate)
- 3D surface map visualization (RPM × Load → value)
- Compare two log sessions side-by-side
- Export maps to CSV / MegaTune format
- Automatic gear detection from speed/RPM ratio
- CANFD full 64-byte data logging
- Wi-Fi streaming (with ESP32 co-processor or Feather variant)
- opendbc fingerprinting (auto-identify car model from CAN traffic)
- ACANFD_FeatherM4CAN — Pierre Molinaro — CAN driver
- opendbc — comma.ai — DBC database & car CAN decoding
- Adafruit Feather M4 CAN — Hardware documentation
- Betaflight Configurator — UI inspiration
MIT License — see LICENSE