Arduino CAN Bus: Reading Vehicle Data
What CAN Bus Actually Is
Controller Area Network — the two-wire (CAN-H, CAN-L) differential bus that's been standard in vehicles since the mid-90s (mandatory in the US since 2008) for ECUs, sensors, and modules to talk to each other. OBD-II's pins 6 and 14 on the standard connector are exactly this CAN-H/CAN-L pair on most modern vehicles.
Hardware: MCP2515 CAN Module
Arduino has no native CAN controller — you need an MCP2515-based CAN bus module (SPI interface, widely available, cheap) between the Arduino and the vehicle's CAN bus.
- MCP2515 module VCC → 5V, GND → GND
- SPI pins (CS, SO, SI, SCK) → Arduino's SPI pins (10, 12, 11, 13 on Uno)
- CAN-H/CAN-L → vehicle's OBD-II port pins 6/14, or directly onto the bus wiring if you're tapping in elsewhere
Library + Basic Read
#include <SPI.h> #include <mcp2515.h> MCP2515 mcp2515(10); // CS pin void setup() { Serial.begin(115200); mcp2515.reset(); mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); // most vehicles use 500kbps mcp2515.setNormalMode(); } void loop() { struct can_frame frame; if (mcp2515.readMessage(&frame) == MCP2515::ERROR_OK) { Serial.print(frame.can_id, HEX); Serial.print(" "); for (int i = 0; i < frame.can_dlc; i++) { Serial.print(frame.data[i], HEX); Serial.print(" "); } Serial.println(); } }You'll See Raw IDs, Not Labels
What you get back is raw CAN IDs and byte payloads — there's no universal "this ID always means RPM" standard across manufacturers outside the standardized OBD-II PID request/response protocol. For OBD-II-standard data (RPM, coolant temp, speed), you actually want to send a specific request frame (ID 0x7DF, PID in the data payload) and parse the standardized response, rather than passively sniffing broadcast traffic.
Requesting a Standard OBD-II PID (Example: RPM)
struct can_frame request; request.can_id = 0x7DF; request.can_dlc = 8; request.data[0] = 0x02; request.data[1] = 0x01; // mode 01: show current data request.data[2] = 0x0C; // PID 0x0C: engine RPM mcp2515.sendMessage(&request); // response comes back on 0x7E8, parse bytes per the OBD-II PID specManufacturer-Specific / Passive Sniffing
For non-OBD-II data (things like individual door status, specific manufacturer diagnostics), you're reverse-engineering — passively logging all traffic while triggering the behavior you're interested in (open a door, watching for a new/changing CAN ID), then correlating. Tools like SavvyCAN on a computer (fed via a USB-CAN adapter) make this reverse-engineering process much easier than parsing raw serial output by eye.
Safety Note
Passive read-only sniffing is safe. Sending frames onto a live vehicle's CAN bus can trigger real actuators (this is literally how remote-start and some ECU tuning tools work) — don't send frames you don't understand the effect of, and never do this kind of experimentation while the vehicle is in motion or in gear.
Related Guides
- CAN Bus for Makers: ESP32 TWAI and MCP2515 Wiring, Termination, and Message Design
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- How to Use the ESP32-CAM: Video Streaming, Motion Detection, and Time-Lapse
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- How to Use Sensors with Arduino and ESP32: Temperature, Distance, Load, Current, and Hall Effect
- How to Control Motors with Arduino and ESP32: Stepper, DC, and Servo Drivers
- How to Inspect a Used Car Before Buying with the Anyscan A30M
- How to Use Bi-Directional Control and Active Testing with the Anyscan A30M