OBD II Bike Connector - Pass via bluetooth

Off course I took a look into your GitHub, as you responded to this thread :smiley:
And I like the idea of not waiting for data responses with a delay. Simple wait for Serial.available() or timeout.

Reduced ISORequestByteDelay from 10 to 5.
Then removed all delays according to the receiver -> We will see if the ECU simply responds as fast as it can or stops due to ISO protocol breach...

The last thing, was to change the static delay between the messages to a dynamic value.
I´m storing the last K-Line Response milis and simply calc the time left:

delay(ISORequestDelay - millis() - lastKresponse);

My Emulator counts up to 10(!!!) messages per second, even via bluetooth! Sure, my PC is to fast and I should implement the byte & request delay there as well!

Can´t wait to test that on my bike! Sure, it will slow down to 6-7 responses a second. But that´s great!

0x81 is just a different format with no length information, because messages can only be 1 byte long.
There are two more formats without any source and target information but it seems they are not supported. After fast Init, we receive 0xEA + 0x8F as response. That describes:
Header with Target & Source Information. Additional Length. Normal Timing.
ISO14230 / KWP2000 Protocol.
The more often I read these documents, the more I understand what´s going on there :wink:

Alright that sounds good! I just tested my datalogger with the inter byte request time delay of 0 ms instead of 5 ms, and it just worked fine, although the data rate did not increase much.

What I actually meant with sending 0x81 instead of 0x80 is this:
To establish communication you send:

Start communication: 81 11 F1 81 04
Start diagnostic session: 80 11 F1 02 10 80 14

According to the KWP2000 documents the available parameters for the start diagnostic request are:

...
0x80: ReservedByDocument
0x81: StandardSession
...

Hence I figured it might help to send 0x81 instead of 0x80 to get positive responses from the currently blocked PIDs. However I just gave it a try and communication could not be established. So probably for the KDS protocol the 0x80 parameter requests already a standard diagnostic session.

Furthermore I tried to requests localIdentifiers from 0x0 to 0xFF, and observed that no positive response were given after 0x62 except for the "Supported PIDs" and the long 0xBF message.

It really works! My Sniffer is amazingly fast :o
Nearly doubled my requests from 4 to 7, sometimes 8 per Sec (Maybe rounded).

Good to know, that it doesn´t work. But somehow like that, it should change the mode. We will go on investigating :slight_smile:

That's really good performance, good job! Today I'll try to adjust the timing paramerers. However I first have to do some reading because the timing paramers need to meet certain conditions.

Are there any other interesting requests I can send? Did you ever figure out how to control certain actuators? Btw, do you have access to actual KDS software?

Scissor:
Today I'll try to adjust the timing paramerers. However I first have to do some reading because the timing paramers need to meet certain conditions.

Spare your time...
Out: 83 02
In: 7F 83 10
0x7F is a negative Response Message. Maybe it is related to the result of the init-sequence, which I explored 4 posts above. It just says "Normal Timing". Probably it also means "not editable"?!?

Scissor:
Are there any other interesting requests I can send? Did you ever figure out how to control certain actuators? Btw, do you have access to actual KDS software?

I´m still investigating the diagnostic fault codes but didn´t get further...
ClearDiag: SID 0x14 PID 0x01.
Read Freezed Data Frame:
Out: 12 01 01
In: 52 01 15 07 01 54

Out: 12 02 01
In: 52 02 10 02 01 54

Out: 12 03 01
In: 52 03 10 01 01 54
It does not fit to any known results in the Z750 diagnostic schema.

No, I have not access to KDS Diag. software :frowning: that would make my life a lot easier! You could sniff the data which is being transferred to the COM-Port and all that guessing, try and error would be unnecessary.
[EDIT] Got it. But you cannot install it, without the specific driver. I´m not sure if I can find them and make my device compatible with that driver. Or rewrite the driver...
[EDIT 2] I could extract the Installer and got access to the executable. It starts but (understandable) could not find a connection. I´ll try to fake the driver somehow and see if I could make it work. This will solve all our problems and answer any question :smiley:

Two things I found out yesterday:
The throttle maximum value is getting bigger and bigger. Started with 405 and currently I´m on 455.
It means, that I will not reach 100% again on full-throttle (works also with engine turned off).
Maybe I will stop storing it onto the EEPROM and adjust it on time.

