PowerBroker2 / ELMduino

Arduino OBD-II Bluetooth Scanner Interface Library for Car Hacking Projects
MIT License
632 stars 123 forks source link

more on custom PIDs - splitting response #38

Closed miguelos6 closed 4 years ago

miguelos6 commented 4 years ago

This is continuation of issue 4 some custom PIDs return values and suggested calculation for result is divided in two parts A and B (all examples for Torque app) see example of calculations suggested -> here if A256+B makes all sense as it's simply conversion of longer response to decimal (I guess) such a calculation : (A33.55/255+B*0.13/255)-12 is impossible to get with current findResponse()

Would it be possible to return A and B separately so calculation could be done on them ?

currently the most complex query & response for me is Distance Since Last DPF Regeneration Dist. DPF 223039 A*256+B 0 65535 km 7E0 myELM327.queryPID(34, 12345) //0x22 and 0x3039 => 34 and 12345

PowerBroker2 commented 4 years ago

While bit masking/shifting the output of findResponse() would relatively easily provide the A and B values you're looking for, I can add 2 class member variables a and b to make things even easier.

PowerBroker2 commented 4 years ago

I opted not to use class variables a and b, but rather broke out the 64-bit response into 8 individual bytes that you can then use as your application sees fit.

Changes implemented in 2.3.0.

miguelos6 commented 4 years ago

Hi @PowerBroker2, sorry for re-opening this. Could you please give an example how to apply such calculations on A & B returned separately ? Will old sketches work with this change ?

PowerBroker2 commented 4 years ago

No worries!

Some standard PIDs return a single byte - in this case, the LSB of the response is the A value and B is undefined. Other standard PIDs return 2 bytes - in this case, the 2nd LSB is the A value and the LSB is the B value. Custom PIDs sometimes return even more than 2 bytes of data.

Because there is no set byte for the A value and B value, I can't easily/automatically interpret which byte in the response is A or B. This is why I didn't implment class variables a and b. Instead I implemented the following:

byte responseByte_0;
byte responseByte_1;
byte responseByte_2;
byte responseByte_3;
byte responseByte_4;
byte responseByte_5;
byte responseByte_6;
byte responseByte_7;

Where responseByte_0 is the response LSB, responseByte_1 is the 2nd LSB, ..., and responseByte_7 is the response MSB.

For your application, if you get a valid response from getResponse(), you can then use responseByte_1 and responseByte_0 as your A and B values, respectively.

Does that help?

miguelos6 commented 4 years ago

Thank you for reply, but again I'm sorry for not understanding (I'm not a programmer, just using the lib to do small work for me) I guess I need to change my code (pre 2.3.0) in order to know how to interpret returned values? that is - now more significant bytes may contain garbage data & simply doing sth like this : int16_t Tpid1a6d = myELM327.findResponse() will give unpredicted results ?

So far I used only A and A+B values. Only recently found a value that need to be split into separate calculations on A and B

Maybe it's an option to zero out more significant bytes and just filling A or A+B on query ? Could you please provide an option on how to read A, B (or more) separately in sketch after findresponse? (as mentioned - I'm not a programmer:/)

PowerBroker2 commented 4 years ago

Any non-valid bytes are already zeroed out, so it's fully backwards compatible with previous sketches. The difference is now you have more flexibility when calculating complex PID responses. For your case:

myELM327.findResponse();
byte B = myELM327.responseByte_0;
byte A = myELM327.responseByte_1;
// do things with A and B
cyberelectronics commented 4 years ago

Hi, I'm trying to implement some Extended PIDs for a Hyundai Kona EV (the car not yet arrived, so I can't test it right now. I'm more a HW guy, so implementing any SW is not an easy task for me). Your library works great with my old car and BT adapter (checked the RPM data).

I found some examples/sniff and description on the net, but it looks like for a query (PID 220100), the response can be 6x 8bytes or even 9x 8bytes long or maybe I just misunderstand something. (Source) example_hexdump

pid 220100 @ 2019-03-23T10:50:56.868873 7BB 10 26 62 01 00 7E 50 07 7BB 21 C8 FF 7C 67 66 0E E5 [?=200] [?=255] [INDOORTEMP=22] [OUTDOORTEMP=11.5] [EVAPORATORTEMP?=11] [?=14] [?=229] 7BB 22 8F FF FF 8F FF 11 FF [?=143] [?=255] [?=255] [?=143] [?=255] [?=17] [?=255] 7BB 23 FF FF FF FF FF FF FF [?=255] [?=255] [?=255] [?=255] [?=255] [?=255] [?=255] 7BB 24 FF 3A D6 8F 90 43 FF [?=255] [?=58] [?=214] [?=143] [?=144] [speed?=41.6407706650093] [?=255] 7BB 25 FF 00 FF FF 00 00 00 [?=255] [?=0] [?=255] [?=255] [?=0] [?=0] [?=0]

