Arduino Forum

Using Arduino => Programming Questions => Topic started by: scott_fx on May 14, 2014, 07:00 am

Title: How to speed up OBDII uart?
Post by: scott_fx on May 14, 2014, 07:00 am
Hey guys,

I have the sparkfun obdii uart board (https://www.sparkfun.com/products/9555) that i'm using to get data for a Digital instrument cluster that I'm designing in processing. It's working just fine, but it's extremely slow. Could you guys take a look at my code and tell me if it's a problem with my code or if the sparkfun board is just really that slow?

Code: [Select]
//Set up ring buffer
char rxData[20];
char rxIndex = 0;

void setup()
{
  Serial.begin(9600); // prints to serial monitor
  Serial1.begin(9600); //Hardware serial connection to the obdii uart
  OBD_init(); //initiates obd link
}

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

  int mph = (int)((getSPEED() * 10000L + 5) / 16090); //convert kmh to mph
  Serial.print (mph);
  Serial.print (",");
  Serial.print (getRPM());
  Serial.print (",");
  Serial.print (getWATERTEMP());
  Serial.print (",");
  Serial.print (getOILTEMP());
  Serial.print (",");
  Serial.print (getFUEL());
  Serial.print (",");
  Serial.println (getVOLT());
  // delay(10);//wait .5 seconds and grab another reading
}

