6

I am using this device for my Arduino that connects to the OBD-II port of my car to get various information such as speed, rpm, etc. I am using this for a digital Nixie tube speedometer using the smart Nixie tube from a Kickstarter project.

I pass the information to the Nixie tubes through serial and get information from the OBD-II through serial as well. I am getting speed updates about every 20 seconds and that is incredibly slow when the device can supposedly update up to 100 times per second.

I originally was using an Arduino Uno and using software serial for the tubes, but I figured the slowdown was due to interupts interfering with the hardware serial, so I switched to the Arduino Mega 2560 to use a second hardware serial pin to hopefully fix the problem, yet the same issue is occurring.

Here is my code... There must be something I'm doing wrong. Serial2 is for the Nixie tubes.

#import <Arduino.h>

#include <OBD.h>

int ones = 0;
int tens = 0;

int kphValue = 0;
int mphValue = 0;

int rpmValue = 0;

String onesString;
String tensString;
String payloadString;
String rpmString;
COBD obd;

void setup(){
    Serial2.begin(9600);
    obd.begin();

    //Initiate OBD-II connection until success
    while (!obd.init()){
        Serial2.print("$0,Y,Y,255,255,000,000$0,Y,Y,255,255,000,000!");
        delay(1000);
        Serial2.print("$0,Y,Y,000,000,000,000$0,Y,Y,000,000,000,000!");
        delay(1000);
    }
}

void loop(){
    onesString = String(ones);
    tensString = String(tens);
    payloadString;
    rpmString;

    if(obd.read(PID_RPM, rpmValue)){
        if (rpmValue < 3000){
            rpmString =",N,N,255,000,255,000";
        }
        else if (rpmValue >= 5000){
            rpmString = ",N,N,255,255,000,000";
        }
        else if (rpmValue >= 3000){
            rpmString = ",N,N,255,255,030,000";
        }
    }

    if (obd.read(PID_SPEED, kphValue)){
        kphToMPH();
        payloadString = "$" + onesString + rpmString + "$" + tensString + "rpmString" + "!";
        Serial2.print(payloadString);
    }
}

void splitDigits(int input_number)
{
    ones = (input_number%10);
    tens = ((input_number/10)%10);
}

void kphToMPH()
{
    mphValue = kphValue * 0.621371;
    splitDigits(mphValue);
}

EDIT: I am using the following library: https://github.com/stanleyhuangyc/ArduinoOBD

11
  • 1
    The call to Serial2.available() looks suspicious, what happens if you remove it? That calls checks for incoming data, not that it's OK to send data.
    – PeterJ
    Commented Apr 23, 2014 at 4:37
  • I will edit that out. I added that to see if it would help because without it I'm still getting slow responses. Commented Apr 23, 2014 at 4:38
  • Seem to be missing the source for obd.read() there.
    – John U
    Commented Apr 23, 2014 at 8:14
  • 1
    Can you add some sort of indication, like blink an LED, to show what's delaying? Is it that the code waits between reads, that the reads happen really slowly (low baud rate? faulty comms routine? long negotiation period?) or that the car waits a long time to respond? etc. etc.
    – John U
    Commented Apr 23, 2014 at 8:41
  • The read is a relatively high baud rate. Commented Apr 23, 2014 at 12:19

3 Answers 3

6

I think the problem is not so much in the update speed, but in the fact that you loose accuracy in your calculation. The following lines are the culprit:

int kphValue = 0;  // [-32768:32767]
int mphValue = 0;
mphValue = kphValue * 0.621371;

Make sure the calculation will be entirely in integers to prevent calculation errors:

uint16_t kphValue = 0;  // [0:65535]
uint16_t mphValue = 0;
mphValue = ( kphValue * 621 ) / 1000;

This limits kphValue to just over 100, if you want to go higher than that there are two option:

  • multiply by 62 and divide by 100;
  • use longer integers int32_t [-2147483648:2147483647], which comes with enough room for multiplying by 621371 and dividing by 1000000, but will perform slower and with a larger memory footprint. Do you really need that accuracy?
