Reverse engineering CAN network - eavesdropping on my car
Reverse-engineering a car internal CAN networks using modern ESP32-P4 hardware.
After owning my trusty Opel Vectra C (2002) for almost 7 years, I’ve always wanted to tap into its nervous system. You know the dream: pulling live data to display on a custom LCD, building a one-of-a-kind instrument cluster, or simply mapping the steering wheel buttons to control a DIY media player.
Unfortunately, due to a chronic lack of free time (and, let’s be honest, the steep learning curve), it remained a pipe dream. Until now.
The three-headed dragon: Vectra C CAN architecture
If you're diving into a 2002 Vectra C, you aren't just dealing with a single plug-and-play network. General Motors’ Epsilon platform uses a tiered network architecture to separate critical systems from comfort features.
Here is what the car is running:
| Network | Speed | Type | What lives here? |
|---|---|---|---|
| HS-CAN | 500 kbps | Dual-Wire Differential | Critical: Engine (ECU), ABS/TC, ESP, transmission. |
| MS-CAN | 95.2 kbps | Dual-Wire Differential | Infotainment: Media player, Electronic Climate Control (ECC), display. |
| LS-CAN | 33.3 kbps | Single-Wire (SWCAN) | Body/Comfort: Door status, locks, windows, steering wheel controls |
Knowing this, I needed a way to talk to three separate buses simultaneously.

Pin assignment
| Pin | Signal | Notes |
|---|---|---|
| 1 | LS-CAN H (SW) | Low-speed / single-wire CAN (GMLAN SW-CAN) |
| 3 | MS-CAN H | Mid-speed CAN high (~95 kbps) |
| 4 | GND | Chassis ground |
| 5 | GND | Signal ground |
| 6 | HS-CAN H | High-speed CAN high (ISO 15765-4, 500 kbps) |
| 11 | MS-CAN L | Mid-speed CAN low |
| 14 | HS-CAN L | High-speed CAN low |
| 16 | 12 V DC (BAT+) | Battery positive, ignition / permanent feed per vehicle |
Other pins are not used in this project.
Hardware choices
To read the data, we need transceivers. I initially chose the SN65HVD231 because of their affordable price, popularity, and 3.3V logic compatibility.
⚠️ A Note for Fellow Hackers: > The SN65HVD231 is a standard dual-wire differential transceiver. It works flawlessly for the High-Speed and Mid-Speed networks. However, the Vectra C's LS-CAN (33.3 kbps) is a Single-Wire CAN (SWCAN) network (also known as GMLAN). While you can hack a dual-wire transceiver to read single-wire data (usually by tying CAN-L to ground and tapping CAN-H to the single wire), it is highly prone to noise and bus errors. If you are building this from scratch, save yourself the debugging headaches and buy a dedicated SWCAN transceiver like the AU5790, TH3122, or NCV7356 for the low-speed bus!
Next came the harder choice: How do we process the data? Traditionally, makers use microcontrollers combined with external SPI CAN controllers (like the classic MCP2515). But I decided to step into the modern era and use the ESP32-P4.
I chose this specific chip because it features three built-in TWAI (Two-Wire Automotive Interface) controllers. According to the data sheets, this eliminates the bottleneck of external SPI controllers and allows direct connection to the three transceivers.
My hardware stack:
- Board: Guition JC4880P (ESP32-P4 based) with an integrated screen.
- Networking: An additional ESP32-C6 chip onboard handles WiFi and Bluetooth.
- Transceivers: 3x SN65HVD231 modules.

Software battles with ESP-IDF
Development was done using the ESP-IDF plugin for VSCode IDE (version 5.5.3). If you think wiring is hard, wait until you fight with the software.
Getting the built-in TWAI drivers to play nice required long battles with:
- Custom Bit Timing: The MS-CAN runs at an oddball 95.2 kbps. You can't just select a default speed preset; you have to calculate and set custom bit-timing configurations (BRP, TSEG1, TSEG2) in the TWAI driver.
- Controller Initialization: Mapping three separate TWAI instances to the correct RX/TX pins without triggering routing conflicts.
- Bus-Off States: Because of the single-wire transceiver hack mentioned earlier, the LS-CAN occasionally threw acknowledgment errors, forcing the TWAI controller into a "Bus-Off" state. Writing a robust recovery routine was mandatory to keep the system from freezing.
I also tried to update IDF to 6.0.2 but everything had gone to shit as BSP libraries refused to cooperate among other things.
One major physical hurdle I quickly discovered is that the standard OBD2 diagnostic socket under the dash doesn't give you access to all three networks (my car has most basic media player and display so MS-CAN wasn't probably necessary to use here).
While the Vectra C exposes both the HS-CAN and LS-CAN right at the diagnostic port, the MS-CAN is entirely absent—meaning if I want to read that infotainment data, I'll eventually have to splice into the wiring harness somewhere behind the radio or climate control panel.
Because of this, I’ve decided to focus my initial efforts entirely on the LS-CAN network. Not only is it easily accessible via the OBD port, but it also broadcasts the greatest amount of data I find immediately valuable for my project, like steering wheel button presses and physical switch statuses.
Success at last
After tracing wiring faults, endless debugging loops, digging through several GitHub repositories and of course running from my PC to my car a lot of times (should have tracked this - who knows, maybe it was half-marathon in total?), the magic finally happened.

We are finally reading live data - watching the car talk to itself in real time.
Now that the hardware and network layers are stable, the real fun begins: reverse-engineering the exact payloads to see what this car is really thinking, and eventually mapping those steering wheel buttons to my own applications. Stay tuned!