K-line serial printing does not show anything on an oscilloscope

Hello.

I’ve bought myself an oscilloscope to troubleshoot my problem with the K-line. When I used the oscilloscope to measure the K-line signal levels when triggering the LIN_KTX pin high and low I was able to read the traffic on the K-line pin with an oscilloscope. So I was able to create the initialization start with it, but then I found out that writing through Serial1.print seemed to do nothing. Has anyone confirmed the connection between the Serial1 and LIN_KTX works? I have selected Macchina M2 board in the set up and created an example code for troubleshooting my problem. I’m not really an expert on programming nor on serial ports so I can’t really say the connection with Serial1 and K-line is broken but to me it seems like it is.

Am I right that if I printed 0x00 in hexadecimals it’d look like being grounded all the time if it’s in a loop? I’m currently printing 0x00, HEX to the Serial1 and all I see in the oscilloscope is an almost straight 5 volt line. I will be testing as much as I can but I’d like to know if someone recognized the same problem. I’m very sorry for asking such dumb stuff but I’d like to be able helping create such great open source stuff as this project. I’d like to be as much help as I can even though I believe even my working code isn’t too perfect and ready-to-use as it is.

I can post my code if someone needs it, but it isn’t much use. Also, I’ve never done it nor really know how to do it. But the code is simply setting LIN_KRX, LIN_KTX, LIN_KSLP and PS_J1850_9141 pins on (output except for LIN_KRX) and high. Then beginning Serial1 at 10400 and printing 0x00, HEX there. I receive no sign of any activity. But when I wrote LIN_KTX high and low I was able to read it on the oscilloscope.

Best regards,
mato1223

Inspecting schematics and finding out LIN_KTX pin (or actually the K TX pin on the left connector of the processor), I’ve found out it does nothing on the event of serial print. It does not go low as it’s supposed to or anything. So there might be the problem.

When trouble shooting such things I normally would use not just 0 but also FF and possibly mixes… I know nothing about K-line at this time but when looking you want to try all cases as you may be missing something.

Well. I’ve done some research on the K-line and it idles high and is driven low, so a logical 0 equals 0 volts, a logical 1 equals 12 volts (in this case 5 volts because it’s powered through USB) and the idle state is 12 volts (5 volts). So printing out a 0xFF would result in the same as idling the line, just the same as having no communication. I also tried 0x55 and other cases where there’d be some change between the bits. Still no effect.

I haven’t messed with the K-line stuff yet but have you tried powering the M2 from an ODBII port while testing it? It may require the 12 volts to work. You can have both the USB and the 12 volts from the ODBII port. I do this on my workbench now.

When you didn’t say you tried other values I assumed you only tried the 0x00 value. Sometimes you can tell if a device isn’t doing what you think it should by sending other values and seeing something happen.

Since I haven’t tinkered with the K-line stuff yet I am more or less shooting in the dark here trying to insure you tried everything I would try. I know a device I have sitting here that I use to develop with has to have 12 volts for some functions to work although others work fine just off USB but that’s not an M2.

I now know what is the problem. I tried writing serial directly to the K-line and it worked. But when I had sent initialization sequence it made it impossible to use Serial1 for ouptut at least. Perhaps for input as well? So driving the LIN_KTX pin as output and high makes it unusable for Serial communications. How can this be fixed?

I will try to drive the K-line with only serial communication in order to keep it up and serial communication doesn’t require any sort of setting the RX and TX pins I suppose. Now my problem is though that I have to send 0x33 to the car (also to begin it with a 200ms low and end with a 200ms high) at 5 bits per second. Is serial able to do that?

Also I have to receive 0x55 at the speed X for determining the speed. (10.4 kilobauds quite often, only it can’t be relied on) This would make it comply with the ISO standards if I’m correct. I’ve gathered the ISO 14230 documents but haven’t found any ISO 9141 documents yet.

And thank you redheadedrod for your help on my problem. I’m very glad to receive some interest in this subject even though you don’t have all the information required. Do you have a solution for the problem of serial communication becoming unavailable after driving the LIN_KTX pin high and low? Can it be reset somehow back to serial?

Best I can offer is there IS a K-line library out there that works already. Check out the “Project spotlight” and you may be able to find it. Even if not fully compatible with your vehicle you should be able to see how they do what they are doing and use that. It MAY be another protocol than what your looking for but should answer most of your questions.

