OBD II Bike Connector - Pass via bluetooth


I´d like to receive diagnostic data from my motorcycle (Kawasaki Z750r - 2012). The first challenge is, that against all cars and trucks, the diagnostic interface is not standardized on two wheels. Neither the plug, nor the communication.

That´s why I cannot use a cheap (china) OBD II to Bluetooth adapter.

I´m familiar how to communicate via K-LINE of KDS Bus, from here. In my case KWP2000 (ISO-14230).

[u]What I´d like to do:[/u] Contrary the most solutions here (or the most famous OBDuino), I want to [u]pass[/u] the data via Bluetooth, not receiving it. My receiver is a Gamin Virb XE Action-Cam. It stores the information according to a video, and display them later on, as some nice gauges.

Step two will be an additional, small display to show current gear and some infos, at present I don´t know :)

[u]Background:[/u] The Virb XE action cam already has GPS, gyro sensors and stores the data onto a microSD card. It comes with a OBD 2 compatible bluetooth connection. Thats why I am searching for a lightweight solution.

There are lots of (more or less) expensive OBD Boards. But none of them fulfilled my wishes.

[u]What do I have:[/u]

  • Arduino Uno / Arduino Mega - should be replaced later
  • E-L9637D - Regulates the RS323 (RX & TX) Signal down from 12V to ~4V
  • TSR 1-2450 24 V/DC 5 V/DC 1 A 6 W - Reduces Voltage from 12V to 5V
  • Bluetooth receiver / recorder (aka Virb XE)

[u]What do I need:[/u]

  • Arduino Nano, BlendMicro or similar (included Bluetooth?)
  • Bluetooth-Shield (unless already included)
  • Waterproof LCD Display - Step II
  • Waterproof case - Step II


Which board would you suggest? The space blow my seat is really restricted. It should be powerful enough to transmit several information at once. In the best case, it already has Bluetooth. Will it work with the L9637D? The description says it can handle ISO 9141 and I need 14230. How to submit the signal to the receiver? It seems that the Virb XE uses the same protocol (ELM327) as the OBD II Bluetooth adapters are using. Also the famous Torque App communicates by that. Maybe someone has already forwarded diagnostic data.

Maybe you´ve got any further suggestions or Ideas.

A small update:

The partially above mentioned items (E-L9637D & Virb XE-Camera) arrived. Also some wires, 12V switch, SOIC Board (to solder Lxxx SMD on it), ect.

And I just bought some stuff to finally begin with the project.

At first I wanted to start with my old UNO, but to implement the bluetooth part, its necessary to have a BT device :)

My next steps will be (dependent on what arrives first):

  • Soldering SMD to socket
  • Soldering LCD (long waterproof cable to place it into the cockpit later on)
  • Create Sketch from several tutorials*
  • Create DotNet Test-Application to connect to the Arduino by USB (Serial) and afterwards by Bluetooth
  • Measure voltages & pin assignment on diagnostic interface
  • Measure voltages after L9637D
  • Connect everything on a breadboard
  • Testing, optimizing, testing, optimizing, and begin right from the start
  • Soldering everything up
  • Creating waterproof case (Board & Display)
  • Laying wires under the fueltank
  • Take a ride with my Action Cam

*My most influential Tutorials: KDS Protocol Kawasaki KawaDuino OBD 2 LCD Sketch ELM327 Commands PDF OBDuino Wiki OBD Explaination (German) LCD wiring Getting started with Blend Micro

That´s pretty much everything I/you need and a bit more :)

A huge update:

It´s nearly done! There´ve been several challenges, but it finally works :slight_smile:


  • Arduino Nano
  • HC-06 Bluetooth
  • L9637D
  • Ceramic-Capacitor 10 nF 50 V/DC
  • 510Ohm Resistor

Also several different displays, to test. At least I´ve ordered a 128x64 OLED from AdaFruit 0,98".

There are a lot of wiring schematics around. But it´s only important to connect Rx to Rx and Tx to Tx (not twisted!) and use a pullup-resistor to the K-Line (connect K-line with VIN from diagnostic by ~510-550Ohm)
Important: Unconnect Rx if you want to upload your Sketch!

This has to be splitted into several smaller pieces of work.

