AppleScript C Perl Shell Xcode Other

Perl/Tk OBD II interface

Post Reply
coding / perl     Views: 765Prev .. Next
Perl/Tk OBD II interfacePosted: Monday, October 28, 2019 [13:06:49] - 1
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
Used ELM327 OBD USB module with Perl/Tk to display live OBD data on a 1996 Toyota Camry.
It does not offer much as far as OBD data due to early implementation of protocol.
Besides live data interface shows trouble codes and those codes can be cleared.

ELM327 OBD USB module
Perl/Tk OBD II interface
Live data GUI

Perl/Tk OBD II interface

Perl/Tk OBD II interface
Interface can be set to show as much data as protocol can provide.
For 96' Toyota Camry protocol is J1850 VPW
it uses 4 OBD plug pins:

Perl/Tk OBD II interface

Perl/Tk OBD II interface
Pins:
Pin 2 - J1850 Bus+
Pin 4 - Chassis Ground
Pin 5 - Signal Ground
Pin 6 - CAN High (J-2284)
Pin 7 - ISO 9141-2 K Line
Pin 10 - J1850 Bus
Pin 14 - CAN Low (J-2284)
Pin 15 - ISO 9141-2 L Line
Pin 16 - Battery PowerThere's no place like ~
Perl/Tk OBD II interface programPosted: Monday, October 28, 2019 [13:16:38] - 2
rootPosted by:rootMember Since:
June 16 2010
Posts: 357
Program itself:
View Code#!/usr/bin/perl
## Create a global hash with live data and update each gauge from the hash
use strict;
use warnings;
use Tk;
use Tk::Gauge;
use Tk::Pane;
use Device::ELM327;
use subs qw/ getfilesdata readvalues tachometer speedometer temperature throttle intakemap igntiming loadvalue intaketemperature voltage /;

$0 =~ m/(.+)[\/\\](.+)$/;my $progpath = $1;
unless(-d $progpath) {print "Please run this program with a fully qualified path\n";exit(0);}
my $localtime = `date`;chop $localtime;$localtime =~ tr/ //s;my($wd,$mo,$da,$ti,$sh,$ye) = split(/\s/,$localtime);$ti =~ s#\:#\.#g;
our %readdata=();

my @parameters = ( # Parameters to read, changes with car make and model
'Engine Coolant Temperature',
'Absolute Throttle Position',
'Engine RPM',
'Calculated LOAD Value!',
'Bank 1 - Sensor 1',
'Bank 1 - Sensor 2',
'Intake Air Temperature',
'Vehicle Speed Sensor',
'Intake Manifold Absolute Pressure',
'Long Term Fuel Trim - Bank 1/3',
'Ignition Timing Advance for #1 Cylinder',
'Input Voltage'
);
open(TXR,">$progpath/res/$mo.$da.$ti.txt"); ## Record all data to a plain text file

my $debug_level = 0; # 0 to suppress debug output, >0 to print it.
my $port_name='/dev/cu.usbserial';## Change port accordingly
my $obd = Device::ELM327->new($port_name, $debug_level);
if ($obd->{'port'} == -1) {die "Can't open $port_name: $!\n";}
my $mw = MainWindow->new;&setwindow($mw);
$mw->configure(-title=>"OBD II Live", -background=>"white");
$mw->Label(-text => 'OBD II Live',-font=>'OpenSans 22',-background =>'white')->pack();
#$mw->Button(-text => "Close", -command =>sub{exit})->pack();
my $mtop = $mw->Scrolled("Pane", -scrollbars => '')->pack( -fill => 'both', -expand => 1 );
$mtop->configure( -background => 'black' );

my $mbot = $mw->Scrolled("Pane", -scrollbars => '')->pack( -fill => 'both', -expand => 1 );
$mbot->configure( -background => 'black' );