https://showcase.macchina.cc/

And more specifically:


Well, I haven’t looked into the OBD9141 library too much anymore. I did try to access my car with the library. I even used it as a base for building my own software for accessing the K-line. Only it seems that it does not work for some reason. It didn’t get through to the car at all. I didn’t get any response, no matter how I tried. I even had serial monitor print me out some debugging data. I received no information on serial as I’d expect it to do now, not even with the OBD 9141 library. I’m quite sure it’s the Serial1 that’s broken out by LIN_KTX and LIN_KRX, or the fact that changing them to do anything will result in a broken Serial1 connection. I’m not sure if the SAM datasheet would answer my question better but the problem is probably the serial connection after using TX and RX pins for something else than Serial…

And one of the reasons for building my own K-line library is that the OBD 9141 isn’t fully compatible with K-line. It is ISO 9141 compatible, but not compatible with all the cars using the K-line. Also it isn’t Macchina M2 made, it’s Arduino made. And for a person who does not wish to program their own readers it’d be helpful to have a library already fully compatible with the K-line made for the M2 so that they wouldn’t need to do anything more than to upload the code and plug in the M2.

Thank you redheadedrod, I’ve solved my problem. Looking again in the OBD 9141’s code I found out what I was looking for all along. It is the SAM’s problem, that the registers for the pin had to be set, and after that I have my code working. I’ll be looking further into how I can get my car work with my code, but it seems to be possible after all.

Glad I could help. I am in the process of writing a new firmware package for the M2 which is intended to be more modular. I have the SWCAN, CAN and J1850VPW stuff to work from now but will be looking to add support for the K-Line stuff and J1850PMW stuff once I get this stuff running so certainly will be interested in incorporating anything that people come up with.

I would love to take a look at your code I’m about to start working on a new project and I think it would be a big help

1 Like

Hi,

Could you please share the code fix that you made to make this work ?? Thanks in advance.

Hello.

I’m really sorry I haven’t had the time to do anything and I went through a complete reinstall (accidentally too complete) so I had everything started again. I can post the code for this project someday next week if someone needs it. It seems to me that it is quite easy to modify the OBD9141 code to work with ISO 14230 if someone has the time. I’m currently studying and working at the same time so I have minimal free time and even less to spare on extra projects. All I really have programmed is the initialization sequence and am hoping to find a back up of it somewhere. It’s not too hard to program though. I’d like to know if anyone has a car with ISO 14230 slow init. It’d help out programming a complete initialization sequence.

Here’s my code for testing K-line for ISO 14230 fast initialization. It’s quite simple and really is no use for anything at this point. I’m hoping to advance this piece of code but currently I’m busied by studies and work all along with my family life. It first lights up some LEDs (using only the 5 LEDs, without RGB-LED because I had to mod my M2 a bit after accidentally using a bit too short cable and cracking up the micro-USB port). I’m hoping to buy a new M2 with the ESP32 at some point in order to work better without the hassle with cables.

But to come to the code here’s how it works. It contains four extra functions, two for serial communications, one for initializing with the fast initialization and one for creating a checksum. This code worked with my car to establish a connection and then send a message I really can’t remember what it is. It’s been quite a while since I wrote the code so I can’t remember which command I used but it should show how it typically works to communicate over ISO-14230. I once had some code that established the connection and then terminated it for it wasn’t required to keep up the communication any longer than that for securing the proper working of the code. I was able to receive a stop communication positive response.

The start communication request is sent first by triggering the K-line LOW for 25 ms, then HIGH for 25ms and then sending the start communication request (0xC1, 0x33, 0xF1, 0x81, checksum) (checksum calculated with online calculator: 0x9A, but for communications the checksum created by the code did work well). Currently it only checks the checksum and is forced to skip it if it’s wrong. What it checks is the fourth byte which is the positive response. According to the ISO14230-2 the fifth byte is the response byte but according to the ISO14230-4 the OBD communications shall not use the additional length byte (normally the fourth byte).

Also remember the requirement of the two functions which change the way the serial pins are used. They are REQUIRED for M2 to work properly because of the way the processor was built. You won’t be able to start serial communications if you won’t trigger the pins.

If someone wishes to learn more about ISO14230 communications I may be able to help for I have a copy of these documents. I hope someone will find some time to further improve the K-line communication on the M2. I will try to find some time on some point but currently I’m too occupied by other things. If someone needs help I can try to find the time to read the forum and help as much as I can. I hope someone tries out the sketch because then we can confirm it works on cars with fast initialization.

