SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By nosrednafs
#194412
Hi guys, Can someone check my code or maybe give me some insight on what I'm doing wrong.

I have a SparkFun OBD-II UART paired with a red board. I got the sample script to work with a I2C display and then moved to what I was looking to use this for, read engine coolant temp.

from here it is supposed to be 1 byte and the conversion is A - 40. Engine is about 150-160 F and I'm reading 13C and 55 F. The vehicle is a Chevy 2007 Trailblazer

Anyway, any pointers would be appreciated. I assume the problem is going to be in my string to long conversion.

Thanks!
Code: Select all
/*
  OBD-II-UART Quickstart Sketch
  Written by Ryan Owens for SparkFun Electronics 7/5/2011
  Updates for Arduino 1.0+ by Toni Klopfenstein

  Released under the 'beerware' license
  (Do what you want with the code, but if we ever meet then you buy me a beer)

  This sketch will grab RPM and Vehicle Speed data from a vehicle with an OBD port
  using the OBD-II-UART board from SparkFun electronics. The data will be displayed
  on a serial 16x2 LCD. See the tutorial at https://www.sparkfun.com/tutorials/294
  to learn how to hook up the hardware:

*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif



//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 enginecoolant = 0;
int enginecoolantF = 0;

void setup() {
  //Both the Serial LCD and the OBD-II-UART use 9600 bps.

  Serial.begin(9600);


  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(1500);
  //Reset the OBD-II-UART
  Serial.println("ATZ");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);

  //Delete any data that may be in the serial port before we begin.
  Serial.flush();

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)

  display.clearDisplay();

  // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.display();

}

void loop() {

  //Delete any data that may be left over in the serial port.
  Serial.flush();


  //Query the OBD-II-UART for the Vehicle rpm
  Serial.println("0105");
  //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
  enginecoolant = (strtol(&rxData[6],0,16))-40;
  enginecoolantF = (enginecoolant * 9/5) + 32;
  delay(100);
  display.clearDisplay();
  display.setCursor(0, 8);
  display.println(enginecoolant);
  display.setCursor(0, 20);
  display.println(enginecoolantF);
  display.display();

}

//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;
  //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 (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;
      }
    }
  }
}
By YellowGTM
#194705
I am receiving the same values. That makes me wonder what area of memory we are reading from.

One thing that sticks out is the getResponse() function. It's looking for a Carriage Return (\r).
At what point of the program is a carriage return entered, or needed? I believe the function should
be looking for a NULL terminator such as 0 (\0).

I'll be re-writing the code this weekend and will post any progress.
By YellowGTM
#194709
SOLUTION!

You need to change this:

//Query the OBD-II-UART for the Vehicle rpm
Serial.println("0105");

to this:

//Query the OBD-II-UART for the Vehicle rpm
Serial.print("0105\r");

in order for this to work:

//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 (Serial.available() > 0) {
//Start by checking if we've received the end of message character ('\r').
if (Serial.peek() == '\r') {
By nosrednafs
#194743
Well I was so excited I ran home and got everything. still not getting a good result. :( Mind posting your entire code or looking over mine? Am I not using the right variable to pass to the oled display or something? I'm getting erronious values that don't seem to change.


I'm a newbie when it comes to coding so I fully expect a error on my end.

Code: Select all
/*
  OBD-II-UART Quickstart Sketch
  Written by Ryan Owens for SparkFun Electronics 7/5/2011
  Updates for Arduino 1.0+ by Toni Klopfenstein

  Released under the 'beerware' license
  (Do what you want with the code, but if we ever meet then you buy me a beer)

  This sketch will grab RPM and Vehicle Speed data from a vehicle with an OBD port
  using the OBD-II-UART board from SparkFun electronics. The data will be displayed
  on a serial 16x2 LCD. See the tutorial at https://www.sparkfun.com/tutorials/294
  to learn how to hook up the hardware:

*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif



//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 enginecoolant = 0;
int enginecoolantF = 0;

void setup() {
  //Both the Serial LCD and the OBD-II-UART use 9600 bps.

  Serial.begin(9600);


  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(1500);
  //Reset the OBD-II-UART
  Serial.println("ATZ");
  //Wait for a bit before starting to send commands after the reset.
  delay(2000);

  //Delete any data that may be in the serial port before we begin.
  Serial.flush();

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)

  display.clearDisplay();

  // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.display();

}

void loop() {

  //Delete any data that may be left over in the serial port.
  Serial.flush();


  //Query the OBD-II-UART for the Vehicle rpm
Serial.print("0105\r");

  //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
  enginecoolant = (strtol(&rxData[6],0,16))-40;
  enginecoolantF = (enginecoolant * 9/5) + 32;
  delay(100);
  display.clearDisplay();
  display.setCursor(0, 8);
  display.println(enginecoolant);
  display.setCursor(0, 20);
  display.println(enginecoolantF);
  display.display();

}

//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;
//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 (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;
      }
    }
  }
}
By mdancer
#194748
Have you used Mode 1, PID 0 to check that your ECU supports PID 5?
Have you checked the first byte of the response to ensure it is requested mode + 0x40 (e.g. 0x41 for mode 1)?
Have you checked the rxIndex variable after calling getResponse() to see if rxData[6] is even valid before calling strtol()?
By nosrednafs
#194749
Hi Mdancer. I'm totally over my head with your questions. I would assume it supports engine coolant since it is a pretty standard pid that seems like a pretty basic necessity. If you give me instruction to check any of your other questions I'll be glad to try. sorry pretty new at all this and reading as much as I can .
By mdancer
#194753
Hi nosrednafs,

Ok, I'll explain and provide direction. Since I don't know how much experience you have with OBD-II, I'm going to provide a fair amount of detail, so if it sounds like I'm 'preaching to the choir', I appologize.

First, I agree that PID 05 is probably supported and it's probably safe to assume as much, but why assume when you can know for sure? Every mode uses PID 00 to provide information as to which of the next 32 PIDs are supported by the ECU (engine control unit). When you send the command "0105\r" over serial, you're sending out a PID request to all ECUs that may be listening. The first two characters are the hex value of the mode and the second two are the PID. Any ECU that recognizes your PID request, usually just one, will respond with a multi-byte message where the first byte is the requested mode + 0x40 and the requested PID as the second byte. The response data will be the remaining bytes of the message.

To use Mode 01, PID 00, send out the command "0100\r" over serial. The ECU should respond with 6 bytes of data - the first two as described above and the last four make up a sequence of 32 bits where each bit corresponds to the PIDs 01-20 (hex). For your case if the 5th bit is set, then the ECU supports PID 05. You could perform this check in the setup() function and provide an informational message to the user if PID 05 isn't supported.

Second, as I mentioned previously, the response from the ECU should have as the first two bytes the mode + 0x40 followed by a repeat of the requested PID. When you receive a message, you can check these two bytes and make sure they are as expected. If not, provide a message stating such. This will help debug future programs you write for OBD-II and make for a more robust program.

Finally, in the code you provided, your getResponse() function pulls data from serial until it reaches the '\r' character. As it does so it stores the data in rxData[] using rxIndex to keep track of where in the buffer it is. But this index also provides useful information to the caller of getResponse(). The caller could check that the message has enough data in it to constitute a valid message. For example if you requested Mode 01, PID 00, you know the ECU should send 6 bytes of data. In hex with spaces, this would be a minimum of 17 characters, so if rxIndex is less than 17, the message isn't valid and you can indicate as such. With Mode 01, PID 05, the response should be 3 bytes, so rxIndex should be 8.

If you implement these checks, your program can provide more information that might help figure out why you're not getting the results you expect. I hope this all has helped.

Mike