2929########################################################################
3030
3131import serial
32- import string
3332import time
34- from utils import Response , unhex
33+ import protocols
34+ from utils import strip
3535from debug import debug
3636
3737
38- class State ():
39- """ Enum for connection states """
40- Unconnected , Connected = range (2 )
41-
4238
4339class OBDPort :
4440 """ OBDPort abstracts all communication with OBD-II device."""
4541
4642 def __init__ (self , portname ):
4743 """Initializes port by resetting device and gettings supported PIDs. """
4844
49- self .ELMver = "Unknown"
50- self .state = State . Unconnected
51- self .port = None
45+ self .connected = False
46+ self .port = None
47+ self .protocol = None
5248
5349 # ------------- open port -------------
5450
@@ -71,40 +67,58 @@ def __init__(self, portname):
7167
7268 debug ("Serial port successfully opened on " + self .get_port_name ())
7369
74- # ------------- atz (reset) -------------
70+
71+ # ---------------------------- ATZ (reset) ----------------------------
7572 try :
76- r = self .write_and_read ("atz" , 1 ) # wait 1 second for ELM to initialize
77- r = self .__strip (r )
78- if not r :
79- self .__error ("atz (reset) did not return with an ELM version" )
80- return
81- self .ELMver = r
82-
73+ r = self .send ("ATZ" , delay = 1 ) # wait 1 second for ELM to initialize
74+ # return data can be junk, so don't bother checking
8375 except serial .SerialException as e :
8476 self .__error (e )
8577 return
8678
87- # ------------- ate0 (echo OFF) -------------
88- r = self .write_and_read ("ate0" )
89- r = self .__strip (r )
79+
80+ # -------------------------- ATE0 (echo OFF) --------------------------
81+ r = self .send ("ATE0" )
82+ r = strip (r )
9083 if (len (r ) < 2 ) or (r [- 2 :] != "OK" ):
91- self .__error ("ate0 did not return 'OK'" )
84+ self .__error ("ATE0 did not return 'OK'" )
9285 return
9386
94- # ------------- ath1 (headers ON) -------------
95- r = self .write_and_read ("ath1" )
96- r = self .__strip (r )
87+
88+ # ------------------------- ATH1 (headers ON) -------------------------
89+ r = self .send ("ATH1" )
90+ r = strip (r )
9791 if r != 'OK' :
98- self .__error ("ath1 did not return 'OK', or echoing is still ON" )
92+ self .__error ("ATH1 did not return 'OK', or echoing is still ON" )
9993 return
10094
101- # ------------- done -------------
102- debug ("Connection successful" )
103- self .state = State .Connected
95+
96+ # ----------------------- ATSP0 (protocol AUTO) -----------------------
97+ r = self .send ("ATSP0" )
98+ r = strip (r )
99+ if r != 'OK' :
100+ self .__error ("ATSP0 did not return 'OK'" )
101+ return
104102
105103
106- def __strip (self , s ):
107- return "" .join (s .split ())
104+ # -------------- 0100 (first command, SEARCH protocols) --------------
105+ r = self .send ("0100" , delay = 1 ) # give it a second to search
106+
107+
108+ # ----------------------- ATDP (list protocol) -----------------------
109+ r = self .send ("ATDP" )
110+ r = strip (r )
111+ protocol_class = protocols .get (r ) # lookup the protocol by name
112+ if protocol_class is None :
113+ self .__error ("ELM responded with unknown protocol" )
114+ return
115+
116+ self .protocol = protocol_class ()
117+
118+
119+ # ------------------------------- done -------------------------------
120+ debug ("Connection successful" )
121+ self .connected = True
108122
109123
110124 def __error (self , msg = None ):
@@ -118,36 +132,53 @@ def __error(self, msg=None):
118132 if self .port is not None :
119133 self .port .close ()
120134
121- self .state = State . Unconnected
135+ self .connected = False
122136
123137
124138 def get_port_name (self ):
125139 return self .port .portstr if (self .port is not None ) else "No Port"
126140
127141
128142 def is_connected (self ):
129- return self .state == State . Connected
143+ return self .connected
130144
131145
132146 def close (self ):
133147 """ Resets device and closes all associated filehandles"""
134148
135- if (self .port != None ) and ( self .state == State . Connected ) :
136- self .write ( "atz " )
149+ if (self .port != None ) and self .connected :
150+ self .__write ( "ATZ " )
137151 self .port .close ()
138152
139- self .port = None
140- self .ELMver = "Unknown"
153+ self .connected = False
154+ self .port = None
155+ self .protocol = None
156+
141157
158+ def send_and_parse (self , cmd , delay = None ):
159+
160+ r = self .send (cmd , delay )
142161
143- def write_and_read (self , cmd , delay = None ):
162+ messages = self .protocol (r ) # parses string into list of messages
163+
164+ # if more than one ECUs have responded, pick the primary
165+ # TODO: add support for more ECU types
166+ if len (messages ) > 1 :
167+ messages = filter (lambda m : m .tx_id == self .protocol .PRIMARY_ECU , messages )
168+
169+ return messages [0 ]
170+
171+
172+ def send (self , cmd , delay = None ):
144173
145174 self .__write (cmd )
146175
147176 if delay is not None :
148177 time .sleep (delay )
149178
150- return self .__read ()
179+ r = self .__read ()
180+
181+ return r # return raw string only
151182
152183
153184 # sends the hex string to the port
@@ -162,7 +193,8 @@ def __write(self, cmd):
162193 debug ("cannot perform write() when unconnected" , True )
163194
164195
165- # accumulates and returns the ports response
196+ # accumulates until the prompt character is seen
197+ # returns raw string
166198 def __read (self ):
167199
168200 attempts = 2
0 commit comments