SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By Mee_n_Mac
#144336
Here's a more thorough piece of diagnostic code. It should wait longer btw display changes and should show the number of characters received by the Arduino. I still want to see the OBD controller send back the correct responses to the ATZ and ATSP0 commands. It appears we're seeing them echoed which, if true, indicates good comm back and forth btw the OBDII and Arduino. That's to say the voltage levels are correct and the Tx/Rx lines aren't swapped. Then we have to prove the two are actually talking to each other and then prove the OBDII is talking to the car.

One step at a time.
Code: Select all
#include <LiquidCrystal.h>

#define LCD_RS 4
#define LCD_ENABLE 5

#define LCD_DATA1 7
#define LCD_DATA2 8
#define LCD_DATA3 12
#define LCD_DATA4 13

LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_DATA1, LCD_DATA2, LCD_DATA3, LCD_DATA4);
#define ContrastPin 6
#define BrightnessPin 9


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

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

void setup(){
  //Both the Serial LCD and the OBD-II-UART use 9600 bps.
  Serial.begin(9600);
  
  //Delete any data that may be in the serial port before we begin.
  delay (100);
  Serial.flush();

  //Init and clear the old data from the LCD.
  lcd.begin(16, 2);
  lcd.clear();
  // Put a Hello World message on the LCD.
  lcd.print("  Initializing  ");
  delay (1000);  
  lcd.setCursor(0, 1);
  lcd.print(" Reseting OBDII ");
  delay (2000);  // allow user time to look towards OBDII LEDs
  
  //Reset the OBD-II-UART
  Serial.println("ATZ");
  
  // Show both responses - if any
  getResponse();  // get first response
  
  // Show what came back.
  lcd.clear();
  // Now show what are we doing.
  lcd.print("Reset response");
  lcd.setCursor(0, 1);
  if(rxIndex)
  {
    lcd.print("rxIndex1 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    lcd.print(rxData);  // should be cmd echo ??
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #1");
  }
  delay (2000);  // wait just long enough for user to see 1st response
  
  // Now get 2nd real response ??
  getResponse();
  
  lcd.setCursor(0, 1);
  lcd.print("                ");  // erase prior response
  if(rxIndex)
  {
    lcd.print("rxIndex2 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    lcd.print(rxData);  // should be ELM ... 
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #2");
  }
  delay (5000);  // wait long enough for user to see responses
  
  //Now command the OBDII to auto-detect the proper protocol as done in example 1.
  Serial.println("ATSP0");
  // Again show both responses - if any
  getResponse();
  
  // Show what came back.
  lcd.clear();
  lcd.print("Auto protocol");
  lcd.setCursor(0, 1);
  if(rxIndex)
  {
    lcd.print("rxIndex3 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    lcd.print(rxData);  // should be cmd echo ?
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #3");
  }
  delay (2000);  // wait just long enough for user to see 1st response
  
  // Now get 2nd real response ??
  getResponse();
  lcd.setCursor(0, 1);
  lcd.print("                ");  // erase prior response
  if(rxIndex)
  {
    lcd.print("rxIndex4 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    lcd.print(rxData);  // should say OK
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #4");
  }
  delay (5000);  // wait long enough for user to see responses

  //Hoping the above worked, now put the speed header on the first row.
  lcd.clear();
  lcd.print("Speed: ");
  //Put the RPM header on the second row.
  lcd.setCursor(0, 1);
  lcd.print("RPM: ");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
}

void loop(){
  //Delete any data that may be in the serial port before we begin. 
  Serial.flush();
  //Set the cursor in the position where we want the speed data.
  lcd.setCursor(7, 0);
  //Write over the old speed data, and then reset the cursor position.
  lcd.print("        ");  // need 8 spaces for speed data per old code
  lcd.setCursor(7, 0);
  
  //Query the OBD-II-UART for the Vehicle Speed
  Serial.println("010D");
  
  //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();
  getResponse();
  //Convert the string data to an integer
  if(rxIndex) // test to see if anything was received from OBDII
  {
    vehicleSpeed = strtol(&rxData[6],0,16);
    //Print the speed data to the lcd
    lcd.print(vehicleSpeed);
  }
  else
  {
    lcd.print("No data");
  } 
  delay(100);
  
  //Delete any data that may be left over in the serial port.
  Serial.flush();
  //Move the serial cursor to the position where we want the RPM data.
  lcd.setCursor(5, 1);
  //Clear the old RPM data, and then move the cursor position back.
  lcd.print("          ");  // need 10 spaces for RPM data ?
  lcd.setCursor(5, 1);

  //Query the OBD-II-UART for the Vehicle rpm
  Serial.println("010C");
  //Get the response from the OBD-II-UART board
  getResponse();
  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
  if(rxIndex) // test to see if anything was received from OBDII
  {
    vehicleRPM = ((strtol(&rxData[6],0,16)*256)+strtol(&rxData[9],0,16))/4;
    //Print the rpm data to the lcd
    lcd.print(vehicleRPM);
  }
  else
  {
    lcd.print("No data");
  }
  
  //Give the OBD bus a rest
  delay(300);
}

//The getResponse function collects incoming data from the UART into the rxData buffer
// and only exits when a carriage return character is seen. Once the carriage return
// string is detected, the rxData buffer is null terminated (so we can treat it as a string)
// and the rxData index is reset to 0 so that the next string can be copied.
void getResponse(void){
  char inChar=0;
  rxIndex=0;
  unsigned long TimeIn = millis();
  //Keep reading characters until we get a carriage return or time out
  while(inChar != '\r'){
    //If a character comes in on the serial port, we need to act on it.
    if(Serial.available() > 0){
      //Start by checking if we've received the end of message character ('\r').
      if(Serial.peek() == '\r'){
        //Clear the Serial buffer
        inChar=Serial.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 = Serial.read();
        //Add the new character to the string, and increment the index variable.
        rxData[rxIndex++]=inChar;
      }
    }
    if (millis() - TimeIn > 1000) // wait 1 sec for some kind of response
    {
      break;
    } 
  }
}
By Mee_n_Mac
#144337
dkulinski wrote:I quickly skimmed over the previous posts, but was it discovered if the ODB UART is sending out TTL level serial or RS-232 level serial? If it is the latter you will of course need something like the MAX232 to convert the voltages.

EDIT: Nevermind, found the tutorial showing a direct connection to the Arduino.

Dan
From the schematic it appears the OBD controller is a 3.3V part. I was concerned it might not be outputting enough voltage to have a logic 1 sensed by the Arduino. But a closer look shows it's Tx out line is pulled up to 5V via a 4.7K resistor. So my belief is that it's an open collector (or drain) output and the pull-up should provide enough volts to have the levels seen by the Arduino. I'm not sure what's going on w/the ground situation. The above code may help show whether the Arduino is seeing garbage from the OBDII or real data. When I see the responses shown in the tutorial then we'll know we've got solid comm btw the OBD and Arduino.
By Mee_n_Mac
#144339
dkulinski wrote:Just an FYI, you do need to change it into the character equivalent of the numeral. The easy way is to add each numeral to 0. Mee_n_Mac has some of my code that I used to do that in another project which I don't currently have access to. I am sure he can supply it if it already isn't in the code he handed you.

Dan
Hey ! Shouldn't you be busy getting that Laser Tag system working !! :mrgreen:

From the tutorial I think all the data coming from the OBD is ASCII characters. The tutorial code does an ASCII to long conversion, then glues the bytes back together (if needed) and then applies the proper scaling and offsets to the now-numeric variables. lcd.print supposedly inherits serial.print's ability to print most any type variable w/o formatting info. (though I don't see floats explicitly listed for lcd.print like I do for serial.print !). Supposedly the tutorial code worked :pray: and the versions above (which were cloned from that tutorial code) haven't changed that part of it.

In any case I want to see the (character) responses to the reset and protocol commands. When that's working then it's time to see if the OBD is talking to the car properly. This code is just to prove the system all works before going on to the mucho more complicated OBDuino code. At that point I think the OP will be on his own but at least he'll know he has a working HW base and it'll be a matter of getting the SW "tuned".
By dkulinski
#144342
Actually this Sunday I have the Sparkfun Mini-bot class. As for lasertag, yes, I should be working on it, but this weekend is packed.

It is good to hear that it comes in as ascii makes it easy to sling around. And yes the lcd.print can convert between types. I generally write my own conversion if there is an expected range of values. Of course this project doesn't seem very time sensitive so ease of use may be more important here.

It does seem that it is actually getting data and talking to the OBD port (or trying to). My question is, does the device automatically detect the protocol? You may want to try to set it to a specific protocol and then retest.

Dan
By Mee_n_Mac
#144343
OK, I was looking at your vid and the tutorial again and I think I see something. First I have to believe that the terminal program in the tutorial (example #1) was NOT setup to locally echo what was typed and so everything you see in the term windows was coming from the OBD. That explains why I don't see the command and it's echo in those windows. So given that, what I do see are 2 additional blank lines following the ATZ command but no such thing following the ATSP0 command. So I've mod'ed the code yet another time to allow for additional readings to see if we can see the ELM ... response we're supposed to. In your vid I think we're seeing the ATZ echo and then a blank line (the timing is proper for this) and then the code doesn't read the remaining blank line and the ELM ... I don't know why we don't see the "OK" response to the ATSP0 command, perhaps there was not enough of a wait to see that. So w/o the ground, try the following and let's prove the Arduino and OBD are talking. Then we can figure out why there's no good data from the car.
Code: Select all
#include <LiquidCrystal.h>

#define LCD_RS 4
#define LCD_ENABLE 5

#define LCD_DATA1 7
#define LCD_DATA2 8
#define LCD_DATA3 12
#define LCD_DATA4 13

LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_DATA1, LCD_DATA2, LCD_DATA3, LCD_DATA4);
#define ContrastPin 6
#define BrightnessPin 9


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

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

void setup(){
  //Both the Serial LCD and the OBD-II-UART use 9600 bps.
  Serial.begin(9600);
  
  //Delete any data that may be in the serial port before we begin.
  delay (100);
  Serial.flush();

  //Init and clear the old data from the LCD.
  lcd.begin(16, 2);
  lcd.clear();
  // Put a Hello World message on the LCD.
  lcd.print("  Initializing  ");
  delay (1000);  
  lcd.setCursor(0, 1);
  lcd.print(" Reseting OBDII ");
  delay (2000);  // allow user time to look towards OBDII LEDs
  
  //Reset the OBD-II-UART
  Serial.println("ATZ");
  
  // Show both responses - if any
  getResponse();  // get first response
  
  // Show what came back.
  lcd.clear();
  // Now show what are we doing.
  lcd.print("Reset response");
  lcd.setCursor(0, 1);
  if(rxIndex)
  {
    lcd.print("rxIndex1 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    delay(100);         // allow LCD time to refresh 
    lcd.print(rxData);  // should be cmd echo ??
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #1");
  }
  delay (2000);  // wait just long enough for user to see 1st response
  
  // Now get 2nd response, a blank line ??
  getResponse();
  lcd.setCursor(0, 1);
  lcd.print("                ");  // erase prior response
  delay(100);         // allow LCD time to refresh
  if(rxIndex)
  {
    lcd.print("rxIndex2 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    delay(100);         // allow LCD time to refresh
    lcd.print(rxData);  // should be ??? 
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #2");
  }
  
  // Now get 3rd response, a blank line ??
  getResponse();
  lcd.setCursor(0, 1);
  lcd.print("                ");  // erase prior response
  delay(100);         // allow LCD time to refresh
  if(rxIndex)
  {
    lcd.print("rxIndex3 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    delay(100);         // allow LCD time to refresh
    lcd.print(rxData);  // should be ???
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #3");
  }
  
  // Now get 4th response, ELM ... ??
  getResponse();
  lcd.setCursor(0, 1);
  lcd.print("                ");  // erase prior response
  delay(100);         // allow LCD time to refresh
  if(rxIndex)
  {
    lcd.print("rxIndex4 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    delay(100);         // allow LCD time to refresh
    lcd.print(rxData);  // should be ELM ... 
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #4");
  }
  delay (5000);  // wait long enough for user to see responses
  
  //Delete any data that may be in the serial port before we do next step
  Serial.flush();
  
  //Now command the OBDII to auto-detect the proper protocol as done in example 1.
  Serial.println("ATSP0");
  // Again show both responses - if any
  getResponse();
  
  // Show what came back.
  lcd.clear();
  lcd.print("Auto protocol");
  lcd.setCursor(0, 1);
  if(rxIndex)
  {
    lcd.print("rxIndex5 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    delay(100);         // allow LCD time to refresh
    lcd.print(rxData);  // should be cmd echo ?
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #5");
  }
  delay (2000);  // wait just long enough for user to see 1st response
  
  // Now get 2nd real response ??
  getResponse();
  lcd.setCursor(0, 1);
  lcd.print("                ");  // erase prior response
  delay(100);         // allow LCD time to refresh
  if(rxIndex)
  {
    lcd.print("rxIndex6 = ");
    lcd.print(rxIndex);
    delay (2000);
    lcd.setCursor(0, 1);
    lcd.print("                ");  // erase prior response
    delay(100);         // allow LCD time to refresh
    lcd.print(rxData);  // should say OK
    // lcd.print(" Got a message");
  }
  else
  {
    lcd.print("no response #6");
  }
  delay (5000);  // wait long enough for user to see responses

  //Hoping the above worked, now put the speed header on the first row.
  lcd.clear();
  lcd.print("Speed: ");
  //Put the RPM header on the second row.
  lcd.setCursor(0, 1);
  lcd.print("RPM: ");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);
}

void loop(){
  //Delete any data that may be in the serial port before we begin. 
  Serial.flush();
  //Set the cursor in the position where we want the speed data.
  lcd.setCursor(7, 0);
  //Write over the old speed data, and then reset the cursor position.
  lcd.print("        ");  // need 8 spaces for speed data per old code
  lcd.setCursor(7, 0);
  
  //Query the OBD-II-UART for the Vehicle Speed
  Serial.println("010D");
  
  //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();
  getResponse();
  //Convert the string data to an integer
  if(rxIndex) // test to see if anything was received from OBDII
  {
    vehicleSpeed = strtol(&rxData[6],0,16);
    //Print the speed data to the lcd
    lcd.print(vehicleSpeed);
  }
  else
  {
    lcd.print("No data");
  } 
  delay(100);
  
  //Delete any data that may be left over in the serial port.
  Serial.flush();
  //Move the serial cursor to the position where we want the RPM data.
  lcd.setCursor(5, 1);
  //Clear the old RPM data, and then move the cursor position back.
  lcd.print("          ");  // need 10 spaces for RPM data ?
  lcd.setCursor(5, 1);

  //Query the OBD-II-UART for the Vehicle rpm
  Serial.println("010C");
  //Get the response from the OBD-II-UART board
  getResponse();
  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
  if(rxIndex) // test to see if anything was received from OBDII
  {
    vehicleRPM = ((strtol(&rxData[6],0,16)*256)+strtol(&rxData[9],0,16))/4;
    //Print the rpm data to the lcd
    lcd.print(vehicleRPM);
  }
  else
  {
    lcd.print("No data");
  }
  
  //Give the OBD bus a rest
  delay(300);
}

//The getResponse function collects incoming data from the UART into the rxData buffer
// and only exits when a carriage return character is seen. Once the carriage return
// string is detected, the rxData buffer is null terminated (so we can treat it as a string)
// and the rxData index is reset to 0 so that the next string can be copied.
void getResponse(void){
  char inChar=0;
  rxIndex=0;
  unsigned long TimeIn = millis();
  //Keep reading characters until we get a carriage return or time out
  while(inChar != '\r'){
    //If a character comes in on the serial port, we need to act on it.
    if(Serial.available() > 0){
      //Start by checking if we've received the end of message character ('\r').
      if(Serial.peek() == '\r'){
        //Clear the Serial buffer
        inChar=Serial.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 = Serial.read();
        //Add the new character to the string, and increment the index variable.
        rxData[rxIndex++]=inChar;
      }
    }
    if (millis() - TimeIn > 1000) // wait 1 sec for some kind of response
    {
      break;
    } 
  }
}
BTW I notice in the vid that the echoes of the commands are NOT in caps, they are all lower case. Could it be that the OBD is case sensitive ? That the code should send atz and not ATZ, atsp0 and not ATSP0 ? If the above doesn't get you the proper "ELM ..." and "OK" responses, alter the code and give the non-caps a try !
By Mee_n_Mac
#144344
dkulinski wrote:It does seem that it is actually getting data and talking to the OBD port (or trying to). My question is, does the device automatically detect the protocol? You may want to try to set it to a specific protocol and then retest.
Dan
It supposedly can auto-detect the car's protocol ... if you issue the correct command. Right now I have a sneaking suspicion that we've yet to do that. I think we've seen the echo back of the command. I've yet to see the proper acknowledgement of the command (see the post above). If the OP tries the above code and still doesn't see it/them, then I really wonder if caps are the problem (see above post).

Wouldn't that be a ... :naughty:
By Mee_n_Mac
#144348
djarmyssg wrote:Im headed out to try the new code, and then the caps lock and see what we get...
I'll look at the new vid. For this part, you don't even need to be connected to the car. Just the Arduino, PC, LCD and OBDII are good enough. Once we see the proper acknowledgements ("ELM ..." and "OK") then it'll be necessary to connect to the car. Right now I think the Tx/Rx lines (w/o the ground) are good and the Arduino and OBD can talk ... if the Arduino sends the right commands. I guess we'll see shortly.
By djarmyssg
#144349
just ran the newest code... same as before.. no change.. lowercased the commands... still no change... My serial LCD backpack just came in.. so imma try the ORIGINAL code.. and see if it's a LCD issue!
By Mee_n_Mac
#144350
There was some really odd stuff going on in that last video. The code sent rxIndex to the LCD and it's "print" was some odd thing. But rxIndex is just a number btw 0 and 255, I don't see how the LCD could display what it did. And then I didn't see the ATZ echo any more. The display blanked completely and it shouldn't have and was there something else in there ? Did the power perhaps brown out ? Or a wire come loose ? What was that chiming sound ?

The code was not that different from the version that showed the ATZ and ATSP0 echoes. WTF ??
By Mee_n_Mac
#144351
djarmyssg wrote:just ran the newest code... same as before.. no change.. lowercased the commands... still no change... My serial LCD backpack just came in.. so imma try the ORIGINAL code.. and see if it's a LCD issue!
If you have a DVM, I'd check very very carefully all the voltages and grounds and make sure what you think you have is what you really have. I can't tell from the protoboard what's connected to what there. Seems there's a ground and a 3.3v (?) wire running to it. Are you running the LCD off 3.3V ? Or is that the 5V from the Arduino ... hard to tell from the vids.

Was the chiming sound a reset on the LCD, perhaps due to too much current being drawn from the Arduino ? Where are all those other wires on the LCD going? I thought I'd see just 8; power, ground, 4 data, strobe and enable. Looks like there's 12 wires.
By Mee_n_Mac
#144352
I might also recommend you go back to the software you used this AM to make today's 1'st video, the one where you saw the ATZ and ATSP0 echoes. See if you can repeat that. Something has gone south and I don't see that the recent code can account for that. (minus some stupid typo on my part)

BTW : did you disconnect the OBD from the Arduino when uploading a new sketch ? I wonder if the OBD can get screwed up like the LCD backpacks can when sharing the Tx/Rx lines on pins D0/1 ?
By djarmyssg
#144353
Well Checked all the power nad grounds... All is well there.. I cleaned up the board a lil so It makes it easier to understand. but yes It is a 3.3 LCD. As for OBD being disconnected, I have to everytime I upload otherwise I get an error. But im trying to figure out what the "RXindex1 = lll" could be??? I get the exact same thing even when I dont have power to the OBD board... as long as the TX/RX lines are attached, thats what comes up!!
By Mee_n_Mac
#144354
I have a new TOTM. What happened in the last vid was you got a lot of noise and the rxData array got over flowed (there's nothing in the tutorial code, and hence mine, to prevent that). Also rxIndex got overflowed. Now I'd have thought that would still result in a number btw 0 and 255 but perhaps the Arduino software tries to be too helpful and expands the range to be a too large a number to display in the LCD. (<-this part is goofy) Then when it is displayed we get the ||| oddity. Also then when the rxData is displayed it's 9999999 characters wrap all over the prior characters displayed on the LCD and overwrite the "Reset response" on the 1'st line. (<-this part is sound)

I note that this AM you started with the ground applied and then took it off. Perhaps this sets a virtual ground level for the LCD and Arduino and OBD but w/o that connection for too long ... that virtual level gets lost.

I dunno ... I'm scratching my head here !

We can redo the getResponse() function to limit the incoming characters to the 19 the array will hold and jump to an error/exit condition (with some message) if more come in before a CR.

TOTM = theory of the minute
Last edited by Mee_n_Mac on Fri May 11, 2012 7:03 pm, edited 1 time in total.
By Mee_n_Mac
#144355
djarmyssg wrote:Well Checked all the power nad grounds... All is well there.. I cleaned up the board a lil so It makes it easier to understand. but yes It is a 3.3 LCD.
Good and bad. Is there a single place where the Arduino ground and LCD ground and OBD ground are all tied together ? And nothing else is tied to that place ?

Errr, I'm a little worried about the Arduino sending 0-5 V signals to the 3.3V LCD.