So for anyone interested, here is the latest Arduino code I've been using with the OBD2 Interface.
I changed to the EF command, to get all data at once, but there's still a little problem, as I don't get the fuel value.
But I will no further investigate in this, as meanwhile I've written my own Data extractor, so I can get much more data that I want to display.
I've also extended my Project with an extra 8 digit display to show the current laptime, and on beginning of each lap showing the difference to the bestlaptime for some seconds. So far everything is working perfect.

For the future I'm planing some further extensions (at least for games, where I can get the data, like rFactor):
- some extra LEDs, to go green, when Tire Temp is in optimum Range.
- RPM LEDs flashing, when Limiter is on.
- use RPM LEDs also for showing the Start Lights.
- and currently I'm trying to get the actual gap to either the own best lap, or the next car ahead, as you can see it for example ingame in GTR2.
Not all this is really necessary, but out of this project, I developed a real passion for building and coding
I will not post this program here, as it's not really polished, and also not directly related to X-Sim, but if anyone is interested, I'm willing to share.
- Code: Select all
// X-Sim Sample code for receiving OBD2 values from the extractor without using the converter
// OBD2 is industry standard, X-Sim will additionally expand the OBD2 PID with gear and other values
//
// Usage:
// Arduino has to be connected to a free USB port on the computer where the extractor is running
// Open the extractor and open the settings menu, there you have to select the OBD2 menu
// Add now your arduino comport to the OBD2 list.
// After closing the dialog you will see the arduino LED will be enabled which represents receiving data.
// After closing the extractor application the LED will switch off.
// Use this code to insert your own display code at the fitting positions
// Copyright 2013 Martin Wiedenbauer, X-Sim.de
//
// References:
// http://en.wikipedia.org/wiki/OBD-II_PIDs
// http://elmelectronics.com/DSheets/ELM327DS.pdf
// http://www.x-sim.de
#include <TM1638.h> //can be downloaded from http://code.google.com/p/tm1638-library/
// define a module on data pin 5, clock pin 4 and strobe pin 3
TM1638 module(5, 4, 3);
byte brightness = 2;
const int bit_A = 6;
const int bit_B = 7;
const int bit_C = 8;
const int bit_D = 9;
const int bit_E = 10;
const int bit_F = 11;
const int ledPin = 13; // the number of the LED pin
unsigned int rpmmax = 100;
unsigned int rpmlast;
int setrpm = 0;
int mode = 1;
int rpm, speed, coolant, gear, position, currentlap, fuel;
long bestlaptime;
bool extractordetected = false; //Will get updated from last positive or negative receive
int receivebuffer[100] = { 0 }; //Receive buffer
void setup()
{
pinMode(ledPin, OUTPUT); //Arduino UNO LED off
digitalWrite(ledPin, LOW);
Serial.begin(9600);
//Do here stuff to init display and zero to default
pinMode(ledPin, OUTPUT);
//The pins are all outputs
pinMode(bit_A, OUTPUT);
pinMode(bit_B, OUTPUT);
pinMode(bit_C, OUTPUT);
pinMode(bit_D, OUTPUT);
pinMode(bit_E, OUTPUT);
pinMode(bit_F, OUTPUT);
//The pins are all set to low for the BCD decoder and up to neutral and reverse control
digitalWrite(bit_A, LOW);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
// initialize the screen:
module.clearDisplay(); //clears the display from garbage if any
module.setDisplayToString("X-Sim 3"); //prints the banner
module.setLEDs(0b10000000 | 0b00000001 << 8);
delay(50);
module.setLEDs(0b11000000 | 0b00000011 << 8);
delay(50);
module.setLEDs(0b11100000 | 0b00000111 << 8);
delay(50);
module.setLEDs(0b11110000 | 0b00001111 << 8);
delay(50);
module.setLEDs(0b11111000 | 0b00011111 << 8);
delay(50);
module.setLEDs(0b11111100 | 0b00111111 << 8);
delay(50);
module.setLEDs(0b11111110 | 0b01111111 << 8);
delay(50);
module.setLEDs(0b11111111 | 0b11111111 << 8);
delay(200);
module.setLEDs(0b00000000 | 0b00000000 << 8);
delay(50);
module.setLEDs(0b11111111 | 0b11111111 << 8);
delay(100);
module.setLEDs(0b00000000 | 0b00000000 << 8);
delay(50);
module.setLEDs(0b11111111 | 0b11111111 << 8);
delay(100);
module.setLEDs(0b00000000 | 0b00000000 << 8);
delay(50);
module.setLEDs(0b11111111 | 0b11111111 << 8);
delay(100);
module.setLEDs(0b00000000 | 0b00000000 << 8);
delay(3000); //small delay 3 sec
module.clearDisplay(); //clears the 1st display
}
void displaygear(int gearvalue)
{
switch (gearvalue)
{
case -1:digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, HIGH);
digitalWrite(bit_C, HIGH);
digitalWrite(bit_D, HIGH);
digitalWrite(bit_E, LOW);
digitalWrite(bit_F, LOW);
break;
case 0: digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, HIGH);
digitalWrite(bit_C, HIGH);
digitalWrite(bit_D, HIGH);
digitalWrite(bit_E, LOW);
digitalWrite(bit_F, HIGH);
break;
case 1: digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 2: digitalWrite(bit_A, LOW);
digitalWrite(bit_B, HIGH);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 3: digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, HIGH);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 4: digitalWrite(bit_A, LOW);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, HIGH);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 5: digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, HIGH);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 6: digitalWrite(bit_A, LOW);
digitalWrite(bit_B, HIGH);
digitalWrite(bit_C, HIGH);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 7: digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, HIGH);
digitalWrite(bit_C, HIGH);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 8: digitalWrite(bit_A, LOW);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, HIGH);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
case 9: digitalWrite(bit_A, HIGH);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, HIGH);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
break;
}
}
int HexToInt(int c)
{
if (c >= '0' && c <= '9')
{
return c - '0';
}
else if (c >= 'a' && c <= 'f')
{
return c - 'a' + 10;
}
else if (c >= 'A' && c <= 'F')
{
return c - 'A' + 10;
}
else
{
return -1; // getting here is bad: it means the character was invalid
}
}
//This function will wait for the first character in receivetrigger and will parse the result
int ReceiveValueWithTimeout(int receivetrigger, int commandhighbyte, int commandlowbyte, int receivelength)
{
int arduinoserialbuffer = 0;
int buffercount = -1;
for (int z = 0; z < 1500; z++) //500ms timeout should be enough
{
while (Serial.available())
{
if (buffercount == -1)
{
arduinoserialbuffer = Serial.read();
if (arduinoserialbuffer != receivetrigger) //Wait until the trigger is reached, ignore echo, first character is not in the buffer
{
buffercount = -1;
}
else
{
buffercount = 0;
}
}
else
{
arduinoserialbuffer = Serial.read();
receivebuffer[buffercount] = arduinoserialbuffer;
buffercount++;
if (buffercount > receivelength) //buffer has now waitlen character length
{
return ParseReceiveBufferOffset(commandhighbyte, commandlowbyte, receivelength, 0);
buffercount = -1;
}
}
}
delay(1);
}
return -1;
}
long ParseReceiveBufferOffset(int commandhighbyte, int commandlowbyte, int receivelength, int offset)
{
//First character is removed of the receivebuffer string and must not be verified again
if (receivebuffer[0] == '1' && receivebuffer[2] == commandhighbyte && receivebuffer[3] == commandlowbyte)
{
//Parse 2 byte values on position 5 and 6 in the buffer string
if (receivelength == 7)
{
int highresult = HexToInt(receivebuffer[offset + 5]);
int lowresult = HexToInt(receivebuffer[offset + 6]);
if (highresult == -1 || lowresult == -1){ return -1; }
return ((16 * highresult) + lowresult);
}
//Parse 4 byte values on position 5,6,8 and 9 in the buffer string
if (receivelength == 10)
{
int tophighresult = HexToInt(receivebuffer[offset + 5]);
int toplowresult = HexToInt(receivebuffer[offset + 6]);
int highresult = HexToInt(receivebuffer[offset + 8]);
int lowresult = HexToInt(receivebuffer[offset + 9]);
if (tophighresult == -1 || toplowresult == -1 || highresult == -1 || lowresult == -1){ return -1; }
return ((4096 * tophighresult) + (256 * toplowresult) + (16 * highresult) + lowresult);
}
if (receivelength == 16)
{
int tophighresult = HexToInt(receivebuffer[offset + 5]);
int toplowresult = HexToInt(receivebuffer[offset + 6]);
int highresult = HexToInt(receivebuffer[offset + 8]);
int lowresult = HexToInt(receivebuffer[offset + 9]);
int tophighresult2 = HexToInt(receivebuffer[offset + 11]);
int toplowresult2 = HexToInt(receivebuffer[offset + 12]);
int highresult2 = HexToInt(receivebuffer[offset + 14]);
int lowresult2 = HexToInt(receivebuffer[offset + 15]);
if (tophighresult2 == -1 || toplowresult2 == -1 || highresult2 == -1 || lowresult2 == -1 || tophighresult == -1 || toplowresult == -1 || highresult == -1 || lowresult == -1){ return -1; }
return ((268435456 * tophighresult) + (16777216 * toplowresult) + (1048576 * highresult) + (65536 * lowresult) + (4096 * tophighresult2) + (256 * toplowresult2) + (16 * highresult2) + lowresult2);
}
}
return -1; //Something is wrong with the returned OBD2 echo command byte
}
bool ReceiveAllValuesWithTimeout(int receivetrigger, int commandhighbyte, int commandlowbyte, int receivelength)
{
int arduinoserialbuffer = 0;
int buffercount = -1;
for (int z = 0; z < 1500; z++) //500ms timeout should be enough
{
while (Serial.available())
{
if (buffercount == -1)
{
arduinoserialbuffer = Serial.read();
if (arduinoserialbuffer != receivetrigger) //Wait until the trigger is reached, ignore echo, first character is not in the buffer
{
buffercount = -1;
}
else
{
buffercount = 0;
}
}
else
{
arduinoserialbuffer = Serial.read();
receivebuffer[buffercount] = arduinoserialbuffer;
buffercount++;
if (buffercount > receivelength) //buffer has now waitlen character length
{
return true;
buffercount = -1;
}
}
}
delay(1);
}
return false;
}
void getAllData()
{
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('E');
Serial.write('F');
Serial.write('\r');
if (ReceiveAllValuesWithTimeout('4', 'E', 'F', 42))
{
rpm = ParseReceiveBufferOffset('E', 'F', 10, 0);
speed = ParseReceiveBufferOffset('E', 'F', 10, 6);
coolant = ParseReceiveBufferOffset('E', 'F', 7, 12);
gear = ParseReceiveBufferOffset('E', 'F', 7, 15);
position = ParseReceiveBufferOffset('E', 'F', 7, 18);
currentlap = ParseReceiveBufferOffset('E', 'F', 7, 21);
bestlaptime = ParseReceiveBufferOffset('E', 'F', 16, 24);
fuel = ParseReceiveBufferOffset('E', 'F', 7, 36);
}
}
void ClearReceiveBuffer()
{
while (Serial.available())
{
Serial.read();
}
}
void SendEchoDisabled() //not used here and a part of the OBD2 ELM327 specifications, as INFO
{
//ATE0 Echo disabled
Serial.write('A');
Serial.write('T');
Serial.write('E');
Serial.write('0');
Serial.write('\r');
}
//010c Request RPM, remember: OBD2 RPM is ((A*256)+B)/4
int GetOBD2RpmValue()
{
//010c
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('0');
Serial.write('c');
Serial.write('\r');
return ReceiveValueWithTimeout('4', '0', 'C', 10);
}
//010d Request speed in kmh
int GetOBD2SpeedValue()
{
//010d
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('0');
Serial.write('d');
Serial.write('\r');
return ReceiveValueWithTimeout('4', '0', 'D', 7);
}
//01e0 Request gear number
int GetOBD2GearValue()
{
//01e0
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('0');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '0', 7);
}
//01e1 Request current position
int GetOBD2PositionValue()
{
//01e0
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('1');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '1', 7);
}
int GetOBD2SpeedValue_new()
{
//01e0
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('1');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '1', 7);
}
int GetOBD2FuelValue()
{
//01e0
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('4');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '4', 7);
}
//01e5 Request current joystick button status 0 to 7
int GetOBD2JoyStatus0to7()
{
//01e5
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('5');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '5', 7);
}
//01e6 Request current joystick button status 8 to 15
int GetOBD2JoyStatus8to15()
{
//01e6
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('6');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '6', 7);
}
//01e7 Request current joystick button status 16 to 23
int GetOBD2JoyStatus16to23()
{
//01e7
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('7');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '7', 7);
}
//01e8 Request current joystick button status 24 to 31
int GetOBD2JoyStatus24to31()
{
//01e8
ClearReceiveBuffer();
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('8');
Serial.write('\r');
return ReceiveValueWithTimeout('4', 'E', '8', 7);
}
void loop()
{
//unsigned long currentMillis = millis();
int i;
char* neutral = "n"; // sets the character for neutral
char* reverse = "r"; // sets the character for reverse
unsigned int rpmleds; //holds the 8 leds values
char message[8]; // variable to display the message on the display
int fuel;
int joybutton;
byte button;
String output;
int dots = 40;
int hours;
int mins;
int secs;
int millisec;
long inttime;
gear = GetOBD2GearValue();
while (gear >= 0)
{
//unsigned long currentMillis = millis();
if (extractordetected == false)
{
SendEchoDisabled(); //Will cause a OK\r as answer from the extractor, ignored in this code
extractordetected = true;
}
getAllData();
if (rpmmax < rpmlast)
rpmmax = rpmlast + 100;
button = module.getButtons();
joybutton = GetOBD2JoyStatus8to15();
if ((button == 0b00000001) || (joybutton == 128))
{
mode = 1;
module.clearDisplay();
output = "Spd RPM";
module.setDisplayToString(output);
delay(500);
module.clearDisplay();
}
if ((button == 0b00000010) || (joybutton == 16))
{
mode = 2;
module.clearDisplay();
output = "Spd Fuel";
module.setDisplayToString(output);
delay(500);
module.clearDisplay();
}
if ((button == 0b00000100) || (joybutton == 32))
{
mode = 3;
module.clearDisplay();
output = "Spd Pos";
module.setDisplayToString(output);
delay(500);
module.clearDisplay();
}
if ((button == 0b00001000) || (joybutton == 64))
{
mode = 4;
module.clearDisplay();
output = "Spd Lap";
module.setDisplayToString(output);
delay(500);
module.clearDisplay();
}
if ((button == 0b00010000))// || (joybutton == 4))
{
mode = 5;
module.clearDisplay();
output = "Spd Temp";
module.setDisplayToString( output );
delay ( 500 );
module.clearDisplay();
}
if ((button == 0b00100000) || (joybutton == 4))
{
mode = 6;
module.clearDisplay();
output = "Best Lap";
module.setDisplayToString(output);
delay(500);
module.clearDisplay();
}
if ((button == 0b01000000) || (joybutton == 2))
{
rpmmax = rpmmax + 100;
output = "rst RPM";
module.setDisplayToString(output);
rpmmax = 100;
delay(500);
}
if ((button == 0b10000000) || (joybutton == 8))
{
mode = 8;
module.clearDisplay();
brightness = (brightness + 1) % 7;
module.setupDisplay(1, brightness);
sprintf(message, "Bright %d", brightness);
module.setDisplayToString(message);
delay(1000);
}
if (gear != -1 && speed != -1 && rpm != -1)
{
digitalWrite(ledPin, HIGH); //We have connection and all values are ok, turn on the LED
//Do here your LCD or display stuff
displaygear(gear - 1);
if (mode == 1)
{
sprintf(message, "%3d%5d", speed, rpm);
dots = 0;
}
if (mode == 2)
{
sprintf(message, "%3d F%3d", speed, fuel);
dots = 0;
}
if (mode == 3)
{
sprintf(message, "%3d P%3d", speed, position);
dots = 0;
}
if (mode == 4)
{
sprintf(message, "%3d L%3d", speed, currentlap);
dots = 0;
}
if (mode == 5)
{
sprintf(message, "%3d T%3d", speed, coolant);
dots = 0;
}
if (mode == 6)
{
dots = 40;
if (bestlaptime == -1)
{
sprintf(message, "000000");
}
else
{
inttime = bestlaptime / 1000;
millisec = bestlaptime % 1000;
hours = inttime / 3600;
inttime = inttime % 3600;
mins = inttime / 60;
inttime = inttime % 60;
secs = inttime;
sprintf(message, "%3d%02d%003d", mins, secs, millisec);
}
}
module.setDisplayToString(message, dots, 0);
//color the leds
rpmleds = map(rpm, 0, rpmmax, 0, 20); // distributes the rpm level to the 8 leds + 1 for shift change
if (rpmleds <= 10) {
module.setLEDs(0b00000000 | 0b00000000 << 8);
}
if (rpmleds == 11) {
module.setLEDs(0b00000001 | 0b00000000 << 8);
}
if (rpmleds == 12) {
module.setLEDs(0b00000011 | 0b00000000 << 8);
}
if (rpmleds == 13) {
module.setLEDs(0b00000111 | 0b00000000 << 8);
}
if (rpmleds == 14) {
module.setLEDs(0b00001111 | 0b00000000 << 8);
}
if (rpmleds == 15) {
module.setLEDs(0b00011111 | 0b00000000 << 8);
}
if (rpmleds == 16) {
module.setLEDs(0b00111111 | 0b00000000 << 8);
}
if (rpmleds == 17) {
module.setLEDs(0b01111111 | 0b00000000 << 8);
}
if (rpmleds == 18) {
module.setLEDs(0b00000000 | 0b11111111 << 8);
}
if (rpmleds >= 19) {
module.setLEDs(0b00000000 | 0b11111111 << 8);
delay(20);
module.setLEDs(0b00000000 | 0b00000000 << 8);
delay(20);
}
}
rpmlast = rpm;
}
extractordetected = false;
digitalWrite(ledPin, LOW); //No connection, turn off the LED
//Do here stuff to set display to zero or default
module.setDisplayToString("No Data"); //prints the banner
}