# esp_obd_ii
OBD-II vehicle data for ESP-IDF. Read live PIDs, supported-PID maps, and DTCs
from an **ELM327** adapter (Bluetooth Classic SPP **or** BLE) **or directly off
the CAN bus** via the on-chip **TWAI** controller. One shared decode layer, two
pluggable links, configured through `menuconfig`.
## Install
ESP Component Registry / GitHub:
```bash
idf.py add-dependency "lahirunirmalx/esp_obd_ii^0.1.0"
# or pin to the git repo:
idf.py add-dependency --git https://github.com/lahirunirmalx/esp-obd-ii.git \
--git-path components/esp_obd_ii
```
Or add to your `main/idf_component.yml`:
```yaml
dependencies:
esp_obd_ii:
git: https://github.com/lahirunirmalx/esp-obd-ii.git
path: components/esp_obd_ii
```
## Configure (`idf.py menuconfig` -> "ESP OBD-II")
- **Data link**: ELM327 adapter, or direct CAN (TWAI).
- **ELM327 transport**: BLE (NimBLE) or Classic SPP (Bluedroid). SPP is only
offered on chips with a Classic BT radio (the original ESP32).
- **BLE UUIDs**: default `FFF0`/`FFF1`/`FFF2`; some adapters use `18F0`/`2AF0`/
`2AF1` - verify with a BLE scanner and override.
- **CAN**: bitrate (default 500k), 11- vs 29-bit IDs, and TX/RX GPIO.
The chosen link also requires the matching Bluetooth host stack in your
`sdkconfig` (NimBLE for BLE, Bluedroid + SPP for Classic); CAN needs none.
## Usage
```c
#include "obd.h"
ESP_ERROR_CHECK(obd_begin() == HAL_OK ? ESP_OK : ESP_FAIL);
float rpm = 0, speed = 0;
if (obd_read_pid(OBD_PID_ENGINE_RPM, &rpm) == 1) ESP_LOGI("app", "rpm=%.0f", rpm);
if (obd_read_pid(OBD_PID_VEHICLE_SPEED, &speed) == 1) ESP_LOGI("app", "kmh=%.0f", speed);
uint8_t bitmap[4];
obd_read_supported(0x00, bitmap); // which PIDs the ECU answers
char dtcs[16][OBD_DTC_STR_LEN];
int n = obd_read_dtcs(OBD_MODE_STORED_DTC, dtcs, 16); // e.g. "P0301"
uint8_t raw[8]; size_t len;
obd_query_raw(0x01, 0x0C, raw, sizeof(raw), &len); // escape hatch
```
See [`examples/obd_demo`](examples/obd_demo) for a complete app.
## Hardware support
| Target | BLE | Classic SPP | Direct CAN (TWAI) |
|----------|:---:|:-----------:|:-----------------:|
| esp32 | yes | **yes** | yes |
| esp32s3 | yes | no | yes |
| esp32c3 | yes | no | yes |
| esp32c6 | yes | no | yes |
| esp32h2 | yes | no | yes |
| esp32s2 | no | no | yes (CAN only) |
- **Classic SPP needs a Bluetooth Classic (BR/EDR) radio - the original ESP32
only.** C3/S3/C6/H2 are BLE-only.
- **Direct CAN needs an external transceiver** (e.g. SN65HVD230 at 3.3V) on the
configured TX/RX GPIO. Wire CANH/CANL to OBD-II pins 6/14; the vehicle bus is
already 120-ohm terminated.
## Architecture
```
obd_pids / obd_dtc shared decode (PID formulas + DTCs) - pure, host-tested
| obd_link.h : request(mode,pid) -> response bytes (non-blocking)
+----+--------------------------+
elm327_link can_link
| hal_obd_transport | hal_can
SPP / BLE (port_*.cpp) TWAI (port_can_twai.cpp)
```
`include/` is the public API; `src/port_*.cpp` are the only files that include
`driver/twai.h` / Bluedroid / NimBLE headers.
## Status / caveat
The decode, ISO-TP, and ELM327 parsing layers are unit-tested on host. The
BLE/SPP scan/connect/subscribe flow is API-correct and builds for every target,
but has **not yet been validated against physical adapters on hardware** - treat
the connection path as beta until confirmed on your adapter. See
[CHANGELOG](CHANGELOG.md).
## License
MIT - see [LICENSE](LICENSE).
idf.py add-dependency "lahirunirmalx/esp_obd_ii^0.1.0"