Hi
For anyone interested, here is the code, that I'm currently using for my dash with the OBD2 interface.
As I don't have the extra 7-segment display, I show the gear on the first digit.
Button 1 shows Gear and Speed.
Button 2 shows Gear and RPM.
Button 3 shows Gear and current Position.
Button 8 switches to Mode to set MaxRPM (Apply full throttle and press Button again.)
Would be great to have the OBD2 interface also provide the current fuel left in the car.
- 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);
//définition des broches du décodeur 7 segments (vous pouvez changer
//les numéros si bon vous semble)
const int bit_A= 6;
const int bit_B= 7;
const int bit_C= 8;
const int bit_D= 9;
//Definition des Pin pour Affichage Neutral et reverse
const int bit_E= 10;
const int bit_F= 11;
const int ledPin = 13; // the number of the LED pin
//byte buttons;
unsigned int rpmmax = 7000;
unsigned int rpmlast;
int setrpm = 0;
int mode = 1;
//long previousMillis = 0; // will store last time LED was updated
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
//long interval = 40; // interval at which to blink (milliseconds)
bool extractordetected=false; //Will get updated from last positive or negative receive
int receivebuffer[20]={0}; //Receive buffer
void setup()
{
pinMode(13, OUTPUT); //Arduino UNO LED off
digitalWrite(13, 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
}
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
}
}
int ParseReceiveBuffer(int commandhighbyte, int commandlowbyte, int receivelength)
{
//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[5]);
int lowresult =HexToInt(receivebuffer[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[5]);
int toplowresult =HexToInt(receivebuffer[6]);
int highresult=HexToInt(receivebuffer[8]);
int lowresult =HexToInt(receivebuffer[9]);
if(tophighresult==-1 || toplowresult==-1 || highresult==-1 || lowresult==-1){return -1;}
return ((4096*tophighresult) + (256*toplowresult) + (16*highresult) + lowresult);
}
}
return -1; //Something is wrong with the returned OBD2 echo command byte
}
//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 < 500; 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 ParseReceiveBuffer(commandhighbyte, commandlowbyte, receivelength);
buffercount=-1;
}
}
}
delay(1);
}
return -1;
}
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
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
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
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('0');
Serial.write('\r');
return ReceiveValueWithTimeout('4','E','0',7);
}
//01e1 Request curent position
int GetOBD2PositionValue()
{
//01e0
Serial.write('0');
Serial.write('1');
Serial.write('e');
Serial.write('1');
Serial.write('\r');
return ReceiveValueWithTimeout('4','E','1',7);
}
//function to display the gear on the big 7 segmemts display
void afficher(char chiffre)
{
// pin are all set to lower levelet high level for neutral reverse
digitalWrite(bit_A, LOW);
digitalWrite(bit_B, LOW);
digitalWrite(bit_C, LOW);
digitalWrite(bit_D, LOW);
digitalWrite(bit_E, HIGH);
digitalWrite(bit_F, HIGH);
//display the numerical needed
if(chiffre >= 8)
{
digitalWrite(bit_D, HIGH);
chiffre= chiffre - 8;
}
if(chiffre>= 4)
{
digitalWrite(bit_C, HIGH);
chiffre= chiffre - 4;
}
if(chiffre >= 2)
{
digitalWrite(bit_B, HIGH);
chiffre= chiffre - 2;
}
if(chiffre >= 1)
{
digitalWrite(bit_A, HIGH);
chiffre= chiffre - 1;
}
}
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 speed = 0;
unsigned int rpm;
unsigned int pos;
int gear = GetOBD2GearValue();
byte button;
String output;
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;
}
//Read all values
gear = GetOBD2GearValue(); //Notice: offset +1 because of reverse gear
speed = GetOBD2SpeedValue();
rpm = GetOBD2RpmValue();
pos = GetOBD2PositionValue();
rpm = rpm/4;
//unsigned long currentMillis = millis();
button = module.getButtons();
switch ( button )
{
case 0b00000001:
mode = 1;
module.clearDisplay();
output = "Gear Spd";
module.setDisplayToString( output );
delay ( 500 );
module.clearDisplay();
break;
case 0b00000010:
mode = 2;
module.clearDisplay();
output = "Gear Rpm";
module.setDisplayToString( output );
delay ( 500 );
module.clearDisplay();
break;
case 0b00000100:
mode = 3;
module.clearDisplay();
output = "Posion";
module.setDisplayToString( output );
delay ( 500 );
module.clearDisplay();
break;
case 0b00001000: mode = 4;
break;
case 0b00010000: mode = 5;
break;
case 0b00100000: mode = 6;
break;
case 0b01000000: mode = 7;
break;
case 0b10000000:
mode = 8;
module.clearDisplay();
switch ( setrpm )
{
case 0:
setrpm = 1;
output = "Set RPM";
rpmmax = 100;
module.setDisplayToString( "Set RPM" );
mode = 8;
delay (500);
break;
case 1:
setrpm = 0;
module.clearDisplay();
module.setDisplayToString( "Gear Spd" );
mode = 1;
delay (500);
break;
}
break;
}
if(gear!=-1 && speed!=-1 && rpm!=-1)
{
digitalWrite(13, HIGH); //We have connection and all values are ok, turn on the LED
//Do here your LCD or display stuff
if ( mode == 1 )
{
module.setDisplayToDecNumber( speed, 0, false ); //displays numerical the speed
if ( gear >= 2 and gear <10 )
{
module.setDisplayDigit( gear - 1, 0, false ); // displays numerical value of the current gear
}
if ( gear == 1 )
{
module.setDisplayToString( neutral, 0, 0 ); // displays the character for neutral
}
if ( gear == 0 )
{ // -1 that reprecents reverse rollover to 255 so...
module.setDisplayToString( reverse, 0, 0 ); // displays the character for reverse
}
}
if ( mode == 2 )
{
module.setDisplayToDecNumber( rpm, 0, false ); //displays numerical the speed
if ( gear >= 2 and gear <10 )
{
module.setDisplayDigit( gear - 1, 0, false ); // displays numerical value of the current gear
}
if ( gear == 1 )
{
module.setDisplayToString( neutral, 0, 0 ); // displays the character for neutral
}
if ( gear == 0 )
{ // -1 that reprecents reverse rollover to 255 so...
module.setDisplayToString( reverse, 0, 0 ); // displays the character for reverse
}
}
if ( mode == 3 )
{
module.setDisplayToDecNumber( pos, 0, false );
if ( gear >= 2 and gear <10 )
{
module.setDisplayDigit( gear - 1, 0, false ); // displays numerical value of the current gear
}
if ( gear == 1 )
{
module.setDisplayToString( neutral, 0, 0 ); // displays the character for neutral
}
if ( gear == 0 )
{ // -1 that reprecents reverse rollover to 255 so...
module.setDisplayToString( reverse, 0, 0 ); // displays the character for reverse
}
}
if ( mode == 8 )
{
module.setDisplayToDecNumber( rpmmax, 0, false );
if ( rpmlast > ( rpmmax + 200 ) )
{
rpmmax = rpmlast;
}
}
//color the leds
rpmleds = map( rpm,0,rpmmax,0,16 ); // distributes the rpm level to the 8 leds + 1 for shift change
if ( rpmleds==1 ) {
module.setLEDs( 0b00000000 | 0b00000000 << 8);
}
if ( rpmleds==7 ) {
module.setLEDs( 0b00000001 | 0b00000000 << 8 );
}
if ( rpmleds==8 ) {
module.setLEDs( 0b00000011 | 0b00000000 << 8 );
}
if ( rpmleds==9 ) {
module.setLEDs( 0b00000111 | 0b00000000 << 8 );
}
if ( rpmleds==10 ) {
module.setLEDs( 0b00001111 | 0b00000000 << 8 );
}
if ( rpmleds==11 ) {
module.setLEDs( 0b00011111 | 0b00000000 << 8 );
}
if ( rpmleds==12 ) {
module.setLEDs( 0b00111111 | 0b00000000 << 8 );
}
if ( rpmleds==13 ) {
module.setLEDs( 0b01111111 | 0b00000000 << 8 );
}
if ( rpmleds==14 ) {
module.setLEDs( 0b00000000 | 0b11111111 << 8 );
}
if ( rpmleds>=15 ) {
module.setLEDs( 0b00000000 | 0b11111111 << 8 );
delay ( 20 );
module.setLEDs( 0b00000000 | 0b00000000 << 8 );
delay ( 20 );
}
}
rpmlast = rpm;
}
extractordetected=false;
digitalWrite(13, LOW); //No connection, turn off the LED
//Do here stuff to set display to zero or default
module.setDisplayToString( "No Data" ); //prints the banner
}