And I got a little setback, also on throttle valve. In the past I could not see it, because of the slow reaction time.
Turning the gas tap slowly, it raises 5%, 10%, 15%, then it drops to 5% again and went on with 20% and upwards.
Maybe you can keep an eye on that. I don´t know what to change on my calculation...

     //201 = 0% = idle, 405 = 100%
      minimum = 201;
      
      value = ecuResponse[2] * 100 + ecuResponse[3];
            
      if(value > ThrottlePosMax)
      {
        ThrottlePosMax = value;
        EEPROM.write(0, ThrottlePosMax - 255);        
      }      
      //((Value-Minimum) *100) / (Maximum - Minimum) = %      
      //OBD II Calculation: (100/255) * A [Backwards]: 100% = 255
      if(value > minimum)        
        ecuResponse[2] = ((value-minimum) *100) / (ThrottlePosMax - minimum) * 255/100;
      else
        ecuResponse[2] = 0x00;
      //Reduce 2 byte answer to 1 byte:      
      ecuResponse[3] = 0x00;

That's a shame that it's not a supported function. Nevertheless I'm going to give it a try, you never know! :slight_smile:

Why don't you try to send 12 00 00 (Requests all data, according to chapter 8.4.1 of ISO-14230-3)? I did not read too much about decoding the DTC response message, but I'll take a look at it soon.

Regarding the KDS software, maybe you could take a look at the files with a hex editor to extract some useful data. Or it would be nice if there were some kind of database files which already contain many possible requests.

I still don't really understand the TPS as two bytes are returned (right?) however the TPS would just give a voltage between 0 and 5 V, such that 1 byte would be sufficient to describe the TPS reading. Anyway, I can imagine the sensor is subjected to noise and interference, such that sometimes you get spikes in the signal. I think it could work to incorporate either some kind of thresholding or a running average to minimize the effect of spikes.

Edit: I managed to get the timing down to 0 ms inter byte request delay instead of 5 ms, and 49 ms between a response-request delay instead of 55 ms. Probably the arduino takes 6 ms to execute the code such that overall the delay equals 55 ms. This implies that the maximum working delay is code specific i.e. you need to figure it out for your self. When I got below 49 ms the ECU did not respond to the request.

Edit2: I also got a general reject message when requesting the 0x83 serviceId to read the timing parameters.

Edit3: I tried to use ReadDataByCommonIdentifier (0x22 instead of 0x21 for localIdentifier), but I only got general rejects (7F 22 10) on all parameters I tried. Probably no data is stored in these registers for the '04 Z750.

Edit4: It looks like your DTC readFreezeFrame request is missing a byte.

12 = readFreezeFrameData
01 = freezeFrameNumber
01 = recordAccessMethodId=requestByLocalId
xx = recordIdentification

Seems like you are missing the last byte, of which the possible values are given in table 8.4.6.1 of the ISO-14230-3 document. If you want to request all data from a single freezeframe number send: 12 NN 00, where NN is the freezeFrameNumber of your choice. And even better if you send 12 FF 00, this would send all the data from all freezeFrameNumbers in several response messages. I'm curious if this trick works for you!

Searching goes on: Just found another PDF, which seems like a combination of ISO-14230-1,2,3&4 documents.
There are SID´s below 10 explained :o
Like Powertrain diagnostic data 0x01, which for sure will contain the injector and ignition data.
Or Emission related data 0x03 for CO2 purpose.

We are starting the Diagnostic mode the "regular" way. By changing the SID, it should be possible also to manipulate values.

Constantly receiving data should be possible by adding parameters:
0x21, 0x0C, [0x00-0x04], [0xXX]
SID, Speed, Transmission Mode (single, slow, medium, fast, stop), Maximum number of responses

I tried to send 12 00 00 before your edit. Now it´s clear why it didn´t worked!
My ISO Document has only 8.4.4 as maximum 8th chapter... I´ll investigate!
My working solution was 12 X 01 54, where X is my counter. But I don´t understand the Answer.
Also I don´t know PID 54, but the healtech software asks that from startup.
On 0x21 it responds with 0 all the time.
There is a lot of black magic going on there :wink:

The KDS Software is compiled in C, there is no way to look into it. But there is a KDSAdapter.dll which might be rewritten and connect to my device. The hope dies last, but I don´t expect to make it work.

Yes, you are right. It must be a value related to the current (operating) voltage. That will explain the different max values. But not the spikes :frowning: I´ll keep an eye on that!
Most sensors are already calculated by the ECU due to the voltage oscillation.
=> After some testing, it has a really strange behaviour. Every 20% it breaks down to 0% very crazy... I have to analyse that.

Nice to know how fast you got it! I am calculating the delay every request. So the time, the code consumes should be irrelevant.
49ms + 6 for your code... I think I´ll stay on 55 :slight_smile:

The next days I´ll be on a racetrack and go on testing!