my $tachf = $mtop->Frame->pack( qw/ -side left / );
my $speedf = $mtop->Frame->pack( qw/ -side left / );
my $tempf = $mtop->Frame->pack( qw/ -side left / );
my $cload = $mbot->Frame->pack( qw/ -side left / );# Load
my $tint = $mbot->Frame->pack( qw/ -side left / );# Intake Temp
my $throt = $mbot->Frame->pack( qw/ -side left / );# Throttle
my $imap = $mbot->Frame->pack( qw/ -side left / );# Intake MAP
my $ignt = $mbot->Frame->pack( qw/ -side left / );# Ignition Timing
my $volt = $mbot->Frame->pack( qw/ -side left / );# Ignition Timing

my $t = $tachf->Frame->pack( qw/ -side left / );
$t->Frame( qw/ -height 30 -background black / )->pack( qw/ -fill both -expand yes / );

tachometer ( $t )->pack( qw/ -side left / );
speedometer ( $speedf )->pack( qw/ -side left / );

my $tf = $tempf->Frame->pack( qw/ -side bottom / );
temperature ( $tf )->pack( qw/ -side left / );

my $trt = $throt->Frame->pack( qw/ -side left / );
throttle ( $trt )->pack( qw/ -side left / );

my $mapint = $imap->Frame->pack( qw/ -side left / );
intakemap ( $mapint )->pack( qw/ -side left / );

my $ignit = $ignt->Frame->pack( qw/ -side left / );
igntiming ( $ignit )->pack( qw/ -side left / );

my $calcld = $cload->Frame->pack( qw/ -side left / );
loadvalue ( $calcld )->pack( qw/ -side left / );

my $tempintake = $tint->Frame->pack( qw/ -side left / );
intaketemperature ( $tempintake )->pack( qw/ -side left / );

my $svolt = $volt->Frame->pack( qw/ -side left / );
voltage ( $svolt )->pack( qw/ -side left / );

$mw->repeat(1500 => \&run); # It appears that value below 1500 (1.5 second) may not work as Tk can't react as fast

MainLoop;

sub run {
readvalues;
$mw->update;
} ## END SUB RUN

