Go Down

Topic: ELMduino - Library for Car Hacking (Read 7408 times) previous topic - next topic

Power_Broker

May 25, 2019, 12:32 am Last Edit: Apr 02, 2020, 08:21 pm by Power_Broker
If you want to start car hacking through your OBD-II port using a bluetooth scanner - ELMduino is for you.

ELMduino download and GitHub Link

This library is also installable via the Arduino IDE's Libraries Manager.

You can use this library to interface with OBD-II scanners such as this common one and will work with any car that has an OBD-II port:



And you can use it to query any pieces of data as specified by the OBD-II public PIDs. Here is a list of PIDs supported. Some notables include speed, rpm, MAF pressure, etc.



Below is an example code that will print rpm data from a (running) car.

Code: [Select]
#include <SoftwareSerial.h>
#include "ELMduino.h"


SoftwareSerial mySerial(2, 3); // RX, TX
#define ELM_PORT mySerial


ELM327 myELM327;


uint32_t rpm = 0;


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  ELM_PORT.begin(115200);

  Serial.println("Attempting to connect to ELM327...");

  if (!myELM327.begin(ELM_PORT))
  {
    Serial.println("Couldn't connect to OBD scanner");
    while (1);
  }

  Serial.println("Connected to ELM327");
}


void loop()
{
  float tempRPM = myELM327.rpm();

  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (uint32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else
  {
    Serial.print(F("\tERROR: "));
    Serial.println(myELM327.status);
    delay(100);
  }
}



Note that you will need to connect to the bluetooth scanner using an HC-05 or other bluetooth to UART converter.

"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

mppstrit

#1
Apr 02, 2020, 07:57 pm Last Edit: Apr 02, 2020, 07:58 pm by mppstrit
Hi, can do PID2102 support? PID response example 2102 (A2 F1 11 61 2 A5 80 0 6F 0 0 0 D0 E8 D0 E8 0 0 0 0 0 0 0 0 0 0 0 0 0 1 7F 0 0 0 0 0 0 8B)

Power_Broker

You can query any PID supported by the ELM327. For custom queries, take a look at this example that prints soot level:

Code: [Select]
// modified test + my custom PIDs
// based purely on library

#include "BluetoothSerial.h"
#include "ELMduino.h"


#define ELM_PORT SerialBT
#define ESP_BLUETOOTH_NAME "ESP32"


BluetoothSerial SerialBT;
ELM327 myELM327;


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  ELM_PORT.begin(ESP_BLUETOOTH_NAME, true);

  Serial.println("Attempting to connect to ELM327...");

  if (!ELM_PORT.connect("V-LINK"))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 1");
    while (1);
  }

  if (!myELM327.begin(ELM_PORT))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 2");
    while (1);
  }
 
  Serial.println("Connected to ELM327");
 
  for (int a = 0; a < 10; ++a)
  {
    digitalWrite(LED_BUILTIN,HIGH); delay(100);
    digitalWrite(LED_BUILTIN,LOW); delay(100);
  }
}