[EDIT]: TPS seems like it is calculated different to all the other 2byte values...
Speed and RPM: A * 100 + B.
My only explanation for dropping values is: A * 255 + B. Every time I am passing the 255 mark, it calculates 155 less. That´s why it is dropping lower again.
That´s very bad, because I have to change my min & max calculation and the max-data storage... Both is at not suitable for values greater then 509, at the moment :frowning:

There appear to be a bunch of documents about the KWP 2000 protocol, but I doubt if all are relevant. Nevertheless we can always try to see if some function works of course! :slight_smile:

If I understand you correctly, you send 12 00 00 54? Does the response also have 54 as last byte? Or did you make a typo? Do you have the healtech OBD software? If so, why don't you try to use your sniffer to reveal all the requests?

Regarding the TPS value, I see the same thing happening. I think it simply means that the value is a 16 bit integer e.g. a range of 0-65535. Now all we need to do is normalize it to fit the engineering unit.

TPS = ((256A+B) / (256256) )* C

Where C is the constant we are looking for. According to my Z750 manual:

0.99 ∼ 1.03 V DC (at idle throttle opening)
4.19 ∼ 4.39 V DC (at full throttle opening)

The minimal value I get is 213 (0x0 0xD5) and at full open in the range of 871 (0x03 0x67). What first comes to mind is a simple factor of 5 (C = 5256256, resulting in simply the 16 bit integer value mulitplied by 5). This yields: 0% open: 1065 mV and 100% 4355 mV. I cannot guarantee this is correct, as I did not check the 100% throttle output properly. Does this fit your readings?
In any case, I'm pretty sure you don't need to update your extreme TPS values during each run, as the output will always be between 0-5V. You could always do some post processing to get a value between 0 and 100%.

Yes, I was sending 12 00 00 54 which does not work. But call DTC group 01 is okay:

To ECU: 12 01 01 54
FrmECU: 52 01 15 07 01 54
To ECU: 12 01 02 54
FrmECU: 52 02 10 02 01 54
To ECU: 12 01 03 54
FrmECU: 52 03 10 01 01 54

If I would have the Software & the Healtech plug, I would have been already through :wink:
But I know someone, who sent me some data in the past. That´s where I got the most known PID´s from.

TPS is (on my bike) a value from 00 D8 up to 03 7F, which I tested several times.
I was totally wrong with my dynamic adjustment, because with my old equation, I more or less took an eye on the last byte.
Calculation A*256+B means 201 - 895, which worked great, yesterday (Forgot to reset max value, so I only got max ~70%).

This is not very straight if you compare the calculation with RPM and Speed. But rather then have to fix voltage oscillation :smiley:

Tomorrow start my trackdays. There, I´ll go on testing the data values. After that I´ll keep on going with the diagnostic.

I see, it is indeed a strange result. At this point I'm not sure how to analyse the response message. I agree that it's odd how the conversion formulas differ from each other. I think I did not see the same thing for the SDS protocol. Probably the Kawasaki engineers like to confuse us. :slight_smile:

Have fun on your trackday, ride save :wink:

Today I estimated the conversion formula for the pressure readings (IAP & MAP) by comparing to the Z750 manual.

In the manual there is a graph of Pressure vs. Voltage for the IAP. From the linear plot I was able to determine the slope dP/dV to be 191.0±0.4 mmHg/V using the least squares method of excel.

The 2 bytes are a 16 bit integer such that the value is given by x=256*a+b [0-65536]. I assumed again that normalizing the value and multiplying by 5 V results in the sensor voltage.

V = (5/(256*256)) * x

Then the pressure is simply given by P = (dP/dV)*V.

This results in:

P_mmHg = 191*(5/(256*256))*x

Or, in more common units:

P_Pa = P_mmHg * 133 = (1335191)/(256*256)*x

The last equation can be approximated by using 100 * 256/192=133:

P_Pa = (100256/192)(5191)/(256256)x ~= (1005/256)*x

And by simply dividing by a factor 1000 to get the pressure in kPa:

P_kPa = P_Pa/1000 = (1/(2*256))*x

Now, this last equation can be simplified by losing some resolution by ignoring b.

P_kPa = a/2 + b/(2*256) ~= a/2

I´m back and me and my bike are still intact :smiley:

There have been several issues during the rides:
TPS went up to 92,7% and afterwards down to zero. That must be related to a rounding issue, which results on a value greater then 255. It overruns and begun at 0.
My temporary fix was to set the value to 0xFF manually, if value is greater then 880 but the response is below 10.
Not the gentleman-way, but it worked.

