Read serial data from motorcycle K-line

Hi all,

I'm trying to read incoming data from my motorcycle's K-line, which is a bi-directional serial line.

I found an individual who had done this very thing, so I basically followed almost exactly what he did, with a few adjustments:

The bike communicates with a 12v signal, and he used a special chip to convert that data to 5v so the Arduino would like it. (L9637) I tried to order a similar chip, but I was not working for me.

I ended up using a Zener clipping circuit into a schmitt trigger (74HC14) to create the 5v output to send to the Arduino. The problem is it actually works...only some of the time. At first I thought I had messed up the circuit between tests, so I took a picture of it the last time it was working. The circuit is simple enough that I don't think I am 'breaking' it.

I attached 4 images:

  1. what I get from the serial monitor when things are working correctly
  2. what I get from the serial monitor currently, plus the code
  3. a drawing of my circuit
  4. a crappy photo of my breadboard

I'm sure this could be a number of things. I have an oscilloscope that has shown me a few things. The signal from the bike is solid. I can see the square wave perfectly. The clipping circuit seems to be working properly, however when I hook power or ground from the arduino into the circuit, there seems to be a sine wave of noise that 'adds' itself to the square serial signal. I am teaching myself electrical theory from the ground up, so when it comes to electron flow and where I should be grounding things I may not have the best ideas for circuits.

Thanks!

Can you post your code (using code tags please) - a photo of it is no good.

Is it a mistake in your drawing that kline seems to be connected to gnd and gnd to signal?

...R

of course, sorry about that. Code attached. I've also attached four images of my oscilloscope--each have an image of the circuit with them showing where it was attached. The first three images did not have the arduino positive, ground, or pin 10 connected (as illustrated).

From my limited understanding, the ground side of the zener clipper circuit is running to IN to the schmitt trigger and the K-line side is running to GND because it inverts the logic of the serial signal. You can see this in the images I attached. I could be wrong, but I think it makes sense....

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(10, 11); // RX, TX
int rpm, spd;

 
void setup() {
    // initialize both serial ports:
    Serial.begin(115200);
 
    // set the data rate for the SoftwareSerial port
    mySerial.begin(16129);
}
 
void loop() {
    static int count = 0;
 
    // read from port 1, send to port 0:
    if (mySerial.available()) 
      {
        int inByte = mySerial.read();
 
        if (inByte == 0x01 && count >= 5) 
          {
            Serial.println(' ');
            count = 0;
          }
 
        Serial.print(inByte, HEX);
        Serial.print(' ');
        ++count;
      }

}

Why have you such a strange baudrate for SoftwareSerial - 16129. Why not a standard baud rate - 9600 or 19200? I don't know what SoftwareSerial would make of such a number, and an odd number at that.

How come you have 3 fairly sensible traces but the one going into the Arduino is all screwed up with a sine wave? What is causing the sine wave? That must be eliminated. Leave the 'scope ground where it is for that test and re-run the other tests - perhaps there is a poor connection somewhere. Or maybe it won't work with the GND and k-line the wrong way round.

If you need to invert a signal just feed the output through a second schmidt trigger on the 74HC14.

The normal serial ttl communication protocol idles HIGH and a 0 is LOW and a 1 is HIGH. So the stop bit is HIGH (actually just the start of the idle HIGH) and the start bit is LOW - the first HIGH-LOW transition from the idle state starts the bit timing.

Do you have a link to a document with the specifications for the K-line protocol?

And please post smaller pictures - 640x480 or 1280x960

...R

Thanks for the reply--the strange baud rate was chosen because that is the rate (or close to) what the bike communicates at. There are no documents on the bike's k-line specs, all of this has been reverse engineered. I've read a bit on the ecuhacking boards, and also from the link I linked to in my first post. The baud rate was 'determined' by measuring the duration of the shortest bit that the oscilloscope recorded. I had to add the special baud rate to the SoftwareSerial.cpp in order to make it work with for the Arduino. There are two baud rates that have worked, for me, 16064 and 16129. I don't know a lot about serial communication and baud rates, but I did wonder what the Arduino would think of the strange rate. I followed these instruction to determine the rate:
https://www.kumari.net/index.php/random/37-determing-unknown-baud-rate

The last trace is definitely messed up, I have tried new connections, new wires, replacing components etc.
I will try sending the K line directly into the schmitt trigger and inverting the signal by using a second schmitt trigger.

A few questions:

If I supplied 5v to the circuit without the Arduino, and only sent the output of the Schmitt trigger to pin 10, would I still have to ground the Arduino into the circuit?

The other option I mentioned in my first post was to use an IC chip that is designed to convert K-line to RX/TX. I wasn't fully sure on what the difference between the VCC (Stabilized voltage supply) and VS (Supply Voltage). Do I need to send 12v to one of them? The datasheet is here:
http://www.st.com/web/en/resource/technical/document/datasheet/CD00000234.pdf

Also there was no marking on the chip to tell me which was the first pin. I read somewhere that if I oriented the chip so that I could read the label, pin 1 would be on the bottom left. Is that correct?

Sorry about the large pics, I'll scale them down in the future.