void loop()
{
  int32_t rpm = -1;
  int32_t soot = -1;
  int32_t burn = -1;
  float litersLeft = -1;

  /////////////////////////////////////////////////////// RPM
  float tempRPM = myELM327.rpm();
 
  Serial.print("Payload received for rpm: ");
  for (byte i = 0; i < PAYLOAD_LEN; i++)
    Serial.write(myELM327.payload[i]);
  Serial.println();
 
  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (int32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else
      printError();

  /////////////////////////////////////////////////////// Soot
  if (myELM327.queryPID(34, 13162))
  {
    int32_t tempSoot = myELM327.findResponse();

    Serial.print("Payload received for soot: ");
    for (byte i = 0; i < PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
   
    if (myELM327.status == ELM_SUCCESS)
    {
      soot = tempSoot;
      Serial.print("Soot: "); Serial.println(soot);
    }
    else
      printError();
  }

  /////////////////////////////////////////////////////// Liters
  if (myELM327.queryPID(34, 4906))
  {
    int32_t tempLitersLeft = myELM327.findResponse() / 64.0;

    Serial.print("Payload received for liters: ");
    for (byte i = 0; i < PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();

    if (myELM327.status == ELM_SUCCESS)
    {
      litersLeft = tempLitersLeft;
      Serial.print("Liters: "); Serial.println(litersLeft);
    }
    else
      printError();
  }

  /////////////////////////////////////////////////////// Burns
  if (myELM327.queryPID(34, 8434))
  {
    int32_t tempBurn = myELM327.findResponse();

    Serial.print("Payload received for DPF burns: ");
    for (byte i = 0; i < PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();

    if (myELM327.status == ELM_SUCCESS)
    {
      burn = tempBurn;
      Serial.print("DPF burns: "); Serial.println(burn);
    }
    else
      printError();
  }

  for (int a = 0; a < 5; ++a)
  {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(200);
    digitalWrite(LED_BUILTIN,LOW);
    delay(200);
  }
}


void printError()
{
  if (myELM327.status == ELM_SUCCESS)
    Serial.println(F("\tELM_SUCCESS"));
  else if (myELM327.status == ELM_NO_RESPONSE)
    Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
  else if (myELM327.status == ELM_BUFFER_OVERFLOW)
    Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
  else if (myELM327.status == ELM_GARBAGE)
    Serial.println(F("\tERROR: ELM_GARBAGE"));
  else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
    Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
  else if (myELM327.status == ELM_NO_DATA)
    Serial.println(F("\tERROR: ELM_NO_DATA"));
  else if (myELM327.status == ELM_STOPPED)
    Serial.println(F("\tERROR: ELM_STOPPED"));
  else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_TIMEOUT"));
  else if (myELM327.status == ELM_GENERAL_ERROR)
    Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));
  }

  delay(500);
}



For more info on custom queries, you can look at this GitHub issue.


However, that is an absolutely ridiculously long PID response. Are you sure the response is supposed to be that long? Can you provide a link to more info on this PID?

"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

mppstrit

Code: [Select]
Name,ShortName,ModeAndPID,Equation,Min Value,Max Value,Units,Header

Температура охлаждающей жидкости,ЕСТ,2102,A/256*200-50,-40,150,C,
Температура на впуске,IAT,2102,B/256*200-50,-40,150,C,
Температура двигателя,EngTemp,210c,O/256*200-50,-40,150,C,
Положение ДЗ,TPS,2102,C/256*100,0,100,%,
Напряжение БС,Volt BS,2102,(D+1.5)/9.854,0,16,V,
Скорость,Speed,2102,E,0,256,km/h,
Обороты двигателя,RPM,2102,F*256+G,0,9000,rpm,
Требуемый ХХ,Need idle,210d,M*256+N,500,1500,rpm,
Барометрическое давление,BARO,2102,H*0.456,0,119,kPa,
Абсолютное давление,МАР,2102,J*0.456,0,119,kPa,
Массовый расход воздуха,MAF,2102,(L*256+M)/47,0,1400,mg/t,
Длительность впрыска пусковая,InjDurSt,2102,(N*256+O)/64,0,1050,ms,
Длительность впрыска текущая,InjDur,2102,(P*256+Q)/250,0,0,ms,
Коррекция длительности впрыска,Corr Inj,2102,T*256+U,0,65535,ms,
Передача МКПП,Gear,2102,V,0,6,,
Режим АКПП,AKPP,2102,W,0,256,,
Скважность продувки адсорбера,AdsFlow,2102,X/256*100,0,100,%,
Рециркуляция отработаных газов,EGR,2102,Y/256*100,0,100,%,

,,, 45 заменить на объем бака
,,,Fuel Tank,Fuel exist,2102,Z/256*45,0,45,l,
Fuel Tank%,Fuel exist,2102,Z/256*100,0,100,%,
Fuel Tank avg%,Fuel exist,210b,X*0.6,0,100,%,
Давление в системе кондиционирования,CondPres,2102,AB*12,0,3100,kPa,

Реальное положение ДЗ,RealTPS,2102,AC,0,256,steps,
Желаемое положение ДЗ,MeanTPS,2102,AE,0,256,steps,

Режим работы двигателя,Engine st,210c,AF,0,255,,
Бензонасос,FuelPump,210B,{A:0},0,1,0ff/On,
ДЗ закрыта,PSClose,210B,{I:0},0,1,0ff/On,
Кондиционер,Condit,210B,{R:0},0,1,0ff/On,
Реле низкой скор.вент.охл-я,L airfan,210B,{S:0},0,1,0ff/On,
Реле высокой скор.вент.охл-я,H airfan,210B,{T:0},0,1,0ff/On,

O2Sensor1,O2S1,2104,(C*256+B)*4.88,0,319810,mV,
O2Sensor2,O2S2,2104,(G*256+F)*4.88,0,319810,mV,
O2Sensor Heater,Heat O2S,210B,{B:0},0,1,0ff/On,
Датчик детонации,Detonate,2101,G*256+H,0,65535,mV,

УОЗ1,УОЗ1,2103,(180-A)/10,-15,15,deg,
УОЗ2,УОЗ2,2103,(180-B)/10,-15,15,deg,
УОЗ3,УОЗ3,2103,(180-C)/10,-15,15,deg,
УОЗ4,УОЗ4,2103,(180-D)/10,-15,15,deg,
Требуемый УОЗ,треб УОЗ,2103,(128-G)/10,-15,15,deg,

Краткосрочная коррекция топливоподачи,Inst Fuel Corr,2104,((S - (178*{S:7}))*256 + R)/655,-50,50,%,
Накопленная коррекция топливоподачи,Add Fuel Corr,2105,((G - (178*{G:7}))*256 + F)/655,-50,50,%,
Средняя коррекция топливоподачи,Abs Fuel Corr,2104,((W - (178*{W:7}))*256 + V)/655,-50,50,%,

Впускной коллектор переменной длины,Twin Port,210B,{Q:0},0,1,0ff/On,
полная нагрузка,FullLoad,210B,{H:0},0,1,0ff/On,
муфта гидротрансформатора АКПП,Clutch,210B,{U:0},0,1,0ff/On,
drive,drive,210c,{A:0},0,1,off/on,

Режим ХХ,EngIdle,210b,{C:0},0,1,Off/on,
Отключение подачи топлива,Inj off,210b,{G:0},0,1,Off/on,
Время с запуска двигателя,EngRunTime,2117,(I*256+J)/10,0,6553.5,sec
Положение ДЗ2,TPS2,2115,T,0,100,%


Pid 2102 contains all the parameters. Car Chevrolet Lacetti SIRIUS D42

Power_Broker

I don't speak Russian - can you provide an English internet link to a description of that PID?
"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

mppstrit

pid2102 in my machine transmits all the data, each response byte is the data of each parameter.

Power_Broker

I don't speak Russian - can you provide an English internet link to a description of that PID?
"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

mppstrit

Code: [Select]
Coolant temperature, EST, 2102,A/256*200-50,-40,150, C,
The temperature at the inlet,IAT,2102,B/256*200-50,-40,150,C,
Position DZ, TPS,2102,C/256*100,0,100,%,
Voltage BS, Volt BS, 2102,(D+1.5)/9.854,0,16,V,
Speed, Speed,2102,E, 0,256, km/h,
Engine speed, RPM, 2102,F*256+G, 0, 9000, rpm,
Barometric pressure, BARO,2102,H*0.456, 0, 119,kPa,
Absolute pressure, MAR, 2102, J*0.456, 0, 119, kPa,
Mass air consumption, MAF, 2102,(L*256+M)/47,0,1400, mg/t,
The duration of the injection launcher,InjDurSt,2102,(N*256+O)/64,0,1050,ms,
The duration of the injection current,InjDur,2102,(P*256+Q)/250,0,0,ms,
Correction of injection duration, Corr Inj,2102, T*256+U, 0,65535, ms,
Transmission of manual transmission, Gear, 2102, V, 0,6,,
Automatic transmission mode, AKPP,2102, W, 0,256,,
The duty cycle EVAP purge,AdsFlow,2102,X/256*100,0,100,%,
Exhaust gas recirculation, EGR,2102, Y/256*100,0,100,%,
Fuel Tank%, Fuel exist,2102,Z/256*100,0,100,%,
The pressure in the air conditioning system,CondPres,2102,AB*12,0,3100,kPa,
The real situation of the DMZ,RealTPS,2102,AC,0,256,steps,
Desired position DZ, MeanTPS,2102,AE, 0,256,steps,


links unfortunately only in Russian. this is the pid of torque

Power_Broker

Chrome can translate it for me, I want to see the link. I've never heard of a PID that returns more than one value.
"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla


Power_Broker

As the library stands right now, the length of that response wouldn't fit inside the response buffer of the ELMduino library (max 40 bytes). Even if the entire response could fit, there's no way parse each of the individual pieces of data out of the response within the library, so you'd have to write your own parser anyway.

You can fork the library (or edit a local copy on your machine) to increase the response buffer and then write your own parser.

Basically, the standard library supports querying custom PIDs if the PID response is 40 chars long or less.

"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

nieprzem

Hello to everybody.

First of all, thank you Power_Broker for creating such library.


I think I might use it but I need some help from more experienced users :)