The speed formula was made by people with speed limits. It covers 120kmh only :stuck_out_tongue_closed_eyes:
Like TPS, you have to multiply with 255 instead of 100.

In matter of that, it would be consequential that RPM has to be multiplied by 255 instead of 100, also. But 100 worked good from 1000-11.500 RPM :roll_eyes:

There must be a specific value, which reboots the arduino. Sometimes after 2, sometimes after 15 minutes.
It should not be related to the communication, global variables or something like that. My emulator is working for hours with the Ardu. I think it must be related to a formula which probably divides by null or something :frowning:

MAP is calculated without voltage. My calculation has always been like that :smiley:

      case 0x05: //Airpressure: From 2 byte to 1 byte:    
      if(ecuResponse[2] >= 2)
        ecuResponse[2] = ecuResponse[2] / 2; //Double precision
      //Ignore accuracy
      ecuResponse[3] = 0x00;      
      break;

Very good to hear your results. Good to know that the speed is calculated the same way, strange though that RPM deviates from that. The longer I work on this, the more confused I get. I just wanted to double check the formula for the IAP/MAP value :slight_smile: Now this simply proves that you were right all along haha! It also proves that the IAP/MAP values follow the specifications written in the Z750 manual e.g. the sensor pressure is linearly proportional to the voltage.

What is your code to calculate the TPS?

I'm having some issues to reconnect with the ECU after communication is lost. When I adjust the delays on the go with a potentiometer, of course at some point the request is send too fast after the last responds which ends up in a timeout (> No response within 1500 ms). Now, when I send a fastinit command directly, I don't get any response back no matter how many times I try.

The sequence looks like this:

...
Response
45 ms delay
Request (This is not received by the ECU)
Response timeout

350ms
Fast init sequence
Response timeout

350ms
Fast init sequence
Response timeout

350ms
Fast init sequence
Response timeout
...

I assumed a simple >300 ms delay would be sufficient as described by the KWP2000 protocol. However, it just won't initiate. So I tried to increase the delay before sending the fastinit sequence to 5000 ms, and it worked!
I took at look at your code, and I see you have a 2000 ms delay before reconnecting. I remember to have read this somewhere in the KWP2000 protocol, but I can't remember in what document. Do you know what the rules are for reconnecting after a timeout?

Edit: I guess it simply means that I have to wait up to 5000 ms to receive the response. There is no way to ignore the response and restart the ECU earlier. I'm going to test this right now.

Edit: Turns out you have to wait for 5000 ms before reinitializing. That's a shame.

Scissor:
What is your code to calculate the TPS?

Today I changed my approach to fix wrong values, by using a different function, to avoid rounding issues:

        //ecuResponse[2] = (value-minimum) *100 / (ThrottlePosMax - minimum) * 255/100;
        ecuResponse[2] = map(value, minimum, ThrottlePosMax, 0, 255);

Seems to work :slight_smile:

I don´t really remember where I got the 2 seconds from. Think the EcuHacking-Forum was it.
Raised it today, but after 20 minutes I still got a break down :frowning:
Maybe the Error was never called and it is really a divide by zero or something.

If the init comes earlier then the 5 Seconds, but then static every second, does it still get ignored?

What´s the matter, that you need the reinitialization?

Trib:
Today I changed my approach to fix wrong values, by using a different function, to avoid rounding issues:

        //ecuResponse[2] = (value-minimum) *100 / (ThrottlePosMax - minimum) * 255/100;

ecuResponse[2] = map(value, minimum, ThrottlePosMax, 0, 255);



Seems to work :)