void OBD_init(void)
{
  //Wait for a little while before sending the reset command to the OBD-II-UART
  delay(2000);
  //Reset the OBD-II-UART
  Serial1.print("ATZ\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(2);
  OBD_read();
  Serial1.print("ATE0\r");
  OBD_read();
  Serial1.flush();
}

int getRPM(void)
{
  //Query the OBD-II-UART for the Vehicle rpm
  Serial1.flush();
  Serial1.print("010C\r");
  OBD_read();

  return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
}

int getSPEED(void)
{
  //Query the OBD-II-UART for the vehicle speed
  Serial1.flush();
  Serial1.print("010D\r");
  OBD_read();

  return strtol(&rxData[6], 0, 16);
}

int getOILTEMP(void)
{
  //Query the OBD-II-UART for the vehicle speed
  Serial1.flush();
  Serial1.print("015C\r");
  OBD_read();

  return strtol(&rxData[6], 0, 16);
}

int getFUEL(void)
{
  //Query the OBD-II-UART for the vehicle speed
  Serial1.flush();
  Serial1.print("012F\r");
  OBD_read();

  return strtol(&rxData[6], 0, 16);
}

int getVOLT(void)
{
  //Query the OBD-II-UART for the vehicle speed
  Serial1.flush();
  Serial1.print("0142\r");
  OBD_read();

  return strtol(&rxData[6], 0, 16);
}

int getWATERTEMP(void)
{
  //Query the OBD-II-UART for the Engine Coolant Temp
  Serial1.flush();
  Serial1.print("0105\r");
  OBD_read();

  return strtol(&rxData[6], 0, 16) - 40;
}


void OBD_read(void)
{
  char c;
  do {
    if (Serial1.available() > 0)
    {
      c = Serial1.read();
      if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
      {
        rxData[rxIndex++] = c; //Add whatever we receive to the buffer
      }
    }
  } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
  rxData[rxIndex++] = '\0';//Converts the array into a string
  rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
}


any help would be greatly appreciated

thanks
Title: Re: How to speed up OBDII uart?
Post by: UKHeliBob on May 14, 2014, 08:18 am
Code: [Select]
  Serial1.begin(9600); //Hardware serial connection to the obdii uartCould this be set to a higher baud rate ?

Get rid of all the Serial.flush() commands.
Title: Re: How to speed up OBDII uart?
Post by: scott_fx on May 14, 2014, 05:38 pm

Code: [Select]
  Serial1.begin(9600); //Hardware serial connection to the obdii uartCould this be set to a higher baud rate ?

according to the product page:  https://www.sparkfun.com/products/9555
UART interface (baud rates from 38 bps to 10 Mbps)
I will try the other baud rates to see if i get any better results


Get rid of all the Serial.flush() commands.


I was trying a a bunch of thing to try to optimize and commenting those out was one of them.  for some reason i remember not getting any value in my serial monitor after i did that though.  I will have to test that again and give you a better explanation of what happened.

Title: Re: How to speed up OBDII uart?
Post by: scott_fx on May 18, 2014, 10:20 pm
Thanks again for your help so far.    I finally got some free time to work on the project again (pregnant wife and getting ready for our firstborn is taking up my project time)  

anyways  here are the results from my tests this morning.  I guess i was mistaken about the Serial.flush   I commented out all the Serial.flush lines.  no real speed up in response but it didn't break the script so they are gone now.

I tried a bunch of different baud rates on Serial1 (kept Serial0 at 9600) and didn't get a response from the obd-ii uart board in my serial monitor.  I tried 115200, 38400, 41600, 10400, 500000, and 250000.  no luck.

next i tried to play with the timeouts and it helped TREMENDOUSLY with speed but the data i retrieve isn't consistent.  I will read back the correct rpm for a few cycles then it'll jump up to 21071 or down to ~53 or 0.  The only way i could solve this is to add a delay:

         delay(75);
but it slows it down (still faster then w/o the timeouts)
Here is a [demo](http://youtu.be/GrkIU5mSe-4) of the processing app in action if you want to see how well it's working.  Optimally I'd like to get those times down even more but I think it's satisfactory for now (unless you have any other suggestions)

issue no 2:  When I add the second value to poll (speed),  it goes a bit out of whack with random numbers.   If i don't do the internal timeout method it works fine, just slow.  If i do the internal timeout method but alternate the readings (cycle 1: read rpm, cycle 2: read speed, cycle 3: read rpm... and so on) it works fine.  again just slower



here is the initial sketch that's working with just one reading:

 
Code: [Select]
 //Set up ring buffer
   char rxData[20];
   char rxIndex = 0;
   int rpmstored = 0;
   void setup()
   {
     Serial.begin(9600); // prints to serial monitor
     Serial1.begin(9600); //Hardware serial connection to the obdii uart
     OBD_init(); //initiates obd link
   }
   
   void loop()
   
     rpmstored = getRPM();
   
     //int mph = (int)((getSPEED() * 10000L + 5) / 16090); //convert to mph
     //  Serial.print (mph);
     Serial.print (0);
     Serial.print (",");
     Serial.print (rpmstored);
     Serial.print (",");
     //  Serial.print (getWATERTEMP());
     Serial.print (0);
     Serial.print (",");
     //  Serial.print (getOILTEMP());
     Serial.print (0);
     Serial.print (",");
     //  Serial.print (getFUEL());
     // Serial.print (",");
     Serial.println (0);
     //  Serial.println (getVOLT());
     delay(75);
   
   
   
   
   }
   
   void OBD_init(void)
   {
     //Wait for a little while before sending the reset command to the OBD-II-UART
     delay(2000);
     //Reset the OBD-II-UART
     Serial1.print("ATZ\r");
     //Wait for a bit before starting to send commands after the reset.
     delay(2);
     OBD_read();
     Serial1.print("ATE0\r");
     OBD_read();
     //Serial1.flush();
   }
   
   int getRPM(void)
   {
     //Query the OBD-II-UART for the Vehicle rpm
     Serial1.print("010C1\r");
     OBD_read();
     return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
   
   }
   
   int getSPEED(void)
   {
     //Query the OBD-II-UART for the vehicle speed
     Serial1.print("010D1\r");
     OBD_read();
   
     return strtol(&rxData[6], 0, 16);
   }
   
   
   void OBD_read(void)
   {
     char c;
     do {
       if (Serial1.available() > 0)
       {
         c = Serial1.read();
         if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
         {
           rxData[rxIndex++] = c; //Add whatever we receive to the buffer
         }
       }
     } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
     rxData[rxIndex++] = '\0';//Converts the array into a string
     rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
   
   }
       
Title: Re: How to speed up OBDII uart?
Post by: PaulS on May 19, 2014, 02:07 pm
Code: [Select]
void OBD_read(void)
    {
      char c;
      do {
        if (Serial1.available() > 0)
        {
          c = Serial1.read();
          if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
          {
            rxData[rxIndex++] = c; //Add whatever we receive to the buffer
          }
        }
      } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
      rxData[rxIndex++] = '\0';//Converts the array into a string
      rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
   
    }

Sure would be a good idea to make sure that there is room in the array (rxData) BEFORE you write to the next position in the array (which might not actually be part of the array).

Code: [Select]
      Serial1.print("010C1\r");
      OBD_read();
      return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;

The OBD thing is not expecting synchronous communication. It is asynchronous. You send it a command. Sometime later, it provides you an answer.

You are blocking waiting for a response. That is NOT the way to increase performance.
Title: Re: How to speed up OBDII uart?
Post by: scott_fx on May 19, 2014, 06:01 pm

Code: [Select]
void OBD_read(void)
    {
      char c;
      do {
        if (Serial1.available() > 0)
        {
          c = Serial1.read();
          if ((c != '>') && (c != '\r') && (c != '\n')) //Keep these out of our buffer
          {
            rxData[rxIndex++] = c; //Add whatever we receive to the buffer
          }
        }
      } while (c != '>'); //The ELM327 ends its response with this char so when we get it we exit out.
      rxData[rxIndex++] = '\0';//Converts the array into a string
      rxIndex = 0; //Set this to 0 so next time we call the read we get a "clean buffer"
   
    }

Sure would be a good idea to make sure that there is room in the array (rxData) BEFORE you write to the next position in the array (which might not actually be part of the array).

Code: [Select]
      Serial1.print("010C1\r");
      OBD_read();
      return ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;

The OBD thing is not expecting synchronous communication. It is asynchronous. You send it a command. Sometime later, it provides you an answer.

You are blocking waiting for a response. That is NOT the way to increase performance.


First,  Thank you for taking the time to help out a novice like myself, it's very appreciated. 
Being a novice and having this be my first foray into the world of serial communication I'm a bit confused.  Your first point about making sure there was room in the rxData array.  could i solve that by clearing out the array at the beginning of the loop with rxData = 0;? 

on the second part.  If i understand this correctly I need to figure out a way to recieve the data without a 'while' loop?


thank you and please excuse my ignorance, just trying to grasp this.

Title: Re: How to speed up OBDII uart?
Post by: PaulS on May 19, 2014, 06:39 pm
Quote
Your first point about making sure there was room in the rxData array.  could i solve that by clearing out the array at the beginning of the loop with rxData = 0;? 

No. You need an if statement - if there is room in the array, add the next character. If not, don't.

Quote
If i understand this correctly I need to figure out a way to recieve the data without a 'while' loop?

Reading all the data that is available, in a while loop, is OK. Its the blocking until the last character arrives that is not.

You need to read all the data that is available, on each pass through loop, taking action only when the end-of-packet marker arrives. That action involves, after using the data, resetting rxIndex to 0 and setting rxData[0] to '\0'.

Think about it like this. Suppose you order something from my store, and I tell you that I will ship stuff each day, ending the shipment with a red box. You wouldn't run out and stand next to the mail box until the red box arrives, would you? No, what you'd do is check your mail each day, and collect any packages that arrive. When the red package arrives, you would now that you no longer need to run out and check every day. Until you order something else, that is.
Title: Re: How to speed up OBDII uart?
Post by: Dsmith658 on Jul 29, 2017, 11:04 pm
I know I'm really late to the party but since I just ran into this problem and solved it for my application I figured I'd add my 2 cents.

I disabled the adaptive timing "AT AT0" then reduced the time out to 1 "AT ST 01"

After this I was able to poll a set of 6 1 byte return PIDs in less than 40ms.  I didn't see any errors during the time I was polling this afternoon.

I was using protocol 6, and I am sure it won't be possible to reduce the time out so drastically on all applications and protocols.
Title: Re: How to speed up OBDII uart?
Post by: Rusty66 on Sep 01, 2018, 09:37 am
Another way to speed things up is mentioned on page 32 of the ELM manual.

With each request you can add a single byte stating the number of respones you know will come. Then the ELM will not wait any longer after receiving that numer of responses.

For the RPM the request would then be "01 0C 1\r"

Rob