OBD II compatible Bluetooth dongles all work with a ELM327 chip (or a china-clone).
This adapters are used by AT-Commands, which setup the communication protocol. Afterwards the Protocol ID´s were submitted in Hex-values as text.

The cmd´s begin with “AT” + command [+ Value] + ‘\r’

ATL0\r - means tunrning Linefeed (\n - 10) off (That does NOT mean, turn CarriageRetrurn | \r - 13 off)!
You have to return: OK\r>\r
The > is a prompt, which completes a command-set. Exceptions where: Unknown Cmds ? or Device Info.
Important for my Action Cam: The answer to ATZ (Reset) must contain the Version. F.e. “ELM327 v1.4”. Otherwise the cam will not proceed.

D Set to default (Header = false, spaces = false, ect)
Z Reset Device
H0 / 1 turn Header on or off
I Device Info (ELM Version)
SP Protocol Auto
S0 / 1 turn spaces on or off
L0 / 1 turn linefeed on or off
E0 / 1 turn Echo on or off
M0 / 1 use memory (store last CMD)

PID Lists:
There are every 32 bits lists of the following 32 specified bits:

Request Response
0100 4100181B8001
0102 412000000001
0104 414008000000
0106 416000000000
0108 418000000000

The response is not completed with OK. Only newline and Prompt (>)!
What does it mean?
Take all PID´s from 01 - 20 (32pcs). 00 is reserved for the first PID-List, 20 is the next one.
Now convert the response from 00-request to bits:

181B8001 (ignore 41 [Response is valid] and 00 [Request])

Now you can lay it over your PID-list and see which PID is valid 1 or not supported 0.

My personal challenge was to translate the OBD2 PID´s to the appropriate KDS PID (Kawasaki Diagnostic System). And afterwards to calculate the response to the required format.

Sending Data:
Now you will receive the register and can put the answer through. (Torque-App will ignore the valid PID´s and you will have to return “NO DATA”, Garmin Virb-Cam suits to the valid values)
01 (Show current data) + 0C (PID) + \r [sometimes a space only. Don´t really know why]
Remember to answer properly:


XX-Requested PID, YY-Value (can also be longer)

Now one of the most interesting part!
I´ve made usage of a code from Tom Mitchell (Kawaduino)
You can get it from BitBucket and see the result on YouTube.

The most important parts are the FastInit()-Function to start communication and diagnostic mode.
And the sendRequest()-Method to create & send the request and receive the response, unpack it and check the checksum.

It was a hard way to get the hardware work (wrong Bluetooth-module, broken E-L9637D, Pulldown instead of Pullup resistor…).
The software was also very time consuming. To check the Arduino Sketch, I created a ECU-Emulator in C#. It answers requests with configurable responses.
Then I created a Bluetooth-Sniffer to receive what kind of information the Camera or Torque-App requests and how I have to respond.
At least I made a ECU-Sniffer, which requests all supported PIDs from my motorcycle.

When it all worked, the next task was to find out which PID correspond to which sensor. The ECU-Hacking Board (link in my second post) just got knowledge of Speed, RPM, Temp, Battery and Gear. By try and error and some help from a guy with a diagnostic device, I was able to find out about 33 known PIDs. Another 27 are still open.

My further investigations are to find out some calculations, how to use throttle or throttle valve.
After that, I´d like to upload all my code to GitHub. Including:

  • ECU-Emulator (Windows Application)
  • ECU-Sniffer (Sketch)
  • Bluetooth-Sniffer (Sketch)
  • ECU-Reader (Sketch) → The result :slight_smile:
  • Valid PID-List with formula, etc. (Excel Sheet)

As you can see in the attached pictures:

The finished Arduino Nano:
2015-10-27 00.17.10.jpg
Test on my bike:
ECU Emulator Bike-00-00-21-879.jpg
ECU-Emulator with Ardu connected:
ECU Emulator-00-00-00-000.jpg
My first ride :slight_smile:
(Garmin Virb XE Action-Cam)

It still is not ready, but working. Who ever is interested can find the code here:

GitHub - KDS 2 Bluetooth

Including my working solution, a .Net ECU Emulator and a Bluetooth-Sniffer to identify the AT Commands.

I´m sure it can be optimized a lot. If you got some hints, please let me know :)

Good job! :sunglasses:

Trib: It still is not ready, but working. Who ever is interested can find the code here:

GitHub - KDS 2 Bluetooth

Including my working solution, a .Net ECU Emulator and a Bluetooth-Sniffer to identify the AT Commands.

I´m sure it can be optimized a lot. If you got some hints, please let me know :)

AMAZING!! You just did what I've been preparing to do on my Z800 :)

Thanks for sharing the information, it helps a lot a lot a lot.

You are welcome!

The final result looks like that: http://www.youtube.com/watch?v=MKdlcnXseew

I´ve adopted the idea of combining that with a display. It would be way to slow. Maybe it will be another project :) There are some possibilities to read the value directly from the tachometer (CAN-Bus).

Trib: You are welcome!

The final result looks like that: http://www.youtube.com/watch?v=MKdlcnXseew

I´ve adopted the idea of combining that with a display. It would be way to slow. Maybe it will be another project :) There are some possibilities to read the value directly from the tachometer (CAN-Bus).

Good job!!! Thanks for shared information!!!

Sorry but I'am noob about this type of comunications.

Knowing all of it, there is a possibility to configure an ELM327 interface to conect directly to ECU, I have tried, but with no success!

Thanks again!!!

Hi rmachiavel,

no I dont think that´s possible. An ELM327 is created for an OBD II Interface, which bikes don´t have. The communication of OBD is made by two wires (Serial), f.e. KDS has one wire (K-Line). The ELM has also a K-Line, but as fas as I know only for manufacturer specific things. Not Diagnostic. It has a specific initialization procedure and an own set of Service ID´s. An example: Read current Data: OBD 01, KDS 21. On the top of it, the PID´s which are requesting the specific values, are completely different. RPM 0C in OBD and 09 in KDS. Also the response has a different length and calculation.

So you´d need a L9637D-Chip to change Serial to K-Line. Then you have to find out how to change the Init-Sequence to wakeup the ECU (ELM do not have KWP2000 (ISO-14230), its a gambling to find a similar one). The device, which is connected to the ELM327 has to translate all ServiceID´s and PID´s. The calculations are different, so this has to be fixed also. Last but not least, the PID-Lists have to be manipulated to the translated ones. That means (if the init works) you have to write a software from the scratch to communicate with the ELM327 and fake & translate all that values.

All of that is done by my project, pretending to be a real ELM327.

Thanks a lot!!!!

Saved me a lot of time! I´ve have tried a lot with ELM327, because I can´t find the L9637 in Brazil and the ELM have a interface that seems to be similar, but is not. So I bought the IC from ebay yesterday, so I will wait it arrive to continue with project. Thanks again!!!

Best Regards!

Great. I can't believe that I miss this topic up to now. Have a karma. +1

I´ve updated the code on GitHub. There have been some precision problems, which are solved now. Also as announced, I removed the LCD part. Some AT-Commands where added to fit to more OBD-Apps.

Then I began to "Sniff" some diagnostic stuff like Error-Codes and how to clear them. But currently I´m in the middle of investigating and testing it. It will take some more time.

During that, I came to a big limitation of sending and receiving messages. Everything was created for a specific Mode and ServiceId. So I started to rewrite that also. KWP2000 is a precursor of Unified Diagnostic Services, which helps what is possible and which ServiceID´s are available.

The whole code will be rewritten more clean and flexible, then.

I've also been busy to connect an Arduino to my motorcycle (Kawasaki Z750 2004), I'd like to thank you a lot for your effort. Especially determining the conversion formulas to get actual engineering units is really useful. I put together this basic library for the KDS serial communication which you can find on my Github, which you might find useful.

Did you try to request localidentifier 0xBF? I get a large response message of 72 bytes (including headers and checksum), which might contain interesting data.

Hi Scissor, you are welcome! I´m glad to find more and more combatants. The list of known PID´s is far from being complete, maybe we will get further with some people :)

Many PID´s base on try & error, some where collated with a 3rd-party diagnostic device. Hard enough to calculate the values to a meaningful result, even harder to make it OBD II understandable :D

I´ve not investigated 0xBF. Is it officially supported by '04 Z? As you can see in my Excel-Sheet, its not part of my PID-List. There are diagnostic functions, which are sending more frames at once (f.e. freezed data) or constantly (for exhaust testing purposes). I´ll try that, after I raised my buffer to such a huge value :-O