The code could be extended with functionality for 5 baud initialization as well but that would need quite a bit of modification for the code. Easiest way would be to edit the OBD9141 library’s initialization function because it already works. Only that I can’t confirm whether it works or not on a car because I don’t have a vehicle to test it on. If someone needs that I can try to build a working initialization sequence but it will require someone who has a car that uses slow initialization. If you are not sure whether your car uses ISO9141 or ISO14230 but have a K-line connection you could try OBD9141 and this sketch to try initialization. If it works on neither it could be the 5 baud initialization quite near the OBD9141 initialization sequence only with different key codes. If that happens to you you may feel free to contact me for a working sketch. Debugging sketch will be really simple by modifying the OBD9141 code so it’ll be a fast debug process. I can’t build a complete one for communication because I’m not a “real” programmer. I understand how code works in some manner and am able to build my own but I really don’t have enough of education for building a whole library with code simple enough. I’d be glad if I could help someone out.

#include <Arduino.h>

#define DEBUG

int KLineBaudrate = 10400;

byte stopCommunicationRequest[8] = {0xC1, 0x33, 0xF1, 0x82};

void setup() {
  pinMode(DS7_RED, OUTPUT);
  analogWrite(DS7_RED, 2500);

  pinMode(PS_J1850_9141, OUTPUT);
  digitalWrite(PS_J1850_9141, HIGH);

  pinMode(LIN_KSLP, OUTPUT);
  digitalWrite(LIN_KSLP, HIGH);

  pinMode(LIN_KRX, INPUT);
  digitalWrite(LIN_KRX, HIGH);

  pinMode(LIN_KTX, OUTPUT);
  digitalWrite(LIN_KTX, HIGH);

  pinMode(LIN_LSLP, OUTPUT);
  digitalWrite(LIN_LSLP, HIGH);

  pinMode(LIN_LTX, OUTPUT);
  digitalWrite(LIN_LTX, HIGH);

  pinMode(DS2, OUTPUT);
  digitalWrite(DS2, HIGH);
  pinMode(DS3, OUTPUT);
  digitalWrite(DS3, HIGH);
  pinMode(DS4, OUTPUT);
  digitalWrite(DS4, HIGH);
  pinMode(DS5, OUTPUT);
  digitalWrite(DS5, HIGH);
  pinMode(DS6, OUTPUT);
  digitalWrite(DS6, HIGH);

  #ifdef DEBUG
  SerialUSB.begin(19200);
  while(!SerialUSB);
  SerialUSB.println("Beginning program!");
  #endif
}

void loop() {
  digitalWrite(DS2, LOW);
  bool initialized = 0;
  if (initialized == 0){
    initialized = KLineInitializeFast();
  }
  while (initialized){
    // Looping after initialization
    byte buf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    digitalWrite(DS6, LOW);
    delay(50);
    Serial1.write(0xC2);
    Serial1.write(0x33);
    Serial1.write(0xF1);
    Serial1.write(0x01);
    Serial1.write(0x0D);
    Serial1.write(0xF4);
    delay(5);
    Serial1.readBytes(buf, 6);
    for (int i = 0; i < 6; i++){
      buf[i] = 0x00;
    }
    Serial1.setTimeout(50);
    Serial1.readBytes(buf, 8);
    for (int i = 0; i < 8; i++){
      SerialUSB.print(buf[i], HEX);
      SerialUSB.print(" ");
    }
    SerialUSB.println();
    delay(2000);
    for (int i = 0; i < 4; i++){
      Serial1.write(stopCommunicationRequest[i]);
    }
    Serial1.write(createChecksum(stopCommunicationRequest, 4));
    delay(5);
    Serial1.readBytes(buf, 5);
    for (int i = 0; i < 5; i++){
      buf[i] = 0x00;
    }
    Serial1.setTimeout(50);
    Serial1.readBytes(buf, 8);
    for (int i = 0; i < 8; i++){
      SerialUSB.print(buf[i], HEX);
      SerialUSB.print(" ");
    }
    SerialUSB.println();
    delay(2000);
  }
  #ifdef DEBUG
  SerialUSB.println();
  SerialUSB.println();
  SerialUSB.println();
  #endif
  digitalWrite(DS2, HIGH);
  digitalWrite(DS3, HIGH);
  digitalWrite(DS4, HIGH);
  digitalWrite(DS5, HIGH);
  digitalWrite(DS6, HIGH);
}