sub readvalues {
my @reads = @parameters;
# Convert names to something shorted to display in a program window
my %conv = ('Engine Coolant Temperature','ECT',
'Absolute Throttle Position','Throttle',
'Engine RPM','RPM',
'Calculated LOAD Value!','LOAD',
'Sensor 1 Oxygen Sensor Output Voltage (Bx-Sy)','STFT V 1',
'Sensor 1 Short Term Fuel Trim (Bx-Sy)','STFT 1',
'Sensor 2 Oxygen Sensor Output Voltage (Bx-Sy)','STFT V 2',
'Sensor 2 Short Term Fuel Trim (Bx-Sy)','STFT 2',
'Intake Air Temperature','Intake Temp',
'Vehicle Speed Sensor','Speed',
'Intake Manifold Absolute Pressure','Int. MAP',
'Bank 1/3 Long Term Fuel Trim - Bank 1','LTFT 1',
'Bank 1/3 Long Term Fuel Trim - Bank 3','LTFT 3',
'Ignition Timing Advance for #1 Cylinder','Ign. Timing',
'Input Voltage','V'
);

foreach my $p (@reads) {
my $resp = readobd($obd,$p);
my @alln = split(/\n/,$resp);
my $p1=$p;$p1 =~ s#(.*?) \- (.*?)#$2#;
foreach my $rvlu (@alln) {
$rvlu =~ s#(.*?)\:(.*)#$1#;my $val=$2;
my $hkey = "$p1 $rvlu";$hkey =~ s#($p) ($p)#$2#;$hkey =~ s/^\s+//;
my $rke = '';if($conv{$hkey}) {$rke=$conv{$hkey};}
print TXR "$p\t$hkey\t$rke\t$val\n"; ## Checking that data is processed - it is processed properly
if($val < 0 && $val) {$val =~ s/^\-//;}
$readdata{$rke}=$val; ## Hash changes properly
} ## FOREACH VALUE END
} ## FOREACH PARAMETER END
return (%readdata);
} ## END READ VALUES

sub readobd {
my ($ffrom,$senssor) = @_;my $obdprint='';
my $obres = $ffrom->Read($senssor);
if ($obres->{'status'} eq "ok") {
foreach my $obread (@{$obres->{'results'}}) {
$obdprint .= "$obread->{'name'}:$obread->{'value'}\n";
}
} else {
$obdprint .= "$obres->{'status'}\n";
}
return($obdprint);
} ## END DISPLAY VALUES

sub setwindow {
my $window = shift;$window->idletasks;
my $width = $window->screenwidth - 40;
my $height = $window->screenheight - 100;
$window->geometry($width . "x" . $height . "+20+30");
}

sub tachometer {
my $c = shift;
my $iLCD = 50; # integral Lowest Common Denominator
my $tachv;
my $tach = $c->Gauge(
-start => 225, -extent => -270,
-background => 'black',
-bands => [
{
-arccolor => 'red',
-minimum => 6100 / $iLCD,
-maximum => 8000 / $iLCD,
-tags => 'redtach',
},
{
-arccolor => '#f4f118',
-minimum => 4500 / $iLCD,
-maximum => 6100 / $iLCD,
-tags => 'yeltach',
},
{
-arccolor => 'red',
-minimum => 0 / $iLCD,
-maximum => 600 / $iLCD,
-tags => 'redtach',
},
],
-bandwidth => 4,
-bandstyle => 'band',
-bandplace => 'underticks',
-fill => '#343434',
-finetickinterval => 250 / $iLCD,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 0,
-highlightthickness => 0,
-hubcolor => '#aeaeaf',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 15,
-majortickinterval => 1000 / $iLCD,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-majorticklabelscale => $iLCD / 1000,
-margin => 60,
-minortickinterval => 500 / $iLCD,
-minorticklength => 10,
-minortickcolor => 'white',
-needles => [
{
-radius => 120,
-color => 'red',
-command => [ sub {
# print "tach args=@_!\n";
1;
}, 1, 2 ],
-variable => \$tachv,
-width => 4,
-showvalue => 0,
-title => 'x 1000 RPM',
-titlecolor => 'white',
-titleplace => 'south',
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 15,
-to => 8000 / $iLCD,
);
$mw->repeat(100 => sub { if ($readdata{'RPM'}) { $tachv = $readdata{'RPM'} / $iLCD;} else {$tachv = 80 / $iLCD;} } );
return $tach;
} # end tachometer

sub speedometer {
my $c = shift;
my $font = 'Helvetica 20';
my $radius = 80;
my $speedv;
my $speedvk;
my $speed = $c->Gauge(
-background => 'black',
-fill => '#343434',
-bands => [
{
-arccolor => '#46a317',
-minimum => 55,
-maximum => 65,
},
{
-arccolor => '#faf704',
-minimum => 65,
-maximum => 75,
},
{
-arccolor => '#cdcdcd',
-minimum => 75,
-maximum => 145,
},
],
-bandwidth => 2,
-bandstyle => 'band',
-bandplace => 'underticks',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 0,
-highlightthickness => 0,
-hubcolor => '#aeaeaf',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 20,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 10,
-minorticklength => 15,
-margin => 30,
-minortickcolor => 'white',
-needles => [
{
-radius => 140,
-color => '#3a7efb',
-variable => \$speedvk,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
{
-radius => 160,
-color => 'orangered2',
-variable => \$speedv,
-width => 3,
-showvalue => 0,
-title => 'MpH / KmH',
-titlecolor => 'white',
-titleplace => 'south',
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 15,
-to => 150,
);

$mw->repeat(1000 => sub { if ($readdata{'Speed'}) {$speedvk=$readdata{'Speed'}; $readdata{'Speed'} /= 1.60934; $speedv = sprintf("%0.0f",$readdata{'Speed'});} else {$speedv = 0; $speedvk=0;} } );
return $speed;
} # end speedoemter

sub temperature {
my $c = shift;
my $font = 'Helvetica -20';
my $tempv;
my $radius = 80;
my $ticks = 36;

my $temp = $c->Gauge(
-background => 'black',
-bands => [
{
-arccolor => 'red',
-minimum => 26,
-maximum => 36,
},
{
-arccolor => 'green',
-minimum => 23,
-maximum => 26,
},
{
-arccolor => 'yellow',
-minimum => 19,
-maximum => 23,
},
{
-arccolor => '#5d98fa',
-minimum => 1,
-maximum => 19,
},
],
-bandwidth => 10,
-bandstyle => 'band',
-bandplace => 'underticks',
-extent => -90,
-fill => '#343434',
-from => 0,
-highlightthickness => 0,
-hubcolor => '#aeaeaf',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 12,
-majorticklength => 14,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-majorticklabelplace => 'hide',
-majorticklabelscale => .5,
-majortickthickness => 3,
-minortickinterval => 4,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 30,
-needles => [
{
-radius => $radius,
-color => 'orangered2',
-variable => \$tempv,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-to => $ticks,
-start => 180,
-style => 'pieslice',
);

my( $hx, $hy ) = $temp->centerpoint;
my $fw = $temp->fontMeasure( $font, '0' );$fw += 5;
$temp->createText( $hx + $fw , $hy - $radius, -text => 'H', -fill => 'white', -font => $font );
$temp->createText( $hx - $radius , $hy + $fw, -text => 'C', -fill => 'white', -font => $font );

$mw->repeat(1000 => sub { if ($readdata{'ECT'}) {$tempv = $readdata{'ECT'} * 0.25;} else {$tempv = 0;} } );
return $temp;
} # end temperature

sub throttle {
my $c = shift;
my $font = 'Helvetica -20';
my $throttle;
my $combi = $c->Gauge(
-start =>270,
-extent => -120,
-background => 'black',
-fill => '#343434',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 0,
-highlightthickness => 0,
-hubcolor => 'black',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 20,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 5,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 10,
-needles => [
{
-radius => 75,
-color => '#dc9d05',
-variable => \$throttle,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 10,
-to => 80,
-style => 'pieslice',
);
$combi->createText( 80 , 40, -text => 'Throttle', -fill => 'white', -font => $font );
$mw->repeat(1000 => sub {
if($readdata{'Throttle'}) {$throttle = sprintf("%0.0f",$readdata{'Throttle'});} else {$throttle = 0;}
} );
return $combi;
} # end combination

sub intakemap {
my $c = shift;
my $font = 'Helvetica -20';
my $throttle;
my $load;
my $inttemp;
my $map;
my $timing;
my $voltage;
my $radius = 80;
my $iimap = $c->Gauge(
-start =>270,
-extent => -120,
-background => 'black',
-fill => '#343434',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 0,
-highlightthickness => 0,
-hubcolor => 'black',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 40,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 5,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 10,
-needles => [
{
-radius => 75,
-color => '#fdf403',
-variable => \$map,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 10,
-to => 120,
-style => 'pieslice',
);
$iimap->createText( 90 , 40, -text => 'Intake MAP', -fill => 'white', -font => $font );
$mw->repeat(1000 => sub {
if($readdata{'Int. MAP'}) {$map = $readdata{'Int. MAP'}; } else {$map = 0;}
} );
return $iimap;
} ## END SUB Intake Manifold Absolute Pressure

sub igntiming {
my $c = shift;
my $font = 'Helvetica -20';
my $timing;
my $radius = 80;
my $itiming = $c->Gauge(
-start =>270,
-extent => -120,
-background => 'black',
-fill => '#343434',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 20,
-highlightthickness => 0,
-hubcolor => 'black',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 20,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 5,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 10,
-needles => [
{
-radius => 75,
-color => '#0623fb',
-variable => \$timing,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 10,
-to => 80,
-style => 'pieslice',
);
$itiming->createText( 80 , 40, -text => 'Ign. Timing', -fill => 'white', -font => $font );
$mw->repeat(1000 => sub {
if($readdata{'Ign. Timing'}) {$timing = $readdata{'Ign. Timing'}; print "$timing\n"; } else {$timing = 0;}
} );
return $itiming;
} ## END IGNITION TIMING

sub loadvalue {
my $c = shift;
my $font = 'Helvetica -20';
my $load;
my $radius = 80;
my $combi = $c->Gauge(
-start =>270,
-extent => -120,
-background => 'black',
-fill => '#343434',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 0,
-highlightthickness => 0,
-hubcolor => 'black',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 20,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 5,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 10,
-needles => [
{
-radius => 75,
-color => '#fa915d',
-variable => \$load,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 10,
-to => 100,
-style => 'pieslice',
);
$combi->createText( 90 , 40, -text => 'Load', -fill => 'white', -font => $font );
$mw->repeat(1000 => sub {
if($readdata{'LOAD'}) {$load = sprintf("%0.0f",$readdata{'LOAD'}); } else {$load = 0;}
} );
return $combi;
} # end Load

sub intaketemperature {
my $c = shift;
my $font = 'Helvetica -20';
my $inttemp;
my $radius = 80;
my $combi = $c->Gauge(
-start =>270,
-extent => -120,
-background => 'black',
-fill => '#343434',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 0,
-highlightthickness => 0,
-hubcolor => 'black',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 20,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 5,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 10,
-needles => [
{
-radius => 75,
-color => '#5dfa73',
-variable => \$inttemp,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 10,
-to => 100,
-style => 'pieslice',
);
$combi->createText( 100 , 40, -text => 'Intake Temp', -fill => 'white', -font => $font );
$mw->repeat(1000 => sub {
if($readdata{'Intake Temp'}) {$inttemp = $readdata{'Intake Temp'}; } else {$inttemp = 0;}
} );
return $combi;
} ## END SUB INTAKE TEMPERATURE

sub voltage {
my $c = shift;
my $font = 'Helvetica -20';
my $voltage;
my $radius = 80;
my $combi = $c->Gauge(
-start =>270,
-extent => -120,
-background => 'black',
-fill => '#343434',
-bands => [
{
-arccolor => '#fa1924',
-minimum => 10,
-maximum => 12,
},
{
-arccolor => '#f6f917',
-minimum => 12,
-maximum => 12.8,
},
{
-arccolor => '#33bd03',
-minimum => 13,
-maximum => 15,
},
],
-bandwidth => 2,
-bandstyle => 'band',
-bandplace => 'underticks',
-finetickinterval => 5,
-fineticklength => 5,
-finetickcolor => 'white',
-from => 10,
-highlightthickness => 0,
-hubcolor => 'black',
-huboutline => 'black',
-hubplace => 'underneedle',
-hubradius => 20,
-majortickinterval => 5,
-majorticklength => 15,
-majortickcolor => 'white',
-majorticklabelcolor => 'white',
-minortickinterval => 1,
-minortickcolor => 'white',
-minorticklength => 9,
-margin => 10,
-needles => [
{
-radius => 75,
-color => '#5d98fa',
-variable => \$voltage,
-width => 3,
-showvalue => 0,
-arrowshape => [ 6, 6, 0 ],
},
] ,
-needlepad => 10,
-to => 20,
-style => 'pieslice',
);
$combi->createText( 100 , 40, -text => 'V', -fill => 'white', -font => $font );
$mw->repeat(1000 => sub {
if($readdata{'V'}) {$voltage = $readdata{'V'}; } else {$voltage = 0;}
} );
return $combi;
} ## END SUB VOLTAGE



program shows positive numbers for Ignition timing while in-fact they're negative numbers.
Also, it shows speed in MpH (red needle) and Kph (blue needle).
unfortunately refresh ratio is about 1.5 seconds. Any number below 1.5 seconds renders dead GUI. It may depend on interface reading speed or Tk rendering.There's no place like ~
coding / perlPrev .. Next
 
Post Reply
Home - Coding: AppleScript C Perl Shell Xcode Other
Our Telegram Group