Add CFC1 and CAF1 modes for STN based ELMs

This commit is contained in:
Marianpol 2021-08-29 17:57:42 +00:00
parent 7c8e10db18
commit 12840ce1b5
2 changed files with 160 additions and 79 deletions

View File

@ -604,9 +604,19 @@ class ELM:
self.lf.flush()
# check OBDLink
elm_rsp = self.cmd("STPR")
elm_rsp = self.cmd("STI")
if elm_rsp and '?' not in elm_rsp:
firmware_version = elm_rsp.split(" ")[-1]
try:
firmware_version = firmware_version.split(".")
version_number = int(''.join([re.sub('\D', '', version) for version in firmware_version]))
stpx_introduced_in_version_number = 420 #STN1110 got STPX last in version v4.2.0
if version_number >= stpx_introduced_in_version_number:
mod_globals.opt_obdlink = True
except:
raw_input("\nCannot determine OBDLink version.\n" +
"OBDLink performance may be decreased.\n" +
"Press any key to continue...\n")
# check STN
elm_rsp = self.cmd("STP 53")
@ -1211,6 +1221,12 @@ class ELM:
if self.ATCFC0:
return self.send_can_cfc0 (command)
else:
if mod_globals.opt_obdlink:
if mod_globals.opt_caf:
rsp = self.send_can_cfc_caf(command)
else:
rsp = self.send_can_cfc(command)
else:
rsp = self.send_can (command)
if self.error_frame > 0 or self.error_bufferfull > 0: # then fallback to cfc0
@ -1222,7 +1238,6 @@ class ELM:
def send_can(self, command):
command = command.strip ().replace (' ', '').upper ()
isCommandInCache = command in self.l1_cache.keys()
commandString = command
if len(command) == 0:
return
@ -1236,7 +1251,7 @@ class ELM:
cmd_len = len (command) / 2
if cmd_len < 8: # single frame
# check L1 cache here
if isCommandInCache and int('0x' + self.l1_cache[commandString], 16) < 16:
if isCommandInCache and int('0x' + self.l1_cache[command], 16) < 16:
raw_command.append (("%0.2X" % cmd_len) + command + self.l1_cache[command])
else:
raw_command.append (("%0.2X" % cmd_len) + command)
@ -1251,16 +1266,6 @@ class ELM:
frame_number = frame_number + 1
command = command[14:]
# add response frames number to each frame to increase polling
if mod_globals.opt_obdlink and mod_globals.opt_perform:
if commandString[:2] in AllowedList and isCommandInCache:
for index in range(len(raw_command) - 1):
raw_command[index] = raw_command[index] + '1'
if int('0x' + self.l1_cache[commandString], 16) < 16:
raw_command[-1] = raw_command[-1] + self.l1_cache[commandString]
else:
raw_command[-1] = "STPX D:" + raw_command[-1] + ",R:" + self.l1_cache[commandString]
responses = []
# send farmes
@ -1328,11 +1333,8 @@ class ELM:
if result[:2] == '7F': noerrors = False
# populate L1 cache
if noerrors and commandString[:2] in AllowedList and not mod_globals.opt_n1c:
if nframes < 16:
self.l1_cache[commandString] = str(hex(nframes))[2:].upper()
else: #for OBDLink STPX command
self.l1_cache[commandString] = str(nframes)
if noerrors and command[:2] in AllowedList and not mod_globals.opt_n1c:
self.l1_cache[command] = str(hex(nframes))[2:].upper()
if len (result) / 2 >= nbytes and noerrors:
# split by bytes and return
@ -1354,9 +1356,72 @@ class ELM:
else:
return "WRONG RESPONSE"
# Can be used only with OBDLink based ELM, wireless especially.
def send_can_cfc_caf(self, command):
if len(command) == 0:
return
if len(command) % 2 != 0:
return "ODD ERROR"
if not all(c in string.hexdigits for c in command):
return "HEX ERROR"
frsp = self.send_raw('STPX D:' + command + ',R:' + '1')
responses = []
for s in frsp.split('\n'):
if s.strip()[:4] == "STPX": # echo cancelation
continue
s = s.strip().replace(' ', '')
if len(s) == 0: # empty string
continue
responses.append(s)
result = ""
noerrors = True
if len (responses) == 0: # no data in response
return ""
nodataflag = False
for s in responses:
if 'NO DATA' in s:
nodataflag = True
break
if all(c in string.hexdigits for c in s): # some data
result = s
# Check for negative
if result[:2] == '7F': noerrors = False
if noerrors:
# split by bytes and return
result = ' '.join(a + b for a, b in zip(result[::2], result[1::2]))
return result
else:
# check for negative response (repeat the same as in cmd())
# debug
# print "Size error: ", result
if result[:2] == '7F' and result[4:6] in negrsp.keys():
if self.vf != 0:
tmstr = datetime.now().strftime("%H:%M:%S.%f")[:-3]
self.vf.write(
tmstr + ";" + dnat[self.currentaddress] + ";" + command + ";" + result + ";" + negrsp[
result[4:6]] + "\n")
self.vf.flush()
return "NR:" + result[4:6] + ':' + negrsp[result[4:6]]
else:
return "WRONG RESPONSE"
# Can be used only with OBDLink based ELM
def send_can_cfc(self, command):
command = command.strip().replace(' ', '').upper()
init_command = command
if len(command) == 0:
return
@ -1388,37 +1453,26 @@ class ELM:
ST = 0 # Frame Interval
Fc = 0 # Current frame
Fn = len(raw_command) # Number of frames
frsp = ''
if raw_command[Fc].startswith('0') and init_command in self.l1_cache.keys():
frsp = self.send_raw ('STPX D:' + raw_command[Fc] + ',R:' + self.l1_cache[init_command])
elif raw_command[Fc].startswith('1'):
frsp = self.send_raw ('STPX D:' + raw_command[Fc] + ',R:' + '1')
else:
frsp = self.send_raw ('STPX D:' + raw_command[Fc])
if Fn > 1:
self.send_raw('at cfc1')
# print 'cfc1', raw_command
while Fc < Fn:
if Fn > 1 and (Fn - Fc) == 1:
self.send_raw('at cfc0')
# print 'cfc0:', Fn, Fc
# enable responses
frsp = ''
if not self.ATR1:
frsp = self.send_raw('at r1')
self.ATR1 = True
tb = time.time() # time of sending (ff)
if len (raw_command[Fc]) == 16:
frsp = self.send_raw (raw_command[Fc])
else:
frsp = self.send_raw (raw_command[Fc] + '1') # we'll get only 1 frame: fc, ff or sf
frsp = self.send_raw(raw_command[Fc])
if raw_command[Fc][:1] != '2':
Fc = Fc + 1
# analyse response
for s in frsp.split('\n'):
if s.strip()[:len(raw_command[Fc - 1])] == raw_command[Fc - 1]: # echo cancelation
if s.strip()[:4] == "STPX": # echo cancelation
continue
s = s.strip().replace(' ', '')
@ -1447,38 +1501,50 @@ class ELM:
continue
# sending consequent frames according to FlowControl
cf = min({BS - 1, (Fn - Fc) - 1}) # number of frames to send without response
# disable responses
if cf > 0:
if self.ATR1:
frsp = self.send_raw('at r0')
self.ATR1 = False
frames_left = (Fn - Fc)
cf = min({BS, frames_left}) # number of frames to send without response
while cf > 0:
cf = cf - 1
burst_size_command = ''
for f in range(0, cf):
burst_size_command += raw_command[Fc + f]
if burst_size_command.endswith(raw_command[-1]):
if init_command in self.l1_cache.keys():
burst_size_request = 'STPX D:' + burst_size_command + ",R:" + self.l1_cache[init_command]
else:
burst_size_request = 'STPX D:' + burst_size_command
else:
burst_size_request = 'STPX D:' + burst_size_command + ",R:1"
# Ensure time gap between frames according to FlowControl
tc = time.time() # current time
if (tc - tb) * 1000. < ST:
time.sleep(ST / 1000. - (tc - tb))
target_time = time.clock() + (ST / 1000. - (tc - tb))
while time.clock() < target_time:
pass
tb = tc
frsp = self.send_raw(raw_command[Fc])
Fc = Fc + 1
# now we are going to receive data. st or ff should be in responses[0]
if len(responses) != 1:
# print "Something went wrong. len responces != 1"
return "WRONG RESPONSE"
frsp = self.send_raw(burst_size_request)
Fc = Fc + cf
cf = 0
if burst_size_command.endswith(raw_command[-1]):
for s in frsp.split('\n'):
if s.strip()[:4] == "STPX": # echo cancelation
continue
else:
responses.append(s)
continue
result = ""
noErrors = True
noerrors = True
cFrame = 0 # frame counter
nBytes = 0 # number bytes in response
nFrames = 0 # numer frames in response
if len (responses) == 0: # no data in response
return ""
if responses[0][:1] == '0': # single frame (sf)
nBytes = int(responses[0][1:2], 16)
nFrames = 1
@ -1492,19 +1558,11 @@ class ELM:
result = responses[0][4:16]
# receiving consecutive frames
# while len (result) / 2 < nBytes:
while cFrame < nFrames:
# now we should send ff
sBS = hex(min({nFrames - cFrame, MaxBurst}))[2:]
frsp = self.send_raw('300' + sBS + '00' + sBS)
# analyse response
nodataflag = False
for s in frsp.split('\n'):
if s.strip()[:len(raw_command[Fc - 1])] == raw_command[Fc - 1]: # echo cancelation
continue
for s in responses:
if 'NO DATA' in s:
nodataflag = True
@ -1515,12 +1573,12 @@ class ELM:
continue
if all(c in string.hexdigits for c in s): # some data
responses.append(s)
# responses.append(s)
if s[:1] == '2': # consecutive frames (cf)
tmp_fn = int(s[1:2], 16)
if tmp_fn != (cFrame % 16): # wrong response (frame lost)
self.error_frame += 1
noErrors = False
noerrors = False
continue
cFrame += 1
result += s[2:16]
@ -1531,9 +1589,16 @@ class ELM:
else: # wrong response (first frame omitted)
self.error_frame += 1
noErrors = False
noerrors = False
if len(result) / 2 >= nBytes and noErrors and result[:2] != '7F':
# Check for negative
if result[:2] == '7F': noerrors = False
# populate L1 cache
if noerrors and init_command[:2] in AllowedList:
self.l1_cache[init_command] = str(nFrames)
if noerrors and len(result) / 2 >= nBytes:
# split by bytes and return
result = ' '.join(a + b for a, b in zip(result[::2], result[1::2]))
return result
@ -1886,7 +1951,14 @@ class ELM:
self.check_answer(self.cmd("at h0"))
self.check_answer(self.cmd("at l0"))
self.check_answer(self.cmd("at al"))
if mod_globals.opt_obdlink and mod_globals.opt_caf:
self.check_answer(self.cmd("AT CAF1"))
self.check_answer(self.cmd("STCSEGR 1"))
self.check_answer(self.cmd("STCSEGT 1"))
else:
self.check_answer(self.cmd("at caf0"))
if self.ATCFC0:
self.check_answer(self.cmd("at cfc0"))
else:
@ -1970,6 +2042,9 @@ class ELM:
self.check_answer (self.cmd ("at st ff")) # reset adaptive timing step 1
self.check_answer (self.cmd ("at at 0")) # reset adaptive timing step 2
if mod_globals.opt_obdlink and mod_globals.opt_caf:
self.check_answer (self.cmd ("STCFCPA " + TXa + ", " + RXa))
# some models of cars may have different CAN buses
if 'brp' in ecu.keys () and '1' in ecu['brp'] and '0' in ecu['brp']: # double brp
if self.lf != 0:
@ -2082,7 +2157,7 @@ class ELM:
break
if self.performanceModeLevel == 3 and mod_globals.opt_obdlink:
for level in reversed(range(4,26)): #26 - 1 = 25 parameters per page
for level in reversed(range(4,100)): #26 - 1 = 25 parameters per page
isLevelAccepted = self.checkPerformaceLevel(level, dataids)
if isLevelAccepted:
return
@ -2095,7 +2170,12 @@ class ELM:
frameLength = '{:02X}'.format(1 + level * 2)
for lvl in range(level):
paramToSend += dataids.keys()[lvl]
if mod_globals.opt_caf:
cmd = '22' + paramToSend + '1'
else:
cmd = frameLength + '22' + paramToSend + '1'
resp = self.send_raw(cmd)
for s in resp.split('\n'):
if s.strip().startswith('037F'):
@ -2103,7 +2183,7 @@ class ELM:
else: # send multiframe command for more than 3 dataids
# Some modules can return NO DATA if multi frame command is sent after some no activity time
# Sending anything before main command usually helps that command to be accepted
self.send_raw ("0322" + dataids.keys()[0] + "1")
self.send_cmd ("22" + dataids.keys()[0] + "1")
for lvl in range(level):
resp = self.request("22" + dataids.keys()[lvl])

View File

@ -23,6 +23,7 @@ opt_cmd = False
opt_ddt = False
opt_si = False #try slow init every time
opt_cfc0 = False #turn off automatic FC and do it by script
opt_caf = False #turn on CAN Automatic Formatting
opt_n1c = False #turn off L1 cache
opt_dev = False #switch to development session for commands from DevList
opt_devses = '1086' #development session for commands from DevList