bool KLineInitializeFast(){
  digitalWrite(DS4, LOW);
  #ifdef DEBUG
  SerialUSB.println("Fast Initialization.");
  #endif

  byte buf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  KLineBaudrate = 10400;
  digitalWrite(LIN_KTX, HIGH);
  digitalWrite(LIN_LTX, HIGH);
  #ifdef DEBUG
  SerialUSB.println("Delaying...");
  #endif
  delay(300); // Must delay the W5min time (300 ms)
  #ifdef DEBUG
  SerialUSB.println("Sending the initialization sequence, then the StartCommunication request.");
  #endif

  digitalWrite(LIN_KTX, LOW);
  digitalWrite(LIN_LTX, LOW);
  delay(25);
  digitalWrite(LIN_KTX, HIGH);
  digitalWrite(LIN_LTX, HIGH);
  delay(25);
  KLineBeginSerial(KLineBaudrate);
  Serial1.write(0xC1);
  Serial1.write(0x33);
  Serial1.write(0xF1);
  Serial1.write(0x81);
  uint8_t forChecksum[4] = {0xC1, 0x33, 0xF1, 0x81};
  Serial1.write(createChecksum(forChecksum, 4));
  SerialUSB.print("Checksum to send: ");
  SerialUSB.println(createChecksum(forChecksum, 4), HEX);
  delay(5);
  Serial1.readBytes(buf, 5);
  for (int i = 0; i < 8; i++){
    buf[i] = 0x00;
  }
  Serial1.setTimeout(50);
  Serial1.readBytes(buf, 8);
  #ifdef DEBUG
  for (int i = 0; i < 8; i++){
    SerialUSB.print(buf[i], HEX);
    SerialUSB.print(" ");
  }
  SerialUSB.println();
  SerialUSB.print("Checksum created: ");
  SerialUSB.println(createChecksum(buf, 7), HEX);
  #endif
  if (buf[6] == createChecksum(buf, 6)){
    if (buf[3] == 0xC1){
      return 1;
    }
  } else{
    if (buf[3] == 0xC1){
      return 1;
    }
  }
  return 0;
}

void KLineBeginSerial(unsigned int baudrate){
  g_APinDescription[LIN_KRX].pPort -> PIO_PDR = g_APinDescription[LIN_KRX].ulPin;
  g_APinDescription[LIN_KTX].pPort -> PIO_PDR = g_APinDescription[LIN_KTX].ulPin;
  Serial1.begin(baudrate);
}

void KLineEndSerial(){
  g_APinDescription[LIN_KRX].pPort -> PIO_PER = g_APinDescription[LIN_KRX].ulPin;
  g_APinDescription[LIN_KTX].pPort -> PIO_PER = g_APinDescription[LIN_KTX].ulPin;
  Serial1.end();
}

uint8_t createChecksum(uint8_t message[8], int lengthOfMessage){
  uint8_t checksum = 0;
  for (int i = 0; i < lengthOfMessage; i++){
    checksum += message[i];
  }
  return checksum;
}

Thank you for sharing the code. What do you mean by the above statement ? Could you please elaborate ?

So these are the functions I am talking about:

void KLineBeginSerial(unsigned int baudrate){
  g_APinDescription[LIN_KRX].pPort -> PIO_PDR = g_APinDescription[LIN_KRX].ulPin;
  g_APinDescription[LIN_KTX].pPort -> PIO_PDR = g_APinDescription[LIN_KTX].ulPin;
  Serial1.begin(baudrate);
}

void KLineEndSerial(){
  g_APinDescription[LIN_KRX].pPort -> PIO_PER = g_APinDescription[LIN_KRX].ulPin;
  g_APinDescription[LIN_KTX].pPort -> PIO_PER = g_APinDescription[LIN_KTX].ulPin;
  Serial1.end();
}

These look a bit weird even to me for I honestly am not as good a programmer as I wish I was but they are simply commands that edit the registers. So what it wants to do here is to change the function of the pin LIN_KRX and LIN_KTX from being a simple I/O pin to a serial pin and vice versa. The reason these two functions must be used is that the Arduino Due cannot change it back from one mode to another without manipulating the registers. For a better explanation you should refer to the datasheet of the processor.