Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Sensors & Modules

OBD-II ELM327: Read Car Diagnostic Codes with Arduino

OBD-II ELM327: Read Car Diagnostic Codes with Arduino

March 11, 2026 /Posted byJayesh Jain / 0

Table of Contents

  • What Is OBD-II and Why Does It Matter?
  • The ELM327 IC — Heart of the Adapter
  • Hardware You Need
  • Wiring ELM327 UART Module to Arduino
  • Understanding AT Commands
  • Arduino Code to Read DTCs and Live Data
  • How to Decode Diagnostic Trouble Codes
  • Reading Live PID Data (Speed, RPM, Temp)
  • Advanced OBD-II Project Ideas
  • Recommended Products from Zbotic
  • Frequently Asked Questions

Every car manufactured after 2000 in India (and after 1996 in the USA) is legally required to carry an OBD-II port — a standardised diagnostic interface that lets technicians and engineers peer inside the vehicle’s electronic brain. The same port that a dealership uses to diagnose a mysterious check-engine light is accessible to you, a hobbyist, with a cheap ELM327 adapter and an Arduino.

In this detailed guide you will learn how the OBD-II system works, how the ELM327 IC translates raw CAN-bus data into simple AT commands, how to wire a UART ELM327 module to an Arduino, and how to write firmware that reads Diagnostic Trouble Codes (DTCs) and live engine data such as RPM, speed, coolant temperature, and throttle position. Whether you are building a custom dashboard, a trip computer, or just want to clear a pesky check-engine light without paying ₹500 at a garage, this guide has you covered.

What Is OBD-II and Why Does It Matter?

OBD stands for On-Board Diagnostics. The second-generation standard, OBD-II (also written OBD2), was introduced in 1996 in the US and has been mandatory in India for BS-VI vehicles since 2020. It defines:

  • A standardised connector: The 16-pin J1962 (trapezoidal) port, always within 60 cm of the steering wheel
  • Communication protocols: ISO 9141-2, ISO 14230 (KWP2000), ISO 15765 (CAN), J1850 PWM/VPW
  • A set of standard PIDs (Parameter IDs): Codes that request specific engine data
  • Diagnostic Trouble Codes (DTCs): Standardised fault codes beginning with P, B, C, or U

Modern vehicles (post-2008 for most, post-2015 for India) almost exclusively use the ISO 15765-4 CAN protocol at 500 kbps, which is what we focus on here.

The ELM327 IC — Heart of the Adapter

The ELM327 is a PIC-based microcontroller programmed by Elm Electronics to act as an OBD-II to RS-232 (serial) interpreter. It speaks every OBD-II protocol and presents a simple AT command interface over serial — similar to old-school Hayes modem commands.

ELM327 Module Form Factors

  • Bluetooth ELM327 dongle: Plugs into OBD-II port, pairs with phone/PC. NOT usable directly with Arduino without a Bluetooth module.
  • Wi-Fi ELM327 dongle: Same, but TCP/IP. More useful for ESP8266/ESP32.
  • UART/Serial ELM327 breakout module: Has TX/RX pins — plugs directly into Arduino. This is what we use in this tutorial.

For Arduino projects, you want the UART serial breakout version (sometimes sold as "ELM327 Serial Module" or "OBD-II to UART"). It typically costs ₹200–₹500 and includes the ELM327 IC, crystal, and a mini OBD connector or wiring harness.

Hardware You Need

  • 1× Arduino Mega (recommended) or Uno
  • 1× ELM327 UART serial module (with OBD-II connector)
  • 1× OBD-II extension cable (optional, for easier bench testing)
  • 1× 16×2 LCD with I2C module (for displaying results in the car)
  • Jumper wires
  • A vehicle with an OBD-II port (any BS-VI car, or older EFI vehicles)
INA219 Current Monitor

CJMCU-219 INA219 I2C Bi-directional Current Monitor

Pair with your OBD-II project to also monitor the 12V car battery current drain from your Arduino circuit — ideal for keeping tabs on power consumption in a car build.

View on Zbotic

Wiring ELM327 UART Module to Arduino

