Add initial project structure for CAN Bus Reader

This commit is contained in:
2026-02-28 19:40:27 +01:00
commit ab827ee875
8 changed files with 334 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

104
README.md Normal file
View File

@@ -0,0 +1,104 @@
# CAN Bus Reader for Renault Clio 3
ESP32-based CAN bus sniffer that reads all packets from a Renault Clio 3 2012 via OBD2, with decoded output for wheel speed, RPM, and other vehicle data.
**Target vehicle**: Renault Clio 3 2012 1.2 16V
**Purpose**: Read odometer and RPM (not available via standard OBD2 PIDs) by sniffing the CAN bus.
## Hardware
| Component | Model |
|-----------|-------|
| MCU | ESP32 DOIT DevKit V1 |
| CAN Transceiver | SN65HVD230 VP230 (3.3V) |
| Connection | OBD2 connector pins 6, 14, GND |
## Wiring
### OBD2 to VP230
| OBD2 Pin | Signal | Connect To |
|----------|--------|------------|
| 6 | CAN High | VP230 CAN_H |
| 14 | CAN Low | VP230 CAN_L |
| 4 or 5 | GND | VP230 GND |
### VP230 to ESP32
| VP230 Pin | ESP32 Pin |
|-----------|-----------|
| TX (or D) | D33 (GPIO 33) |
| RX (or R) | D32 (GPIO 32) |
| VCC | 3.3V |
| GND | GND |
### Diagram
```
OBD2 Port VP230 Module ESP32
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Pin 6 │──────────────────│ CAN_H │ │ │
│ Pin 14 │──────────────────│ CAN_L │ │ │
│ Pin 4/5 │──────┬───────────│ GND │──────────────────│ GND │
└─────────┘ │ │ TX │──────────────────│ D33 │
│ │ RX │──────────────────│ D32 │
│ │ VCC │──────────────────│ 3.3V │
└───────────│ GND │ └─────────┘
└─────────┘
```
**Note**: Power the ESP32 via USB. Do not connect OBD2 pin 16 (+12V) to the ESP32.
## Build & Upload
```bash
pio run -t upload
```
## Usage
1. Connect hardware as above
2. Plug into vehicle OBD2 port (under dashboard, driver side)
3. Turn ignition ON (engine running for full data)
4. Open serial monitor at **115200 baud**
## Output Format
**Raw frames** (all received):
```
ID:0x29A LEN:7 DATA:00 00 1A 00 00 00 00
```
**Decoded values** (when ID is known):
```
ID:0x29A [WheelSpeed] FL:12.5 km/h FR:12.3 km/h
ID:0x29C [WheelSpeed] RL:12.4 km/h RR:12.6 km/h
ID:0x??? [Engine] RPM: 1850
```
## Decoded CAN IDs (Renault Clio 3 / Megane 3)
| ID | Signal | Bytes | Formula |
|----|--------|-------|---------|
| 0x29A (666) | Wheel speed front | 0-1: FR, 2-3: FL | (byte[1]<<8\|byte[0]) * 0.005 km/h |
| 0x29C (668) | Wheel speed rear | 0-1: RR, 2-3: RL | (byte[1]<<8\|byte[0]) * 0.005 km/h |
| 0x230 | Engine RPM (tentative) | 2-3 | raw/4 |
| 0x2D4 | Vehicle speed (tentative) | 0-1 | raw * 0.01 km/h |
To add odometer or fix RPM/speed: edit the `#define` IDs and `decode_renault()` switch cases in `main.cpp`.
RPM and odometer IDs may need tuning. If decoded values look wrong:
- **RPM**: Rev the engine and watch which IDs change; adjust `ID_ENGINE_RPM` and byte/factor in `decode_renault()`
- **Odometer**: Drive a known distance; find IDs that increment slowly; add a new case in `decode_renault()`
## CAN Bus Speed
- **500 kbps** – Clio 3 main CAN (pins 6/14)
- **250 kbps** – Try if no traffic at 500 kbps (motor diagnostic CAN)
Change `CAN_SPEED_KBPS` in `main.cpp` if needed.
## References
- [Renault OBD2 Pinout](https://pinoutguide.com/CarElectronics/renault_obd2_diag_pinout.shtml)
- [Renault Megane 3 RS DBC](https://github.com/krsche/renault-megane-3-rs-can-dbc)

37
include/README Normal file
View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

16
platformio.ini Normal file
View File

@@ -0,0 +1,16 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_speed = 115200
lib_deps = https://github.com/handmade0octopus/ESP32-TWAI-CAN.git

105
src/main.cpp Normal file
View File

@@ -0,0 +1,105 @@
/**
* CAN Bus Sniffer for Renault Clio 3 2012
* Reads all CAN packets via OBD2 pins 6 (CAN_H) and 14 (CAN_L)
* Target: odometer and RPM (not available via OBD2 PIDs)
*/
#include <Arduino.h>
#include <ESP32-TWAI-CAN.hpp>
// SN65HVD230 VP230: TX=D33, RX=D32 (per hardware)
#define CAN_TX_GPIO 33
#define CAN_RX_GPIO 32
#define CAN_SPEED_KBPS 500
void decode_renault(const CanFrame& frame);
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("CAN Bus Sniffer - Renault Clio 3");
Serial.println("-------------------------------");
// Initialize TWAI with 500 kbps, RX=GPIO32, TX=GPIO33
if (ESP32Can.begin(ESP32Can.convertSpeed(CAN_SPEED_KBPS), CAN_TX_GPIO, CAN_RX_GPIO, 10, 20)) {
Serial.println("CAN bus started at 500 kbps");
} else {
Serial.println("CAN bus init FAILED - check wiring");
}
}
void loop() {
CanFrame rxFrame;
// Non-blocking read (timeout 0)
if (ESP32Can.readFrame(rxFrame, 0)) {
// candump-style output: ID:0xXXX LEN:X DATA:XX XX XX XX XX XX XX XX
Serial.print("ID:0x");
Serial.print(rxFrame.identifier, HEX);
Serial.print(" LEN:");
Serial.print(rxFrame.data_length_code);
Serial.print(" DATA:");
for (int i = 0; i < rxFrame.data_length_code && i < 8; i++) {
if (rxFrame.data[i] < 0x10) Serial.print("0");
Serial.print(rxFrame.data[i], HEX);
if (i < rxFrame.data_length_code - 1) Serial.print(" ");
}
Serial.println();
// Optional: decode known Renault IDs (add as reverse-engineered)
decode_renault(rxFrame);
}
}
// Known Renault CAN IDs (Megane 3 RS / Clio 3 platform)
#define ID_WHEEL_SPEED_FRONT 0x29A // 666 - FL, FR
#define ID_WHEEL_SPEED_REAR 0x29C // 668 - RL, RR
#define ID_ENGINE_RPM 0x230 // Common Renault/PSA engine broadcast (verify on your car)
#define ID_VEHICLE_SPEED 0x2D4 // Possible speed/odometer (verify on your car)
// Decoder for Renault Clio 3 - outputs human-readable values
void decode_renault(const CanFrame& frame) {
const uint8_t* d = frame.data;
uint8_t len = frame.data_length_code;
switch (frame.identifier) {
case ID_WHEEL_SPEED_FRONT:
if (len >= 4) {
uint16_t fr = (uint16_t)(d[1] << 8) | d[0];
uint16_t fl = (uint16_t)(d[3] << 8) | d[2];
float fr_kmh = fr * 0.005f;
float fl_kmh = fl * 0.005f;
Serial.printf(" -> [WheelSpeed Front] FL:%.1f km/h FR:%.1f km/h\n", fl_kmh, fr_kmh);
}
break;
case ID_WHEEL_SPEED_REAR:
if (len >= 4) {
uint16_t rr = (uint16_t)(d[1] << 8) | d[0];
uint16_t rl = (uint16_t)(d[3] << 8) | d[2];
float rr_kmh = rr * 0.005f;
float rl_kmh = rl * 0.005f;
Serial.printf(" -> [WheelSpeed Rear] RL:%.1f km/h RR:%.1f km/h\n", rl_kmh, rr_kmh);
}
break;
case ID_ENGINE_RPM:
if (len >= 4) {
uint16_t raw = (uint16_t)(d[3] << 8) | d[2];
uint16_t rpm = raw / 4;
if (rpm < 10000) Serial.printf(" -> [Engine] RPM: %u\n", rpm);
}
break;
case ID_VEHICLE_SPEED:
if (len >= 2) {
uint16_t raw = (uint16_t)(d[1] << 8) | d[0];
float kmh = raw * 0.01f;
if (kmh < 300) Serial.printf(" -> [Speed] %.1f km/h\n", kmh);
}
break;
default:
break;
}
}

11
test/README Normal file
View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html