I think you are obliged to use that chip, or one like it. I don't think the k-line is itself a serial TTL signal such as the Arduino expects. Also I guess the k-line is bi-directional and the way you have it wired it can't be. Notice that the chip has Rx and Tx connections for the Arduino.

I contributed to a long Thread here which, I suspect, was dealing with an almost identical problem. There was a special chip in that case also. More importantly, if you read through the Thread you will get an idea of the process of interfacing with that type of bus. As you will see a big problem was how to detect the start of a transmission. Feel free to use some or all of the code from that Thread if it is useful.

The diagram at the top of the PDF shows the usual dimple marking pin 1 - apart from that I have no idea.

I don't think that measurements of the pulse widths on the k-line will tell you anything about the baud rate. I can't immediately see how it is determined - but it is almost certainly one of the standard rates.

I will try to study the PDF a bit more.

...R

After some more reading ...

I don't think that L9637 chip does much apart from convert the voltage levels and provide a ,means to inject data into the k-line.

So maybe if you only want to read data it is not essential - but the strange sine wave on your 'scope must be eliminated.

And it does look like you may need a strange baud rate. It looks like the L9637 just passes on exactly what it receives.

I think the first step should be to get your hardware working and then get a 'scope picture of a few bytes of data so there is something for us to talk about.

I have no idea if SoftwareSerial can work properly with strange baud rates. I wrote some code here that can be used instead of SoftwareSerial. It should be easy to modify it for strange baud rates - in case that is any value.

...R

Robin2:

Thanks for your responses, I appreciate your effort and willingness to help!

A bit more info about the project:

I am trying to build an instrument cluster for my bike, a display that shows Speed, RPM, fuel level, turn signals, high-beams, etc. To get all the data from the bike, originally I was going to read the sensors directly, the two tricky ones being RPM and speed. The RPM was determined from the crank position sensor (CPS) -- an inductive sensor, and speed from the speed sensor which spit out a 12v square wave whose frequency varied with speed. I got that to work consistently, however I ran into a big snag...I had decided to use NeoPixel LED panels for my display, which use a special library that temporarily disables interrupts when it refreshes the pixels. I was using interrupts to read the CPS, and that messed up my readings.

At the same time I realized the chip in the ECU of the bike communicated via a type of serial. This was cool because I could potentially get more useful data than I initially had hoped for (error codes, engine temp, etc.).

I realized the other day that the Serial.available also seems to rely on interrupts...which puts it at odds with the NeoPixel display as well. Provided I am able to get the data from the bike into the Arduino, I may have to use a second arduino to drive the display, unless there a workaround I am overlooking.

Regarding reading the data from the K-Line, I read through the thread that you posted, and I definitely see how important and tricky it can be to detect each transmission. From what I understand, the bike sends 6 bits of info when you turn the key and start it. This probably changes if you send the ECU commands. The one variable I have not taken into account yet is that all of my tests have been with the stock instrument cluster attached to the bike. My end goal would be able to remove it completely, and have my cluster operating in it's place. However, I'm guessing the stock cluster can send commands to the ECU, and it may very well be doing that when the bike starts in order to get a response with the 6 bits of data that I am currently seeing.

That being said, there is a logical sequence of steps to take like you inferred.

First I'll get my hardware working. Set up the L9637 circuit spit out data that I can see on the oscilloscope.
Then I can try to determine the pattern and parse the data into usable variables.

I'll worry about removing the stock cluster, sending commands down the K-line, and my display/interrupt issues later.

I don't know anything about the Neopixel stuff but if it is blocking interrupts for any noticeable period of time the software is very poorly written. Is it essential to use that software? If it is only switching LEDs on and off I wonder why it needs interrupts at all.

...R

Neopixels are a special type of RGB LED that has circuitry that allows you to control huge strings of them with one data line on from an arduino. The transmission protocol for the pixels has very strict timing constraints which requires it to turn off interrupts while sending the data. The Library was written to be simple and versatile, not necessarily efficient. Here's a link that to Adafruits guide on the subject if you're interested:

That being said, I was able to connect my L9637 chip to the bike and successfully receive the Serial data! My issue with using that chip before was that I wasn't sending it 12v from the bike to the Voltage Supply pin. That being said, I proceeded to try and communicate with the bike after removing the stock instrument cluster. I did not have much luck there. Here's what I understand:

The instrument cluster sends bit 0x01 down the k-line, and the ECU responds with 5 bits of data. With the stock instrument cluster attached to the bike, I can receive all six of those bytes. When I remove the cluster, I have to send the ECU the command 0x01 to get the data back. I am not sure exactly how to do this, but here is my attempt:

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(10, 11); // RX, TX
int previousMillis;
 
void setup() {
    // initialize both serial ports:
    Serial.begin(115200);
 
    // set the data rate for the SoftwareSerial port
    mySerial.begin(16129);
    Serial.println(' ');
    Serial.print("hello world");
    Serial.print(' ');
}
 