The ELM327 UART module communicates at 3.3 V or 5 V TTL serial (check your module datasheet). Most modules are 5 V compatible:

ELM327 Module Pin Arduino Mega Pin
VCC (5V) 5V
GND GND
TX (module transmits) RX1 (pin 19)
RX (module receives) TX1 (pin 18)

On Arduino Uno, use the SoftwareSerial library on pins D10 (RX) and D11 (TX) instead, keeping the hardware serial for USB debugging.

Understanding AT Commands

ELM327 uses a simple text-based protocol. You send commands as ASCII strings terminated with a carriage return (r). Key commands:

Command Function
ATZ Reset ELM327
ATE0 Echo off (cleaner output)
ATL0 Linefeeds off
ATSP0 Auto-detect protocol
03 Request stored DTCs (Mode 03)
04 Clear DTCs / reset MIL (Mode 04)
010C Read engine RPM (Mode 01 PID 0C)
010D Read vehicle speed (Mode 01 PID 0D)
0105 Read coolant temperature (Mode 01 PID 05)

Arduino Code to Read DTCs and Live Data

Initialisation and DTC Reading

// OBD-II ELM327 DTC Reader
// Zbotic.in Tutorial
// Arduino Mega — ELM327 on Serial1

#include <Arduino.h>

HardwareSerial &elm = Serial1; // ELM327 on pins 18/19

String sendCommand(String cmd, unsigned long timeout = 2000) {
  elm.print(cmd + "r");
  String response = "";
  unsigned long start = millis();
  while (millis() - start < timeout) {
    while (elm.available()) {
      char c = elm.read();
      if (c != 'r') response += c;
    }
    if (response.endsWith(">")) break; // ELM327 prompt
  }
  return response;
}

bool initELM() {
  delay(1000);
  sendCommand("ATZ");   // Reset
  delay(1500);
  sendCommand("ATE0");  // Echo off
  sendCommand("ATL0");  // Linefeeds off
  sendCommand("ATH0");  // Headers off
  sendCommand("ATS0");  // Spaces off
  String resp = sendCommand("ATSP0"); // Auto protocol
  return resp.length() > 0;
}

void readDTCs() {
  Serial.println("n--- Requesting Stored DTCs (Mode 03) ---");
  String resp = sendCommand("03", 5000);
  resp.trim();
  Serial.println("Raw: " + resp);

  if (resp == "NODATA" || resp.length() == 0) {
    Serial.println("No DTCs stored — engine is healthy!");
    return;
  }

  // Parse response: each DTC is 4 hex chars after header bytes
  // Format: 43 XX XX XX XX XX (Mode 43 = response to 03)
  // First byte after 43 is count, then 2 bytes per DTC
  int idx = resp.indexOf("43");
  if (idx < 0) { Serial.println("Unexpected response"); return; }
  String data = resp.substring(idx + 2);
  int count = strtol(data.substring(0, 2).c_str(), NULL, 16);
  Serial.print("DTC Count: "); Serial.println(count);

  for (int i = 0; i < count; i++) {
    String dtcHex = data.substring(2 + i * 4, 6 + i * 4);
    byte b1 = strtol(dtcHex.substring(0, 2).c_str(), NULL, 16);
    byte b2 = strtol(dtcHex.substring(2, 4).c_str(), NULL, 16);
    // Decode DTC type from high nibble of b1
    char type;
    switch ((b1 & 0xC0) >> 6) {
      case 0: type = 'P'; break; // Powertrain
      case 1: type = 'C'; break; // Chassis
      case 2: type = 'B'; break; // Body
      case 3: type = 'U'; break; // Network
      default: type = 'P';
    }
    char dtc[8];
    sprintf(dtc, "%c%01X%02X", type, b1 & 0x3F, b2);
    Serial.print("DTC: "); Serial.println(dtc);
  }
}

void setup() {
  Serial.begin(115200);
  elm.begin(38400); // ELM327 default baud
  Serial.println("OBD-II ELM327 Reader - Zbotic.in");
  if (initELM()) Serial.println("ELM327 initialised OK");
  else Serial.println("ELM327 init failed — check wiring");
  readDTCs();
}

