Go Down

Topic: How to speed up OBDII uart? (Read 3503 times) previous topic - next topic

scott_fx

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

UKHeliBob

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.
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

scott_fx


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.


scott_fx

#3
May 18, 2014, 10:20 pm Last Edit: May 19, 2014, 05:43 pm by scott_fx Reason: 1
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"
   
   }
       

PaulS

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.
The art of getting good answers lies in asking good questions.

scott_fx


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.


PaulS

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.
The art of getting good answers lies in asking good questions.

Dsmith658

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.

Rusty66

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

Go Up