void loop() {
    static int count = 0;
    
    
    if (millis() - previousMillis > 10)
    {
    // send 0x01
      mySerial.write(0x01);          
      Serial.println("SENT");
      previousMillis = millis();
    }
        
    
    // read from port 1, send to port 0:
    if (mySerial.available()) 
      {
        int inByte = mySerial.read();
 
        if (inByte == 0x01 && count >= 5) 
          {
            Serial.println(' ');
            count = 0;
          }
 
        Serial.print(inByte, HEX);
        Serial.print(' ');
        ++count;
      }
}

So I'm sure there is a proper way to send the byte and then wait for a response, but regardless, there was another issue that I discovered. I connected my scope up to the K-line, and I attached readings of A) the 0x01 byte that the instrument cluster sends when it is attached and B) the 0x01 byte that gets sent from the Arduino when I run the above code. The readings look different enough for me to assume that the bit I am sending is not shaped correctly, and will not be read by the ECU as 0x01. The cluster byte is 12v, and the one from the Arduino is only 5v....I assume there is some noise entering the signal, and I don't really understand why the Arduino's is 5...I assumed the chip would convert it to 12v. Any suggestions would be great, but I am going to go triple check my connections.

One of the things with the BMW IBus project I referred to earlier was the need to ensure the bus was idle before starting a transmission and I think it was also necessary to check that the transmission worked properly in case another device decided to start transmitting at the same time - in which case both would have to try again. It may be that this is not a problem with your 'bike system.

In your post you seem to be using the words "bits" and "bytes" interchangeably. That can be very confusing.

mySerial.write(0x01); should send a byte value of 1.

I wonder if you should send the 0x01 byte more than once? Your code sends it every 10 msecs. I would extend that to (say) 10 seconds for testing. At this stage I guess all you want to do is see if you can trick the ECU into responding.

I don't understand this

The cluster byte is 12v, and the one from the Arduino is only 5v

Can you capture your 'scope pictures so you can post some of them - please keep the size to about 640x480.

If you google this phrase CS497_presentation_miller.pdf you will see a document that may be useful. I can't figure how to get its full URL.

...R

Although you've identified the baud rate, you may still have the incorrect parity and/or stop bits.

I know from my work on the BMW system that it uses even parity (8E1) instead of the default no parity (8N1). You should be able to see what's being sent from the scope traces though.

Ian.

I meant to attach these scope readings, here they are:

clusterbyte.jpg - what the stock cluster sends the ECU
arduinobyte.jpg - what the arduino sends

clusterbyte.jpg

arduinobyte.jpg

Can you show the wiring diagram that was used to produce each of those graphs? Also, what are the vertical scales?

At least the two pictures have the same general shape so you are not too far from success.

@ian332isport makes a good point about the parity setting. I had forgotten about that. If you don't have documentation you will probably have to figure out the correct option from the graphs or by trial and error.

...E

Attached is the circuit that I have used for both tests. Sorry I never clarified by what I meant by:

The cluster byte is 12v, and the one from the Arduino is only 5v

What I was trying to say is that the byte that was sent from the cluster peaked at 12 volts, while the byte sent by the Arduino peaked at 5 volts. Both measurements were take at the K-line. I marked where the scope probes were attached in the image. I know the arduino sends serial data out at 5v (is that correct terminology?)...I had assumed the L9637 chip would convert the naturally 5v data to 12v before sending it down the k-line. I may be missing something in my circuit. The guy who created the project that I borrowed ideas from (Motorcycle black box part 1. Data acquisition with Arduino Mega.) had a resistor between VS and K in his circuit, but removed it because it was causing errors...I never tried that.

The vertical scale of the scope measurements is in the bottom left of each image (if I understand you correctly). 5v per grid-square for the clusterbyte.jpg and 2v per grid-square for the arduinobyte.jpg.

I have to get the scope back sometime this week to do more testing (I'm borrowing it from work). I vaguely remember seeing a single "C0" (or something like that...) come through the Serial Monitor one time before the six bytes of data would start streaming. It may be some type of command from the cluster when the key is turned on. Or I could be crazy. :slight_smile: When I get the scope I'll post more.

I'm reading up on parity/stop bits, I think I understand the idea behind them...would the fact that I can read the data from the bike correctly be proof that I am using the correct type of parity? I can take the second byte that I receive, then map(byte, 0, 255, 0, 15000) and it gives me a pretty much exact RPM value that my cluster shows. (my bikes guage goes to 15000 rpm)

My read of the L9637 datasheet suggests it should do the voltage conversion in both directions.

I think it would be worthwhile for you to study that PDF that I gave you the Google link to. I have only glanced at it. They seem to be using a different chip that does pretty much the same as yours - and I don't mean that you need to change yours, just that you may learm from their setup. Of course it mght be worth looking at the datasheet for the other chip.

Have you looked to see if the manufacturer has any application notes for the L9637?

...R

I glanced at the PDF and it looks like it has some excellent info inside! I will be sure to look over it in depth. I've done some quick searching and haven't found any more info from the manufacturer regarding the L9637...I'll try and dig a little deeper though.

Mark,

If you find the communication doesn't work with the stock instrument cluster removed, I'd be tempted to add a pull up resistor to the K-line. I suspect the instrument cluster pulling the K-line high internally, but when you remove the cluster the K-line is left to float. 510ohms seems to be the value suggested.

Ian.