void loop() {} // DTCs read once at startup

How to Decode Diagnostic Trouble Codes

DTCs follow a standard 5-character format: X####

  • First character: System — P (Powertrain), B (Body), C (Chassis), U (Network)
  • Second digit: 0 = SAE standard, 1 = manufacturer-specific
  • Third digit: Subsystem (0=fuel/air, 1=fuel/air metering, 2=injector, 3=ignition, 4=emissions, 5=speed/idle, 6=computer, 7=transmission, 8=transmission)
  • Last two digits: Specific fault code

Common DTC Examples

DTC Meaning
P0300 Random/multiple cylinder misfire
P0420 Catalyst system efficiency below threshold (Bank 1)
P0171 System too lean (Bank 1)
P0442 Small evaporative emission system leak
P0113 Intake air temperature sensor high input

Reading Live PID Data (Speed, RPM, Temperature)

// Live OBD-II Data Reader
// Add this to loop() after ELM init

int readPID(String pid) {
  String resp = sendCommand("01" + pid, 1000);
  resp.trim();
  // Remove echo and find hex data after "41 XX"
  int idx = resp.indexOf("41");
  if (idx < 0) return -1;
  String data = resp.substring(idx + 4); // skip "41 XX"
  data.trim();
  if (data.length() < 2) return -1;
  return strtol(data.substring(0, 2).c_str(), NULL, 16);
}

void readLiveData() {
  // RPM (PID 0C): value = (A*256 + B) / 4
  String rpmResp = sendCommand("010C", 1000);
  // Parse two bytes
  // ... (full parsing function included in download)

  // Speed (PID 0D): value = A km/h
  int speed = readPID("0D");
  Serial.print("Speed: "); Serial.print(speed); Serial.println(" km/h");

  // Coolant Temp (PID 05): value = A - 40 degC
  int rawTemp = readPID("05");
  int temp = rawTemp - 40;
  Serial.print("Coolant Temp: "); Serial.print(temp); Serial.println(" C");

  // Throttle Position (PID 11): value = A * 100 / 255 %
  int rawThrottle = readPID("11");
  int throttle = (rawThrottle * 100) / 255;
  Serial.print("Throttle: "); Serial.print(throttle); Serial.println(" %");
}

Advanced OBD-II Project Ideas

1. Custom Digital Dashboard on TFT Display

Drive a 2.4-inch TFT SPI display showing real-time RPM (as a bar graph), speed, coolant temperature, and fuel trim. Use an ESP32 for Wi-Fi OTA firmware updates.

2. Eco-Driving Score Logger

Log throttle position, RPM, and speed to an SD card every second. Post-process in Python to calculate eco-driving scores — useful for fleet management or fuel efficiency analysis.

3. Automatic DTC Cloud Reporter

Use ESP8266 to poll for DTCs every 10 minutes and push alerts to a Telegram bot or email when a new fault is detected. Never be caught off-guard by a check-engine light again.

4. Performance Timer (0-100 km/h)

Monitor speed PID at maximum polling rate, detect when speed crosses 0 km/h → 100 km/h, and display the elapsed time. Accurate to ~250 ms depending on CAN bus polling latency.

5. Long-Trip Fuel Efficiency Tracker

Read MAF sensor (PID 10) or Fuel Rail Pressure and calculate instantaneous fuel consumption. Compare with fuel pump top-ups for a real-world consumption vs OBD-calculated consumption study.

ACS712 30A Current Sensor

30A Range Current Sensor Module ACS712

Monitor your car battery’s current draw independently of OBD-II — combine with ELM327 data for a complete vehicle health monitoring system.

View on Zbotic

Recommended Products from Zbotic

ACS712 5A Current Sensor

5A Range Current Sensor Module ACS712

Ideal for monitoring the 5A-range accessories in your car project — pair with OBD-II data for complete electrical health monitoring.

View on Zbotic

BMP280 Sensor

BMP280 Barometric Pressure and Altitude Sensor