6
  • Yes and what about the computation delay added by the usage of floating point numbers with a MCU that has no floating point support.
    – Blup1980
    Commented Apr 23, 2014 at 6:55
  • Not sure if the first part is correct, while I normally wouldn't write it that way I'd expect kphValue would be converted to a double for the calculation phase. Just gave it a quick try on a PC C++ compiler and result was 621 for an input of 1000.
    – PeterJ
    Commented Apr 23, 2014 at 7:12
  • @Blup1980 - It will be slower but even a very slow micro should be able to do those calculations in a jiffy. If you were doing the conversion 1000 times per second then you might notice a delay.
    – John U
    Commented Apr 23, 2014 at 8:16
  • @Blup1980 where do you see floating point numbers in my answer?
    – jippie
    Commented Apr 23, 2014 at 17:57
  • 1
    @jippie You said that your implementation using integers is better because of the better accuracy. But I wanted to add that it's also better in term of speed because there is no floating point. But it would even be better if you multiply by 636 and divide by 1024 where the divide can be computed as logical shifts instead of a multi cycle divide operation.
    – Blup1980
    Commented Apr 23, 2014 at 19:25
3

try this:

#import <Arduino.h>
#include <OBD.h>

int kphValue = 0;
int rpmValue = 0;

COBD obd;

//array of character arrays, see http://arduino.cc/en/Reference/string
const char* rpmStrings[] = {    ",N,N,255,000,255,000", 
                                ",N,N,255,255,030,000", 
                                ",N,N,255,255,000,000" }; 

void setup(){
    Serial2.begin(9600);
    obd.begin();

    //Initiate OBD-II connection until success
    while ( !obd.init() ) {
        Serial2.print("$0,Y,Y,255,255,000,000$0,Y,Y,255,255,000,000!");
        delay(1000);
        Serial2.print("$0,Y,Y,000,000,000,000$0,Y,Y,000,000,000,000!");
        delay(1000);
    }
}

void loop() {
    if ( obd.read(PID_RPM, rpmValue) && obd.read(PID_SPEED, kphValue) )
    {
        int rpmStringIndex = determineRpmIndex(rpmValue);
        int mphValue = kphToMPH(kphValue);

        //payloadString = "$" + onesString + rpmString + "$" + tensString + "rpmString" + "!";
        //using Arduino String objects is generally not a good idea due to memory constraints
        //it's probably ok, but I'd rather use character arrays and/or print a bit at a time rather than trying to assemble a giant string 
        Serial2.print("$");
        Serial2.print(mphValue % 10); //ones
        Serial2.print(rpmStrings[rpmStringIndex]);
        Serial2.print("$");
        Serial2.print(mphValue / 10); //tens - integer division, don't need modulo

        //do you really want to append the string "rpmString!" or did you want the contents of the variabe rmpString+"!" ?
        //I'm pretty sure you meant the latter
        Serial2.print(rpmStrings[rpmStringIndex]);
        Serial2.print("!");
    }
    else 
    {
        //obd read error
        //add debugging output here to see if obd failed to respond correctly/on time
    }
}

int kphToMph( int kphValue ) {
    //return a value rather than updating a global variable
    return ( int ) ( kphValue * 0.621371 );
}

int determineRpmIndex( int rpmValue ) {
    if ( rpmValue >= 5000 )
        return 2;

    if ( rpmValue >= 3000 )
        return 1;

    return 0;
}
1
  • Looks good and learned a lot... I will upload when I get a chance and let you know! Commented May 2, 2014 at 0:42
1

I was found solution with

Serial.setTimeout(100);

Like this (https://www.arduino.cc/en/Serial/SetTimeout) setTimeout was 1000ms default.

the check serial port per 1000ms.

I referenced this https://stackoverflow.com/a/28892086/929740

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.