Is there a way to parse and check a specific "responseByte_X"? Can you give me an example code? Thank you!

PowerBroker2 commented 4 years ago

I'm curious, what is the 7BB section for? Usually when you query for a PID, the response will begin a slightly modified PID as a sort of "preamble". Is this the case for your PID?

Once you do get the car, use one of the test sketches to manually query the vehicle for this custom PID. Then, record the exact response and post it here.

cyberelectronics commented 4 years ago

I think Header / Module ID. (end of this page) Here on the first page you can find 7BB (Aircon). Ok. I will try to record the exact response (in 2-3 weeks :( )

cyberelectronics commented 4 years ago

Just found this example with more info. So from the "our dump" response, where request ID was 7B3, we can identify: 7BB _10_ _26_ _62 01 00_ 7E 50 07 7BBh = request ID 7B3 + 0x8 (added to response) 10h = first frame (sequence 0) from a multiframe packet (20h means consecutive frame and 00h means single frame packet) 26h= number of bytes (len) = 38 - 3bytes (Mode&PID) 62h 01h 00h = Mode&PID, where 62 => Mode 22h + 40h ("OK" added to response)) and PID 01 00 Aprox. 10ms between sequences

Another crazy example about VESS - CAN mod explained

ATcommands (init, protocol 6) for OBD adapter ( screenshot from OBD2 Simulator, found on the net) : ISO15765_example

cyberelectronics commented 4 years ago

Here is the response, using your demo sketch: 1) Sending command 220101 (from Battery Management System BMS module): Connected to ELM327 220101 SEARCHING... 7F2212 7F2212 7F2212 03E - I think this should be at the beginning of frame 0: 0: ?3E? 620101FFF7E7 // ?3E? = missing byte from frame 0 1:FF 98 0000000080 // State Of Charge SOC = 98h / 2 = 76% -> correct! 2:0000 0F2B 1F1D1D // represents Main Battery voltage (0F*256 + 2B)/10 = 388V which is correct! = according to this file 3:1D1F1D0000 1B C6 // BATT Inlet Temp 1B = 27C -> correct 4:03C5280000 7A* 00 // AUX Batt Voltage 7A 0.1 = 12.2V -> correct 5:0009FF00000888 6:00 00 03 D0 00 00 03 // Cummulative Energy Charged CEC = ((00<24)+(00<16)+(03<8)+D0)/10.0 = 97.6 kWh -> correct 7: 1D 000615A30900 // Cummulative Energy Discharged CED = ((00<24)+(00<16)+(03<8)+1D**)/10.0 = 77.1 kWh -> correct 8:030000000003E8

2) Sending command 220100 (AirCon module, I think it was OFF) 220100 SEARCHING... 7F2212 7F2212 7F2212

3) Sending command P: Print supported PIDs Turn engine ON Turning ECU filter off ATCRA Turning headers ON ATH1 Turning line feeds ON ATL1Turning spaces ON ATS1

Supported PIDs: 01007EB064100800000017EC06410080000001 Supported PIDs: 01207EB064120800180007EC06412080018001 Supported PIDs: 01407EC06414000000020 Supported PIDs: 0160NODATA Supported PIDs: 0180NODATASupported PIDs: 01A0NODATA

Disconnecting bluetooth connectionHalting program ...

PowerBroker2 commented 4 years ago

This is all a little confusing - all I need is the query and the exact response. Most likely, however, you will have to send and parse this particular PID response yourself since the output of the library's parser is 64bit max. You can still find utility in the library for setting up the ELM327 and polling less eccentric PIDs

cyberelectronics commented 4 years ago

I combined 2 demo sketches, but basically I just sent the "220101" thru Arduino Serial Monitor (using this OBD adapter + this ESP32 TTGO board).

void loop()
{
  if(DEBUG_PORT.available())
  {
    char c = DEBUG_PORT.read();
    DEBUG_PORT.write(c);
    ELM_PORT.write(c);
  }
 if(ELM_PORT.available())
  {
    char c = ELM_PORT.read();
    if(c == '>')
      DEBUG_PORT.println();
    DEBUG_PORT.write(c);
  }
}

and the exact response was this (where 03E = 62 bytes length and 620101 = 40h + PID):

Connected to ELM327
220101
SEARCHING...
7F2212
7F2212
7F2212
03E
0:620101FFF7E7
1:FF980000000080
2:00000F2B1F1D1D
3:1D1F1D00001BC6
4:03C52800007A00
5:0009FF00000888
6:000003D0000003
7:1D000615A30900
8:030000000003E8
> 