Add altitude and barometric pressure to your car’s data logger — useful for normalising fuel efficiency calculations based on atmospheric conditions.

View on Zbotic

Frequently Asked Questions

Q1: Will this damage my car’s ECU?

Reading OBD-II data is completely safe — the ECU is designed for this. The only mildly risky command is Mode 04 (clear DTCs), which resets fault codes and readiness monitors. After clearing, the car needs a drive cycle to re-learn. Never clear DTCs before a PUC/emission test as it will fail readiness checks.

Q2: My ELM327 responds “OK” but shows “UNABLE TO CONNECT” to OBD. Why?

The ELM327 connected to the Arduino is fine, but it cannot communicate with the car’s CAN bus. Try: (1) set protocol manually with ATSP6 (ISO 15765-4 CAN 500k 11-bit) — most modern Indian cars. (2) Ensure the car ignition is ON (not just accessory mode — some cars need engine cranked). (3) Check that you’re connected to pins 6 (CAN High) and 14 (CAN Low) on the OBD port.

Q3: Can I use a Bluetooth ELM327 dongle with Arduino?

Yes, by pairing it with an HC-05 Bluetooth module connected to Arduino. The ELM327 acts as a serial server; the HC-05 connects to it. However, this adds latency and complexity. A UART serial ELM327 module is simpler and more reliable for Arduino projects.

Q4: What vehicles are NOT supported?

Pre-2000 vehicles using OBD-I (not standardised), older two-wheelers, and some CNG/LPG conversions that removed the OBD port. Electric vehicles (like Nexon EV) technically have OBD-II but many EVs use proprietary PIDs beyond the standard set.

Q5: How fast can I poll OBD-II data?

CAN bus physical speed is 500 kbps, but ELM327 adds overhead. Expect 5–10 PIDs per second maximum. For higher speed, consider direct CAN bus access using a CAN shield (MCP2515-based) and bypass ELM327 altogether.

Q6: Is it legal to read my own car’s OBD data in India?

Absolutely. Reading your own vehicle’s diagnostic data is legal everywhere. The OBD-II port is mandated by law specifically to allow independent diagnostics. You own the car, you own the data.

Turn Your Car Into a Connected Data Source

With an ELM327 module and an Arduino, you have a professional-grade diagnostic tool for the cost of a sandwich. Read DTCs, monitor live engine data, log trips, and build the custom dashboard your car deserves. Get the components you need from Zbotic and start building today.

Shop Sensors & Modules at Zbotic

Tags: automotive electronics, car diagnostic Arduino, ELM327 tutorial, OBD-II ELM327, OBD2 scanner DIY
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
How to Use LDR Light Sensor wi...
blog how to use ldr light sensor with arduino darkness detection 596410
blog prusaslicer vs cura best slicer for beginners in 2026 596413
PrusaSlicer vs Cura: Best Slic...

Related posts

Svg%3E
Read more

Encoder Module: Position and Speed Measurement with Arduino

April 1, 2026 0
A rotary encoder converts the angular position and rotation speed of a shaft into electrical signals that Arduino can count... Continue reading
Svg%3E
Read more

Infrared Obstacle Sensor: Line Follower and Object Detection

April 1, 2026 0
Infrared obstacle sensors are the building blocks of line-following robots, edge detection systems, and proximity triggers. These tiny modules emit... Continue reading
Svg%3E
Read more

Rain Sensor and Raindrop Detection Module: Arduino Guide

April 1, 2026 0
A rain sensor module detects the presence of water droplets on its surface, giving Arduino a simple signal to trigger... Continue reading
Svg%3E
Read more

LiDAR Sensor TFmini: Distance Measurement Beyond Ultrasonic

April 1, 2026 0
When ultrasonic sensors hit their limits — range too short, accuracy too coarse, outdoor sunlight causing interference — LiDAR sensors... Continue reading
Svg%3E
Read more

Pressure Sensor BMP280: Weather Station and Altitude Meter

April 1, 2026 0
The BMP280 barometric pressure sensor by Bosch measures atmospheric pressure with ±1 hPa accuracy and temperature with ±1°C precision. These... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now