If you´d like to, run my SniffEcu()-Method and send me your results, we will have '04 Z750, '12 Z750r & '14 Z1000 results.

There are many things to investigate. Likewise ECU-ID, which is 1A without a ServiceID. I´m sure, there is a lot more! Currently I search for trouble-codes SID 0x13 and freezed Data SID 0x12. Not very easy without a failure ;)

You are theoretically able to down- and upload even the ECU firmware :)

Thank you. I understand it's difficult to obtain those conversion formulas, I guess they're mostly similar to the formulas for the Suzuki Diagnostic System (SDS). Unfortunately only very few are made public so far.

I made a simple datalogger which I used to find available PIDs (I call them localidentifiers, as in the ISO documents). So far I ran the datalogger from localidentifier 0x00 to 0xFF. I've listed the results in an excel file and it looks very similar to your results, I will post them here shortly.

One remarkable thing is that localidentifier 0xDB does not return anything, not even a negative response message. From the ISO-14230 documents:

0x00 - reservedByDocument 0x01 - localIdentifierScalingTable 0x02-0xEF - localIdentifier 0xF0-0xF9 - dynamicallyDefinedLocalIdentifier 0xFA-0xFE - systemSupplierSpecific 0xFF - reservedByDocument

Hence I expect that the most standard sensor data should be in the 0x02-0xEF range. Unfortunately the 0x01 request does not return the scaling table which would reveal all conversion formulas.

In your excel sheet you mention "Supported PIDs" several times. I don't really understand how you obtain the available localIdentifiers based on that response message. At first I thought it just might be a bitmask i.e. convert the HEX values to bits, and assume every "1" corresponds to an available localIdentifier. However this does not correspond to the results I get.

There are many more things we might try e.g. periodic transmission and dynamicallyDefinedLocalIdentifier to construct a localIdentifier, with this a much higher data rate could possibly be reached.

I also tried once to request all DTCs, but without luck. I might have done something wrong, so I can try again soon.

Indeed, at some point we can decide to use the upload and download functions described in the ISO-14230 document. However, I expect it is a rather delicate process which can easily screw up the ECU if something fails during data transmission.

Attached are the two excel sheets containing the data from a few minutes of datalogging. The datalogger is capable of logging at approximately 7 Hz to a microSD card, which could be increased by fine tuning the serial communication timing parameters.

KDSZ7502004.zip (44.2 KB)

Thank you very much for your data! I will include it into my document.

In your excel sheet you mention “Supported PIDs” several times. I don’t really understand how you obtain the available localIdentifiers based on that response message. At first I thought it just might be a bitmask i.e. convert the HEX values to bits, and assume every “1” corresponds to an available localIdentifier. However this does not correspond to the results I get.

It is exactly working like that!
You will receive a PID-List on

  • 00 (Supported PID´s 1-32)
  • 20 (Supported PID´s 33-65)
  • 40 (Supported PID´s 66-96)

00 gives me Hex: DF F7 87 87
Which is in Binary: 1101 1111 1111 0111 1000 0111 1000 0111
Now you can overlay the binary to PID 1-32.
0x01 supported
0x02 supported
0x03 unsupported
0x04 supported

That´s what my sniffer-functionality does:

const uint8_t pidList[] = { 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0};
const uint8_t pidListCount = (uint8_t)(sizeof(pidList));
void SniffEcu()
  //Get first PID List
  for (int i = 0; i < pidListCount; i++)
    //Copy Array  from last PID List before it will be overwritten:
    uint8_t ecuResponseCopy[7];
    memcpy(ecuResponseCopy, ecuResponse, 7);        
    //Result: PID List
    //80 F1 11 06 61 00 DF F7 87 87 CD
    //                  DF F7 87 87
    //0000 0000 0000 0000 0000 0000 0000 0000
    //01 02 XX 04 05 06 07 08 09...
    //For each Hex value in ECUresponse (4 bytes, without checksum)
    for (uint8_t j = 2; j < 6; j++)
      byte bin = ecuResponseCopy[j];
      //0x80 = 1000 0000 | Shift 1 to the right
      for (int mask=0x80; mask != 0; mask >>= 1)
        if (bin & mask)           

