← All projects

Project 02 · 2026

CAN-Bus Sniffer and RPM Decoding

A toolset that listens to a vehicle's CAN traffic through the OBD-II port and reverse-engineers the manufacturer-specific engine RPM signal.

Role
Sole developer
Hardware
CANable V2.1
Software
Python, python-can
Source
GitHub ↗

02.1Summary

Inside a vehicle, control units talk to each other over a CAN bus. Standard OBD-II queries return some values, but manufacturers broadcast most of the real data in their own messages, and the meaning of those messages is undocumented.

In this project I connected to the OBD-II port of a Toyota Hilux in listen-only mode, logged raw frames and compared recordings taken at different engine speeds to find the RPM signal: CAN ID 0x2C4, first two bytes big-endian, scale ×0.8. I then wrote a tachometer app that shows this signal live.

02.2Block diagram

CAN sniffing chain block diagram Vehicle ECUs control units OBD-II port pin 6 / 14 / 5 CAN · 500 kbit/s CANable V2.1 SLCAN · listen-only CAN canable_logger.py frames → CSV USB rpm_analyzer.py calibration CSV rpm_gauge.py tachometer 0x2C4 rpm_live.py terminal RPM

02.3Specifications

AdapterCANable V2.1 (stok SLCAN firmware)
ModeM1 silent / listen-only: no frames are transmitted
Bitrate500 kbit/s (250 kbit/s selectable)
WiringCANH → OBD pin 6 · CANL → pin 14 · GND → pin 4/5
TerminationOff: the vehicle bus is already terminated
Log formatCSV (session_id, timestamp, can_id, data_hex, raw_line)
Decoded signal0x2C4 · D0–D1 big-endian · RPM = raw × 0.8

02.4Method

To find the RPM signal I took three short recordings with the vehicle stationary, in neutral, handbrake on: idle, 1500 and 2500 rpm. The actual engine speed read from the cluster or a diagnostic tool was used as reference for each.

python3 canable_logger.py --duration 15 --output data/rpm_idle.csv
python3 canable_logger.py --duration 15 --output data/rpm_1500.csv
python3 canable_logger.py --duration 15 --output data/rpm_2500.csv

python3 rpm_analyzer.py calibrate \
  --sample data/rpm_idle.csv=750 \
  --sample data/rpm_1500.csv=1500 \
  --sample data/rpm_2500.csv=2500

rpm_analyzer.py scans candidate 16-bit fields (start byte, endianness) for every CAN ID and ranks the candidate that gives the most consistent linear fit (scale, offset) against the reference speeds. If the log contains a standard OBD reply 41 0C A B, the formula ((A × 256) + B) / 4 is used directly.

Byte layout of frame 0x2C4 CAN ID 0x2C4 · DLC 8 · sample frame 03 D0 C0 D1 00 D2 00 D3 00 D4 00 D5 00 D6 00 D7 raw = (D0 << 8) | D1 = 0x03C0 = 960 RPM = raw × 0.8 = 768 D2–D7: not used in this work

02.5Software

canable_logger.pyShows raw frames live and appends them to CSV. Each run creates a new session id.
rpm_analyzer.pyScans logs, ranks RPM candidates and calibrates them.
rpm_live.pyPrints only the engine speed in the terminal.
rpm_gauge.pyAnalog/digital tachometer. Scale is adjustable live; recorded CSVs can be replayed.
CANalyzerA SavvyCAN-like general analyzer: live/trace views, DBC decoding, filters, plots, periodic transmit, CSV/ASC/JSON logs.

Sample terminal output:

     1.242s  RPM:   768.0  raw: 03 C0 00 00 00 00 00 00

CANalyzer's Dashboard tab combines the tachometer, the throttle pedal signal and a virtual throttle pedal for bench testing:

CANalyzer Dashboard: Gauge (0x2C4, ×0.8) and throttle pedal (0x2C1) configuration, virtual pedal and EMERGENCY STOP.
Fig. 3CANalyzer Dashboard — Gauge (0x2C4, ×0.8) and throttle pedal (0x2C1) configuration, virtual pedal and EMERGENCY STOP.
Filter panel: Filtering by direction (RX/TX), ID range and an ID list.
Fig. 4Filter panel — Filtering by direction (RX/TX), ID range and an ID list.
Fig. 1Bench test — Connecting to the CANable and sending 0x2C1 frames every 32 ms with the virtual throttle pedal.
Fig. 2Interface tour — Connection, send (including OBD-II presets) and filter tabs.

02.6Results and lessons

  • The manufacturer broadcast (0x2C4) arrives continuously, so it suits live monitoring better than OBD polling: no request ever has to be sent on the bus.
  • Safety first when connecting to a vehicle: the adapter opens in listen-only mode, and neither its 5V output nor its termination resistor is used.
  • If there is a gateway behind the OBD port, some in-vehicle networks may not be visible, so the first step is always to log what is actually there.