OBD-II help (J1850 PWM)

Hi,

I'm trying to make an OBD2 interface using the OBD-II uart from Sparkfun, but i'm having a bit of trouble getting the right data. I'm using the following code which i found in this thread Arduino Forum

//This is a character buffer that will store the data from the serial port
char rxData[20];
char rxIndex=0;

//Variables to hold the speed and RPM data.
int vehicleSpeed=0;
int vehicleRPM=0;

void setup(){

  Serial1.begin(9600);
  Serial.begin(9600);   //This is for the serial debug

  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(1500);
  //Reset the OBD-II-UART
  Serial1.print("ATWS\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(1000);
  
  // turn echo off
  Serial1.print("ATE0\r");
  
  // send 01 00 until we are connected
  do
  {
    Serial1.print("0100\r");
    delay(1000);
    getResponse();    //This is only called once since the schoecho has been turned off using ATE0\r
    Serial.print("0100 Response:  ");
    Serial.print(rxData[0]);Serial.print(rxData[1]);Serial.print(rxData[3]);Serial.print(rxData[4]);Serial.print(rxData[6]);Serial.print(rxData[7]); Serial.print(rxData[9]);Serial.print(rxData[10]);
    Serial.println("");
  }
  while(ELM_CHECK_RESPONSE("0100", rxData)!=0);
   
  Serial1.flush();
   
  Serial1.print("ATDPN\r");  //Shows the protocol
  getResponse();
  Serial.print("Protocol: ");Serial.print(rxData[0]);Serial.print(rxData[1]);Serial.println("");
  // str[0] should be 'A' for automatic
  // set header to talk directly to ECU#1
  if(rxData[1]=='1')  // PWM
    Serial1.print("ATSHE410F1\r");
  else if(rxData[1]=='2')  // VPW
    Serial1.print("ATSHA810F1\r");
  else if(rxData[1]=='3')  // ISO 9141
    Serial1.print("ATSH6810F1\r");
  else if(rxData[1]=='6')  // CAN 11 bits
    Serial1.print("ATSH7E0\r");
  else if(rxData[1]=='7')  // CAN 29 bits
    Serial1.print("ATSHDA10F1\r");
  
  //Delete any data that may be in the serial port before we begin.
  Serial1.flush();

}


void loop(){
  //Delete any data that may be in the serial port before we begin.  
  Serial1.flush();

  Serial1.print("010D\r");    //Query the OBD-II-UART for the Vehicle Speed

  //Get the response from the OBD-II-UART board. We get two responses
  //because the OBD-II-UART echoes the command that is sent.
  //We want the data in the second response.
  getResponse();

  //Convert the string data to an integer
  vehicleSpeed = strtol(&rxData[6],0,16);

  //Print the speed data to debug serial window
  Serial.print("SPEED: ");
  Serial.print(vehicleSpeed);
  Serial.print(" km/h");
  Serial.println(" ");
  
  delay(20);
  
  //Delete any data that may be left over in the serial port.
  Serial1.flush();

  //Query the OBD-II-UART for the Vehicle rpm
  Serial1.println("010C\r");
  //Get the response from the OBD-II-UART board
  getResponse();

  //Convert the string data to an integer
  //NOTE: RPM data is two bytes long, and delivered in 1/4 RPM from the OBD-II-UART
  vehicleRPM = ((strtol(&rxData[6],0,16)*256)+strtol(&rxData[9],0,16))/4;

  //Print the rpm data to debug serial window
  Serial.print("RPM: ");
  Serial.print(vehicleRPM);
  Serial.println(" ");

  //Give the OBD bus a rest
  delay(50);
  
}


byte ELM_CHECK_RESPONSE(const char *cmd, char *str)
{
  // cmd is something like "010D"
  // str should be "41 0D blabla"
  if(cmd[0]+4 != rxData[0]
    || cmd[1]!= rxData[1]
    || cmd[2]!= rxData[3]
    || cmd[3]!= rxData[4])
    return 1;

  return 0;  // no error
}


void getResponse(void){
  char inChar=0;
  //Keep reading characters until we get a carriage return
  while(inChar != '\r'){
    //If a character comes in on the serial port, we need to act on it.
    if(Serial1.available() > 0){
      //Start by checking if we've received the end of message character ('\r').
      if(Serial1.peek() == '\r'){
        //Clear the Serial buffer
        inChar=Serial1.read();
        //Put the end of string character on our data string
        rxData[rxIndex]='\0';
        //Reset the buffer index so that the next character goes back at the beginning of the string.
        rxIndex=0;
      }
      //If we didn't get the end of message character, just add the new character to the string.
      else{
        //Get the new character from the Serial port.
        inChar = Serial1.read();
        //Add the new character to the string, and increment the index variable.
        rxData[rxIndex++]=inChar;
      }
    }
  }
}

My car ('02 Ford Focus) uses the J1850-PWM protocol and the code seems to initialize fine and returns the right protocol, but when i ask for live data, i just get some random numbers. Speed jumps between 0 and 13 km/h and rpm can go from 0 to 19 to 832 and sometimes even displays higher or negtive values, bot with and without the engine running.

I have spent a lot of time googling the problem and looking through the elm327 datasheet, but so far i haven't found anything that works.

I have also tried loading the OBDuino code, but that only seems to be working with the ISO-9141 protocol.

If anyone has any code or just pointers on how to get this to work with the J1850-PWM protocol i would be very happy to hear from you, as i'm starting to get a bit frustrated.

/Jakob

Which Arduino are you using? Which version of the IDE?

1.0.1 and i've tried both on an UNO and a Mega 2560.

1.0.1 and

Then you should be aware that Serial.flush() blocks until all outgoing serial data (with the exception of the last byte) has been sent.

I can't see how that is useful in your code.

OK, i've tried to remove all Serial.flush() from the code, but that doesn't seem to do anything.

Here is some output from the serial monitor if that helps anyone:

0100 Response:  ATS
0100 Response:  TS
0100 Response:  TS
0100 Response: EL32 v.3
0100 Response:  L32 v.3
0100 Response: >AE0 v.3
0100 Response: OKE0 v.3
0100 Response:  KEO v.3
0100 Response: >4 0 B 3
0100 Response:  4 0 B 3
0100 Response: >4 0 B 3
0100 Response:  4 0 B 3
0100 Response: 44 0 B 3
0100 Response:  4 0 B 3
0100 Response: >4 0 B 3
0100 Response:  4 0 B 3
0100 Response: >4 0 B 3
0100 Response:  4 0 B 3
0100 Response: >4 0 B 3
0100 Response:  4 0 B 3
0100 Response: 4100BE3E
Protocol:  1
SPEED: 190 km/h 
RPM: 12175 
SPEED: 190 km/h 
RPM: 12175 
SPEED: 190 km/h 
RPM: 12175 
SPEED: 190 km/h 
RPM: 12175 
SPEED: 13 km/h 
RPM: 832 
SPEED: 12 km/h 
RPM: 799 
SPEED: 15 km/h 
RPM: 1439 
SPEED: 0 km/h 
RPM: 0 
SPEED: 12 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 799 
SPEED: 12 km/h 
RPM: 768 
SPEED: 12 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: -14465 km/h 
RPM: 1311 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 67 
SPEED: 1 km/h 
RPM: 768 
SPEED: 1 km/h 
RPM: 960 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: 12 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 1311 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 799 
SPEED: 13 km/h 
RPM: 863 
SPEED: 15 km/h 
RPM: 67 
SPEED: 13 km/h 
RPM: 835 
SPEED: -10369 km/h 
RPM: -8225 
SPEED: 12 km/h 
RPM: 960 
SPEED: 15 km/h 
RPM: 8159 
SPEED: 3199 km/h 
RPM: 768 
SPEED: 13 km/h 
RPM: 832 
SPEED: 127 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 768 
SPEED: 215 km/h 
RPM: 13760 
SPEED: 12 km/h 
RPM: 67 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: 12 km/h 
RPM: 67 
SPEED: 1 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 768 
SPEED: 127 km/h 
RPM: 67 
SPEED: 13 km/h 
RPM: 832 
SPEED: 12 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 768 
SPEED: 12 km/h 
RPM: -8225 
SPEED: 1 km/h 
RPM: 835 
SPEED: 13 km/h 
RPM: 960 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: -14465 km/h 
RPM: 960 
SPEED: 13 km/h 
RPM: 832 
SPEED: 12 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 832 
SPEED: 13 km/h 
RPM: 768 
SPEED: 12 km/h 
RPM: -8225 
SPEED: 10 km/h 
RPM: 640 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 0 km/h 
RPM: 0 
SPEED: 218 km/h 
RPM: 13954

You are printing out the data from getResponse(), even if the response is not complete. That explains a lot of the garbage.

Similarly, you are using the data before you get a complete response, so that explains the rest of the garbage.

getResponse() should either block until a complete response arrives, or it should return a value indicating whether the response is complete, or not (that is, the \r has arrived).

OK, i've changed the code a bit, but it doesn't seem to make any difference, what am i missing?

//This is a character buffer that will store the data from the serial port
char rxData[20];
char rxIndex=0;

//Variables to hold the speed and RPM data.
int vehicleSpeed=0;
int vehicleRPM=0;

boolean gotResponse = false;

void setup(){

  Serial1.begin(9600);
  Serial.begin(9600);   //This is for the serial debug

  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(1500);
  //Reset the OBD-II-UART
  Serial1.print("ATWS\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(1000);
  
  // turn echo off
  Serial1.print("ATE0\r");
  
  // send 01 00 until we are connected
  do
  {
    Serial1.print("0100\r");
    delay(1000);
    getResponse();    //This is only called once since the echo has been turned off using ATE0\r
    Serial.print("0100 Response:  ");
    Serial.print(rxData[0]);Serial.print(rxData[1]);Serial.print(rxData[3]);Serial.print(rxData[4]);Serial.print(rxData[6]);Serial.print(rxData[7]); Serial.print(rxData[9]);Serial.print(rxData[10]);
    Serial.println("");
  }
  while(ELM_CHECK_RESPONSE("0100", rxData)!=0);
  
  gotResponse = false;
   
  Serial1.print("ATDPN\r");  //Shows the protocol
  getResponse();
  Serial.print("Protocol: ");Serial.print(rxData[0]);Serial.print(rxData[1]);Serial.println("");
  // str[0] should be 'A' for automatic
  // set header to talk directly to ECU#1
  if(rxData[1]=='1')  // PWM
    Serial1.print("ATSHE410F1\r");
  else if(rxData[1]=='2')  // VPW
    Serial1.print("ATSHA810F1\r");
  else if(rxData[1]=='3')  // ISO 9141
    Serial1.print("ATSH6810F1\r");
  else if(rxData[1]=='6')  // CAN 11 bits
    Serial1.print("ATSH7E0\r");
  else if(rxData[1]=='7')  // CAN 29 bits
    Serial1.print("ATSHDA10F1\r");
}

void printSpeed(){
  //Convert the string data to an integer
  vehicleSpeed = strtol(&rxData[6],0,16);
  
  Serial.print("SPEED: ");
  Serial.print(vehicleSpeed);
  Serial.print(" km/h");
  Serial.println(" ");

  gotResponse = false;
}

void printRpm(){
  //Convert the string data to an integer
  //NOTE: RPM data is two bytes long, and delivered in 1/4 RPM from the OBD-II-UART
  vehicleRPM = ((strtol(&rxData[6],0,16)*256)+strtol(&rxData[9],0,16))/4;

  //Print the rpm data to debug serial window
  Serial.print("RPM: ");
  Serial.print(vehicleRPM);
  Serial.println(" ");
  
  gotResponse = false;
}

void loop(){
  //Query the OBD-II-UART for the Vehicle Speed
  Serial1.print("010D\r");    
  delay(20);
  //Get the response from the OBD-II-UART board. We get two responses
  //because the OBD-II-UART echoes the command that is sent.
  //We want the data in the second response.
  getResponse();

  while(gotResponse == false){
    // Loop until getResponse() is done
  }
  printSpeed();

  //Print the speed data to debug serial window
  
  delay(20);
  
  //Query the OBD-II-UART for the Vehicle rpm
  Serial1.println("010C\r");
  delay(20);
  //Get the response from the OBD-II-UART board
  getResponse();
  
  while(gotResponse == false){
    // Loop until getResponse() is done
  }
  printRpm();
  
  //Give the OBD bus a rest
  delay(5000);
}

byte ELM_CHECK_RESPONSE(const char *cmd, char *str)
{
  // cmd is something like "010D"
  // str should be "41 0D blabla"
  if(cmd[0]+4 != rxData[0]
    || cmd[1]!= rxData[1]
    || cmd[2]!= rxData[3]
    || cmd[3]!= rxData[4])
    return 1;

  return 0;  // no error
}

void getResponse(void){
  char inChar=0;
  //Keep reading characters until we get a carriage return
  while(inChar != '\r'){
    //If a character comes in on the serial port, we need to act on it.
    if(Serial1.available() > 0){
      //Start by checking if we've received the end of message character ('\r').
      if(Serial1.peek() == '\r'){
        //Clear the Serial buffer
        inChar=Serial1.read();
        //Put the end of string character on our data string
        rxData[rxIndex]='\0';
        //Reset the buffer index so that the next character goes back at the beginning of the string.
        rxIndex=0;
        gotResponse = true;
      }
      //If we didn't get the end of message character, just add the new character to the string.
      else{
        //Get the new character from the Serial port.
        inChar = Serial1.read();
        //Add the new character to the string, and increment the index variable.
        rxData[rxIndex++]=inChar;
      }
    }
  }
}

what am i missing?

Any character that is not a '\r' should cause gotResponce to be set to false.

    getResponse();    //This is only called once since the echo has been turned off using ATE0\r
    Serial.print("0100 Response:  ");
    Serial.print(rxData[0]);Serial.print(rxData[1]);Serial.print(rxData[3]);Serial.print(rxData[4]);Serial.print(rxData[6]);Serial.print(rxData[7]); Serial.print(rxData[9]);Serial.print(rxData[10]);
    Serial.println("");

So, now getResponse() sets a flag. Let's just ignore it... Why are we bothering to set the flag?

  while(gotResponse == false){
    // Loop until getResponse() is done
  }

The comment is wrong. "Loop forever" would be correct.

void getResponse(void){
  char inChar=0;
  //Keep reading characters until we get a carriage return
  while(inChar != '\r'){
    //If a character comes in on the serial port, we need to act on it.
    if(Serial1.available() > 0){
      //Start by checking if we've received the end of message character ('\r').
      if(Serial1.peek() == '\r'){
        //Clear the Serial buffer
        inChar=Serial1.read();
        //Put the end of string character on our data string
        rxData[rxIndex]='\0';
        //Reset the buffer index so that the next character goes back at the beginning of the string.
        rxIndex=0;
      }
      //If we didn't get the end of message character, just add the new character to the string.
      else{
        //Get the new character from the Serial port.
        inChar = Serial1.read();
        //Add the new character to the string, and increment the index variable.
        rxData[rxIndex++]=inChar;
      }
    }
  }

Im kinda curious you say that its printing the response before it has been fully received.
who is that possible, as far as i can see it has a while loop that is checking to see if the \r is received
why is that not enough ? how is it escaping from that ? and why will it work to add a flag and another while loop that essentially does the same check?

Changed the init loop to this:

  do
  {
    Serial1.print("0100\r");
    delay(1000);
    getResponse();    //This is only called once since the echo has been turned off using ATE0\r
    while(gotResponse == false){}
    Serial.print("0100 Response:  ");
    Serial.print(rxData[0]);Serial.print(rxData[1]);Serial.print(rxData[3]);Serial.print(rxData[4]);Serial.print(rxData[6]);Serial.print(rxData[7]); Serial.print(rxData[9]);Serial.print(rxData[10]);
    Serial.println("");
  }
  while(ELM_CHECK_RESPONSE("0100", rxData)!=0);

And added gotResponse = false here:

else{
        //Get the new character from the Serial port.
        inChar = Serial1.read();
        //Add the new character to the string, and increment the index variable.
        rxData[rxIndex++]=inChar;
        gotResponse = false;
      }

Still no difference, there must be something else going on?

Maybe you can give this a try.

Try this library: https://github.com/VittorioEsposito/J1850-PWM-Encoding-Library

link above no longer works. seems to be here now.

http://www.arduinolibraries.info/libraries/j1850-pwm-encoding-library