KWP2000 / ISO-14230 is a previous version of Unified Diagnostic Service, which gives you a small impression of what is possible.
As you an see there, it is not possible to upload the FW without a special token from Kawasaki. But there is enough to mess up :smiley: So stay careful :slight_smile:

7Hz is very good! Due to the BT connection, slow Serial2 and my bad code, mine is about ~4-5.

Using the bitmask method to find supported localIdentifiers does give some strange results. Whenever the bitmask is "1", the response method is not always positive, many general reject messages (0x7F 0x21 0x10) are given as well. You have got the same result in your excel sheet. Is it related to secured registers? Also the 0xBF seems not to be supported, yet I get a very long response message which at this point appears to be related to pressure measurements.

Soon I will take the motorcycle for a ride while datalogging. Do you know if it's safe to log at a high rate? I can imagine the ECU will spend most time controlling the ignition especially at high RPM, additional serial communication might block functions which could result in strange behaviour.

Edit: In the mean time I did a little calculation on the speed of the communication. From the ISO-14230 documents the minimum timing parameters are given as:

Inter byte time ECU response: 0 ms Request-response delay: 25 ms Response-request delay: 55 ms Inter byte time request: 5 ms

Using these parameters in combination with a baudrate of 10400 bits/s = 1300 bytes/s, the resulting maximum request-response rate is about 7.8 Hz (assuming a request of 7 bytes and a response of 9 bytes) and a data rate of approximately 125 bytes/s. This is very close to my datalogging rate at approximately 116 bytes/s (3138 bytes in about 27 seconds). Note that over 50% of the time the response-request and request-response delay are active.

Sending one byte in a request takes at least 5.8 ms (5 ms delay + 0.8 ms due to the baudrate), whereas the response byte can be send in just 0.8 ms. Therefore to achieve very high data rate we should try to use periodic transmission (send 80 11 F1 02 10 82 (instead of 80 for regular diagnostic session) 16). In this case, the rate increases to rougly 16 response messages per second. Additionally we can try to see if it's possible to reduce the Response-request delay and the inter byte time for requests in order to obtain higher datalogging speed.

The bitmask is fine. But some values like Injector timing, CO² Adjust timing, ect. are only working during a diagnostic session. It has to be initialized separately (somehow). I don´t expect it is related to a security layer. There are testing scenarios for exhaust or injection, which can be run and I´d expect they will activate these sensors.

The PID´s are valid globally. Some are available during current data 0x21, some by freezed data 0x12, some maybe only on testing purpose. If you take a look into the KDS3.0 Documentation(from page 21 on), you will see what is possible with the ECU.

0xBF is really curious! Can´t explain it right now. But due to it´s "hidden" location way at the end, it will be a undocumented testing feature, I guess.

Yes, you are safe! You cannot break anything. The diagnostic is on a very low layer. If you are to fast, it will just cut off the connection and that´s it. It is not possible to use important capacity or block anything, everything is separated. That´s how a BUS works :)

Thanks, the timing calculations are very interesting! Currently I´m waiting 10ms between every byte. I´ll try to change that to 5. But I´m still limited by my Bluetooth communication :( I´m sure I can delete my request delay, because it´s already taken by BT! (I should measure that somehow)

The ISO Documentation also says how to manipulate these timings :D

  • Format 80
  • Target 11
  • Source F1
  • Length XX
  • AccessTimingParameter Request Service Id (0x83)
  • Timing Parameter Identifier (00 read limits, 01 set to default, 02 read active parameter, 03 set parameter)
  • P2min, P2 max, P3 min, ect.
  • Checksum

Response Timing works similar to that, but with SID 0xC3.

My goal was to make OBD II dongle compatible devices/apps work with my bike. So I will always loose time by converting requests, calculating values and converting responses. Raising from ~4 to 5 or 6 messages per second would be amazing!

Right now I am designing a more straight sketch to be flexible with different SID´s and response/request length. Not very easy, because I need to convert, fake and redirect requests. Also constantly receiving? :o A challenge!

Thanks for your response! Maybe we can try to start a diagnostic session by sending 0x81 instead of 0x80 after the start communication request. If this works we might have access to the non functioning registers.

You should take a look at the library I wrote. It contains very simple functions to send and receive messages. I posted the link to my Github on the previous page.