I have a china ELM 327 BT device but because of the paring process I prefer to hook it up via wires.
In the device I found out the NXP TJA1040 transceiver:
https://www.nxp.com/docs/en/data-sheet/TJA1040.pdf

I would like to wire it with wemos d1 mini and now, I am not sure it this is right but I think I have to use this pins on the NXP chip. Is that right?


Power_Broker

#12
Apr 19, 2020, 09:28 pm Last Edit: Apr 19, 2020, 09:29 pm by Power_Broker
First of all, thank you Power_Broker for creating such library.
:)  :)  :)


I would like to wire it with wemos d1 mini and now, I am not sure it this is right but I think I have to use this pins on the NXP chip. Is that right?
I've never used the Wemos D1 Mini, but if it has standard Bluetooth functionality built-in (like the ESP32s), then it would actually be a lot simpler just to use bluetooth. That being said, if you still want to hardwire your project and still use ELMduino.h, you will have to wire things up differently than you you suggest.

The chip pointed to in your pic is a CAN transceiver and not the ELM327 itself. In fact, the board in the pic doesn't have an ELM327 chip at all. ELM327s only come in dual-inline packages and the OBD interpreter on the board in your pic is a quad (bottom left hand corner with the surface removed). However, I think the OBD interpreter being used is the STN2120, which (I believe) is compatible with the ELM327 command set - meaning ELMduino.h should still work for the STN2120.

The datasheet I linked has a chip pinout on page 6. Looks like the pins you're interested in are 4 and 5.

NOTE: THE STN2120 IS A 3V3 DEVICE!!
"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

nieprzem

Thank you for the quick response.

The chip which is on the board has 8 pins on every edge.
STN2120 from datasheet has 11 pins.

Not sure if these are the same.
Never mind, I'll have to search for the proper ELM327 device then.

Power_Broker

Oops, I should've paid more attention to the number of pins...

Good luck!
"The desire that guides me in all I do is the desire to harness the forces of nature to the service of mankind."
   - Nikola Tesla

Go Up