Today I will try this:

if (myELM327.queryPID(34, 257))   // Kona PID = 22 01 01 = 34, 257
              {
                 Serial.print("Payload received: ");
                 for (int i=0; i<myELM327.recBytes; i++) Serial.print(myELM327.payload[i]);
                 Serial.println();
              }
cyberelectronics commented 4 years ago

So here is the results for queryPID(34, 257) =>PID 22 0101 at every 2 seconds OBD protocol was modified from '0' (Auto) to '6' (otherwise 'Searching...' will appear). Original payloadLen = 40 in elmduino.h (line 300) Here the car was turned ON

Connected to ELM327
Payload received: 03E0:620101FFF7E77F22127F2212
Payload received: 7F22127F22127F221203E0:620101FFF7E71:FF6E
Payload received: 7F22127F22127F221203E0:620101FFF7E71:FF6E
Payload received: 7F221203E0:620101FFF7E77F22127F22121:FF6E
Payload received: 7F221203E0:620101FFF7E77F22127F22121:FF6E

Modified payloadLen = 200 in elmduino.h (line 300) Now all frames, from 0: to 8: appears correctly The car was turned OFF

Connected to ELM327
Payload received: 03E0:620101FFF7E71:FF6E00000000802:00000E3B1C191B3:19191B00001CB94:46B92D00007C005:000AF100000AFF6:000004270000047:040007684400008:000000000003E8
Payload received: 03E0:620101FFF7E71:FF6E00000000802:00000E3B1C191B3:19191B00001CB94:0DB92800007C005:000AF100000AFF6:000004270000047:040007684600008:000000000003E8

From here the car was turned ON ( 7F2212 was sent 3 times for each query, I don't know what is this)

Payload received: 7F22127F22127F221203E0:620101FFF7E71:FF6E00000000832:00010E3B1B191B3:19191B00001CB94:49B92800007A005:000AF100000AFF6:000004270000047:040007685F0D018:6B0000000003E8
Payload received: 7F22127F22127F221203E0:620101FFF7E71:FF6E00000000832:000C0E3A1B191B3:19191B00001CB94:0FB928000080005:000AF100000AFF6:000004270000047:04000768610D018:6B0000000003E8

Strange: sometimes 7F2212 appears later in the packet, between frame 0: and frame 1:

Payload received: 7F22127F221203E0:620101FFF7E77F22121:FF6E00000000832:000C0E3A1B191B3:19191B00001CB94:2CB92D000092005:000AF100000AFF6:000004270000047:04000768750D018:6B0000000003E8
Payload received: 7F221203E0:620101FFF7E77F22127F22121:FF6E00000000832:000F0E3A1B191B3:19191B00001CB94:01B928000084005:000AF100000AFF6:000004270000047:04000768630D018:6B0000000003E8
Erictric commented 3 years ago

I combined 2 demo sketches, but basically I just sent the "220101" thru Arduino Serial Monitor (using this OBD adapter + this ESP32 TTGO board).

void loop()
{
  if(DEBUG_PORT.available())
  {
    char c = DEBUG_PORT.read();
    DEBUG_PORT.write(c);
    ELM_PORT.write(c);
  }
 if(ELM_PORT.available())
  {
    char c = ELM_PORT.read();
    if(c == '>')
      DEBUG_PORT.println();
    DEBUG_PORT.write(c);
  }
}

and the exact response was this (where 03E = 62 bytes length and 620101 = 40h + PID):

Connected to ELM327
220101
SEARCHING...
7F2212
7F2212
7F2212
03E
0:620101FFF7E7
1:FF980000000080
2:00000F2B1F1D1D
3:1D1F1D00001BC6
4:03C52800007A00
5:0009FF00000888
6:000003D0000003
7:1D000615A30900
8:030000000003E8
> 

Today I will try this:

if (myELM327.queryPID(34, 257))   // Kona PID = 22 01 01 = 34, 257
              {
                 Serial.print("Payload received: ");
                 for (int i=0; i<myELM327.recBytes; i++) Serial.print(myELM327.payload[i]);
                 Serial.println();
              }

Is this a snipet of your code?

Should I had: myELM327.sendCommand("AT SH 7E4");

To set the Header first?

Thanks

cyberelectronics commented 3 years ago

@herode10 It's from the demo code, but yes you should set the Header first, using this command, otherwise more ECUs will respond with the same message. Just finished my project ( Konassist v1.0 ) so you can check the full code/description here also

Erictric commented 3 years ago

@herode10 It's from the demo code, but yes you should set the Header first, using this command, otherwise more ECUs will respond with the same message. Just finished my project ( Konassist v1.0 ) so you can check the full code/description here also

Wow! Impressive work! Thanks for the response.