I don´t really remember where I got the 2 seconds from. Think the EcuHacking-Forum was it.
Raised it today, but after 20 minutes I still got a break down :(
Maybe the Error was never called and it is really a divide by zero or something.

If the init comes earlier then the 5 Seconds, but then static every second, does it still get ignored?

What´s the matter, that you need the reinitialization?

Good that you have hopefully solved that problem! I finally did some first test rides, and found the datalogger works great. I can log approximately 8-10 localIds per second, at a very stable rate. However the Arduino seems to restart sometimes. It takes roughly 10 seconds to restart logging as I found from the motorcycle operating time (works as a RTC :slight_smile: )

I guess there are two possible reasons for a reboot: SRAM overflow or a power supply issue.
I have excluded the first possibility by logging the free memory, which was constant i.e. no memory leaks. The latter possibility seems more likely to me, especially since my (Ebay) Arduino Nano does not have the standard voltage regulator. After a ride the datalogger also feels quite hot, so this indicates there is quite some heat dissipation. The voltage regulator is an AMS1117 with an absolute maximum input voltage of 15V, which is on the edge I think. Do you know if the 12 V line from the diagnostic plug is stable at 12V, or is it directly coupled to the battery such that during charging the voltage can be like 14V? I hope to have solved this problem by using an extra decoupling capacitor. If this problem can't be solved I either have to buy a genuine Arduino Nano, or use an additional voltage regulator.

About the reinitialization, the strange thing is that the K-line must be silent for > 5000 ms. I investigated this by changing the response-request delay (0-55 ms), and the response timeout (0-5000 ms) on the go. Only when the response timeout > 5000 ms (including the 350 ms I have included in the fast init sequence) the ECU responds again. Setting it to a lower value like 1000 ms, and trying 5 or 6 times does not work. It could be related to a flaw in my code, but at this point I assume my code works as it should.

That´s a great speed! You have removed every delay, except the 55ms between the messages, right?
Got still the delay between every byte. Removed it also and test that tomorrow!

I was expecting that! The Arduino crashes and my Action Camera goes on asking for values. EcuConnected is false, so it will try to init constantly.
I have no clue how to differ between a clean startup sequence or a crash to execute the 5 Sec delay :frowning:

But the best solution would be not to crash :smiley:
Checked temperature and it was okay! BT module is warm, but far away from hot!
Also the soldering is made to resist cobblestones or earthquakes :wink:

You should add a Ceramic-Capacitor 10 nF 50 V/DC. The voltage will be up to ~14,5 on idle. Probably more during the ride.
As you can see in the manual, even the internal voltage differs.

Our beloved discussion about A100+B or A255+B:
I´ve never reached more then 11.100 RPM. The limiter comes up at about 12.500 RPM (which I maxed out on the home straight, several times).
Did the camera missed the point or do we still have a wrong formula?

Last but not least: The first thing which stops on my recordings, was the temperature.
Maybe it is just the result of a failure, maybe the cause.
The formula is not very typesafe...

    case 0x07:
      //(A-48)/1.6 = Celsius (KDS)
      //+40 to avoid negative values (OBD)
      if(ecuResponse[2] == 0x00)
        ecuResponse[2] = 40; //0C°
      else
        ecuResponse[2] = (ecuResponse[2] -48) / 1.6 + 40;
    break;

I´m sure it will explode below zero! But water has been between 40-80° (Celsius) and Air between 19-40° (temperature in the Airbox which heats up, not the outside temp).
Do you also store temp on your SD card?

It is really fast! I didn't expect this rate, since I do use the 5 ms and 55 ms delay, just to make sure no timeout occurs.

I added a 1 uF capacitor besides a 100 nF, and managed to capture 21 minutes of logging (I attached the CSV file). I already used the 100 nF capacitor, and could not log the data for more than about 5 minutes before crashing the Arduino. I'm going to try to use the 10 nF capacitor, and see if it can improves stability. This time the arduino did not feel that hot as before, although that's a bit of a placebo effect I guess.

So far I used the 100*a+b formula to calculate the RPM, but never checked carefully. You can use the attached file to compare with your results. I think the formula is at least very close, you could have missed the rev limiter, as it obviously is only a very short period of time. My logger logs the RPM roughly every second, therefore you have to be very lucky to capture the exact right moment.

You can see in the attached log that I capture 10 localIds: TPS, IAP, ECT, IAT, MAP, RPM, Battery voltage, gear, speed and total operating time.

Here is the log file of approximately 20 minutes of riding in regular traffic.

Btw, to double check if the RPM formula is OK, you can try to log only the RPM. If you manage to log at like 8 Hz the probability is a lot higher that you can capture the rev limiter.

DATA16.zip (11.7 KB)

Possibilities for a reset:

  • Divide by zero - should not cause a reboot as I read
  • Stack overflow - full RAM
  • Access Array with a indices greater then the size
  • Bad Pointer Access
  • Voltage drops
  • Reset by Serial (Like Arduino IDE does during an upload)

The serial buffer can be overloaded as well and cause problems.

When emulating, the Arduino is connected via USB, not Pin 0 & 1 and powered by it, also.
The Emulator values differ a bit from what the ECU does. Only some fake random values within the known ranges.
Anything else is the same!
In that scenario the Arduino works like charm for more then 2 hours! Stopped it by myself afterwards. That´s why I´m sure it is related to the data itself. Or as you mentioned, to the power supply.

I got the Autobahn here. Think I will reach the 12,5k RPM for testing purposes somehow :smiley:

The init sequence has to timeout or receive an error value as response. Before it will return "false" I attached a 5 Sec delay. I hope it will make the deal to go on working after an error occurred.