ddt4all
ddt4all copied to clipboard
solved the problem it hangs when start connection with wifi device
My elm.py file looks totally different :
-- coding: utf-8 --
'''module contains class for working with ELM327 version: 160829 Borrowed from PyRen (modified for this use) '''
import options
import serial from serial.tools import list_ports
import sys import os import re import time import string from datetime import datetime
_ = options.translator('ddt4all')
snat = { "01": "760", "02": "724", "04": "762", "06": "791", "07": "771", "08": "778", "09": "7EB", "0D": "775", "0E": "76E", "0F": "770", "11": "7C9", "13": "732", "16": "18DAF271", "1A": "731", "1B": "7AC", "1C": "76B", "1E": "768", "23": "773", "24": "77D", "25": "700", "26": "765", "27": "76D", "28": "7D7", "29": "764", "2A": "76F", "2B": "735", "2C": "772", "2D": "18DAF12D", "2E": "7BC", "2F": "76C", "32": "776", "3A": "7D2", "40": "727", "41": "18DAF1D2", "46": "7CF", "47": "7A8", "4D": "7BD", "50": "738", "51": "763", "57": "767", "58": "767", "59": "734", "5B": "7A5", "60": "18DAF160", "61": "7BA", "62": "7DD", "63": "73E", "64": "7D5", "66": "739", "67": "793", "68": "77E", "6B": "7B5", "6E": "7E9", "73": "18DAF273", "77": "7DA", "78": "7BD", "79": "7EA", "7A": "7E8", "7C": "77C", "81": "761", "82": "7AD", "86": "7A2", "87": "7A0", "91": "7ED", "93": "7BB", "95": "7EC", "97": "7C8", "A1": "76C", "A5": "725", "A6": "726", "A7": "733", "A8": "7B6", "C0": "7B9", "D0": "18DAF1D0", "D1": "7EE", "D2": "18DAF1D2", "D3": "7EE", "D6": "18DAF2D6", "DA": "18DAF1DA", "DB": "18DAF1DB", "DE": "69C", "DF": "18DAF1DF", "E0": "18DAF1E0", "E1": "18DAF1E1", "E2": "18DAF1E2", "E3": "18DAF1E3", "E4": "757", "E6": "484", "E7": "7EC", "E8": "5C4", "E9": "762", "EA": "4B3", "EB": "5B8", "EC": "5B7", "ED": "704", "F7": "736", "F8": "737", "FA": "77B", "FD": "76F", "FE": "76C", "FF": "7D0" }
dnat = { "01": "740", "02": "704", "04": "742", "06": "790", "07": "751", "08": "758", "09": "7E3", "0D": "755", "0E": "74E", "0F": "750", "11": "7C3", "13": "712", "15": "18DA15F1", "16": "18DA71F2", "18": "18DA18F1", "1A": "711", "1B": "7A4", "1C": "74B", "1E": "748", "23": "753", "24": "75D", "25": "70C", "26": "745", "27": "74D", "28": "78A", "29": "744", "2A": "74F", "2B": "723", "2C": "752", "2D": "18DA2DF1", "2E": "79C", "2F": "74C", "32": "756", "3A": "7D6", "40": "707", "41": "18DAD0F1", "46": "7CD", "47": "788", "4D": "79D", "50": "718", "51": "743", "57": "747", "58": "747", "59": "714", "5B": "785", "60": "18DA60F1", "61": "7B7", "62": "7DC", "63": "73D", "64": "7D4", "66": "719", "67": "792", "68": "75A", "6B": "795", "6E": "7E1", "73": "18DA73F2", "77": "7CA", "78": "746", "79": "7E2", "7A": "7E0", "7B": "18DA72F2", "7C": "75C", "81": "73F", "82": "7AA", "86": "782", "87": "780", "91": "7E5", "93": "79B", "95": "7E4", "97": "7D8", "A1": "74C", "A5": "705", "A6": "706", "A7": "713", "A8": "796", "C0": "799", "D0": "18DAD0F1", "D1": "7E6", "D2": "18DAD2F1", "D3": "7E6", "D6": "18DAD6F2", "DA": "18DADAF1", "DB": "18DADBF1", "DE": "6BC", "DF": "18DADFF1", "E0": "18DAE0F1", "E1": "18DAE1F1", "E2": "18DAE2F1", "E3": "18DAE3F1", "E4": "74F", "E6": "622", "E7": "7E4", "E8": "644", "E9": "742", "EA": "79A", "EB": "638", "EC": "637", "ED": "714", "F7": "716", "F8": "717", "FA": "75B", "FD": "74F", "FE": "74C", "FF": "7D0" }
Code snippet from https://github.com/rbei-etas/busmaster
negrsp = {"10": "NR: General Reject", "11": "NR: Service Not Supported", "12": "NR: SubFunction Not Supported", "13": "NR: Incorrect Message Length Or Invalid Format", "21": "NR: Busy Repeat Request", "22": "NR: Conditions Not Correct Or Request Sequence Error", "23": "NR: Routine Not Complete", "24": "NR: Request Sequence Error", "31": "NR: Request Out Of Range", "33": "NR: Security Access Denied- Security Access Requested ", "35": "NR: Invalid Key", "36": "NR: Exceed Number Of Attempts", "37": "NR: Required Time Delay Not Expired", "40": "NR: Download not accepted", "41": "NR: Improper download type", "42": "NR: Can not download to specified address", "43": "NR: Can not download number of bytes requested", "50": "NR: Upload not accepted", "51": "NR: Improper upload type", "52": "NR: Can not upload from specified address", "53": "NR: Can not upload number of bytes requested", "70": "NR: Upload Download NotAccepted", "71": "NR: Transfer Data Suspended", "72": "NR: General Programming Failure", "73": "NR: Wrong Block Sequence Counter", "74": "NR: Illegal Address In Block Transfer", "75": "NR: Illegal Byte Count In Block Transfer", "76": "NR: Illegal Block Transfer Type", "77": "NR: Block Transfer Data Checksum Error", "78": "NR: Request Correctly Received-Response Pending", "79": "NR: Incorrect ByteCount During Block Transfer", "7E": "NR: SubFunction Not Supported In Active Session", "7F": "NR: Service Not Supported In Active Session", "80": "NR: Service Not Supported In Active Diagnostic Mode", "81": "NR: Rpm Too High", "82": "NR: Rpm Too Low", "83": "NR: Engine Is Running", "84": "NR: Engine Is Not Running", "85": "NR: Engine RunTime TooLow", "86": "NR: Temperature Too High", "87": "NR: Temperature Too Low", "88": "NR: Vehicle Speed Too High", "89": "NR: Vehicle Speed Too Low", "8A": "NR: Throttle/Pedal Too High", "8B": "NR: Throttle/Pedal Too Low", "8C": "NR: Transmission Range In Neutral", "8D": "NR: Transmission Range In Gear", "8F": "NR: Brake Switch(es)NotClosed (brake pedal not pressed or not applied)", "90": "NR: Shifter Lever Not In Park ", "91": "NR: Torque Converter Clutch Locked", "92": "NR: Voltage Too High", "93": "NR: Voltage Too Low" }
cmdb = ''' #v1.0 ;AC P; ATZ ; Z ; reset all #v1.0 ;AC P; ATE1 ; E0, E1 ; Echo off, or on* #v1.0 ;AC P; ATL0 ; L0, L1 ; Linefeeds off, or on #v1.0 ;AC ; ATI ; I ; print the version ID #v1.0 ;AC ; AT@1 ; @1 ; display the device description #v1.0 ;AC P; ATAL ; AL ; Allow Long (>7 byte) messages #v1.0 ;AC ; ATBD ; BD ; perform a Buffer Dump #V1.0 ;ACH ; ATSP4 ; SP h ; Set Protocol to h and save it #v1.0 ;AC ; ATBI ; BI ; Bypass the Initialization sequence #v1.0 ;AC P; ATCAF0 ; CAF0, CAF1 ; Automatic Formatting off, or on* #v1.0 ;AC ; ATCFC1 ; CFC0, CFC1 ; Flow Controls off, or on* #v1.0 ;AC ; ATCP 01 ; CP hh ; set CAN Priority to hh (29 bit) #v1.0 ;AC ; ATCS ; CS ; show the CAN Status counts #v1.0 ;AC ; ATCV 1250 ; CV dddd ; Calibrate the Voltage to dd.dd volts #v1.0 ;AC ; ATD ; D ; set all to Defaults #v1.0 ;AC ; ATDP ; DP ; Describe the current Protocol #v1.0 ;AC ; ATDPN ; DPN ; Describe the Protocol by Number #v1.0 ;AC P; ATH0 ; H0, H1 ; Headers off*, or on #v1.0 ;AC ; ATI ; I ; print the version ID #v1.0 ;AC P; ATIB 10 ; IB 10 ; set the ISO Baud rate to 10400* #v1.0 ;AC ; ATIB 96 ; IB 96 ; set the ISO Baud rate to 9600 #v1.0 ;AC ; ATL1 ; L0, L1 ; Linefeeds off, or on #v1.0 ;AC ; ATM0 ; M0, M1 ; Memory off, or on #v1.0 ; C ; ATMA ; MA ; Monitor All #v1.0 ; C ; ATMR 01 ; MR hh ; Monitor for Receiver = hh #v1.0 ; C ; ATMT 01 ; MT hh ; Monitor for Transmitter = hh #v1.0 ;AC ; ATNL ; NL ; Normal Length messages* #v1.0 ;AC ; ATPC ; PC ; Protocol Close #v1.0 ;AC ; ATR1 ; R0, R1 ; Responses off, or on* #v1.0 ;AC ; ATRV ; RV ; Read the input Voltage #v1.0 ;ACH ; ATSP7 ; SP h ; Set Protocol to h and save it #v1.0 ;ACH ; ATSH 00000000 ; SH wwxxyyzz ; Set Header to wwxxyyzz #v1.0 ;AC ; ATSH 001122 ; SH xxyyzz ; Set Header to xxyyzz #v1.0 ;AC P; ATSH 012 ; SH xyz ; Set Header to xyz #v1.0 ;AC ; ATSP A6 ; SP Ah ; Set Protocol to Auto, h and save it #v1.0 ;AC ; ATSP 6 ; SP h ; Set Protocol to h and save it #v1.0 ;AC ; ATCM 123 ; CM hhh ; set the ID Mask to hhh #v1.0 ;AC ; ATCM 12345678 ; CM hhhhhhhh ; set the ID Mask to hhhhhhhh #v1.0 ;AC ; ATCF 123 ; CF hhh ; set the ID Filter to hhh #v1.0 ;AC ; ATCF 12345678 ; CF hhhhhhhh ; set the ID Filter to hhhhhhhh #v1.0 ;AC P; ATST FF ; ST hh ; Set Timeout to hh x 4 msec #v1.0 ;AC P; ATSW 96 ; SW 00 ; Stop sending Wakeup messages #v1.0 ;AC P; ATSW 34 ; SW hh ; Set Wakeup interval to hh x 20 msec #v1.0 ;AC ; ATTP A6 ; TP Ah ; Try Protocol h with Auto search #v1.0 ;AC ; ATTP 6 ; TP h ; Try Protocol h #v1.0 ;AC P; ATWM 817AF13E ; WM [1 - 6 bytes] ; set the Wakeup Message #v1.0 ;AC P; ATWS ; WS ; Warm Start (quick software reset) #v1.1 ;AC P; ATFC SD 300000 ; FC SD [1 - 5 bytes]; FC, Set Data to [...] #v1.1 ;AC P; ATFC SH 012 ; FC SH hhh ; FC, Set the Header to hhh #v1.1 ;AC P; ATFC SH 00112233 ; FC SH hhhhhhhh ; Set the Header to hhhhhhhh #v1.1 ;AC P; ATFC SM 1 ; FC SM h ; Flow Control, Set the Mode to h #v1.1 ;AC ; ATPP FF OFF ; PP FF OFF ; all Prog Parameters disabled #v1.1 ;AC ; ATPP FF ON ; PP FF ON ; all Prog Parameters enabled #v1.1 ; ; ; PP xx OFF ; disable Prog Parameter xx #v1.1 ; ; ; PP xx ON ; enable Prog Parameter xx #v1.1 ; ; ; PP xx SV yy ; for PP xx, Set the Value to yy #v1.1 ;AC ; ATPPS ; PPS ; print a PP Summary #v1.2 ;AC ; ATAR ; AR ; Automatically Receive #v1.2 ;AC 0; ATAT1 ; AT0, 1, 2 ; Adaptive Timing off, auto1*, auto2 #v1.2 ; ; ; BRD hh ; try Baud Rate Divisor hh #v1.2 ; ; ; BRT hh ; set Baud Rate Timeout #v1.2 ;ACH ; ATSPA ; SP h ; Set Protocol to h and save it #v1.2 ; C ; ATDM1 ; DM1 ; monitor for DM1 messages #v1.2 ; C ; ATIFR H ; IFR H, S ; IFR value from Header* or Source #v1.2 ; C ; ATIFR0 ; IFR0, 1, 2 ; IFRs off, auto*, or on #v1.2 ;AC ; ATIIA 01 ; IIA hh ; set ISO (slow) Init Address to hh #v1.2 ;AC ; ATKW0 ; KW0, KW1 ; Key Word checking off, or on* #v1.2 ; C ; ATMP 0123 ; MP hhhh ; Monitor for PGN 0hhhh #v1.2 ; C ; ATMP 0123 4 ; MP hhhh n ; and get n messages #v1.2 ; C ; ATMP 012345 ; MP hhhhhh ; Monitor for PGN hhhhhh #v1.2 ; C ; ATMP 012345 6 ; MP hhhhhh n ; and get n messages #v1.2 ;AC ; ATSR 01 ; SR hh ; Set the Receive address to hh #v1.3 ; ; AT@2 ; @2 ; display the device identifier #v1.3 ;AC P; ATCRA 012 ; CRA hhh ; set CAN Receive Address to hhh #v1.3 ;AC ; ATCRA 01234567 ; CRA hhhhhhhh ; set the Rx Address to hhhhhhhh #v1.3 ;AC ; ATD0 ; D0, D1 ; display of the DLC off*, or on #v1.3 ;AC ; ATFE ; FE ; Forget Events #v1.3 ;AC ; ATJE ; JE ; use J1939 Elm data format* #v1.3 ;AC ; ATJS ; JS ; use J1939 SAE data format #v1.3 ;AC ; ATKW ; KW ; display the Key Words #v1.3 ;AC ; ATRA 01 ; RA hh ; set the Receive Address to hh #v1.3 ;ACH ; ATSP6 ; SP h ; Set Protocol to h and save it #v1.3 ;ACH ; ATRTR ; RTR ; send an RTR message #v1.3 ;AC ; ATS1 ; S0, S1 ; printing of aces off, or on* #v1.3 ;AC ; ATSP 00 ; SP 00 ; Erase stored protocol #v1.3 ;AC ; ATV0 ; V0, V1 ; use of Variable DLC off*, or on #v1.4 ;AC ; ATCEA ; CEA ; turn off CAN Extended Addressing #v1.4 ;AC ; ATCEA 01 ; CEA hh ; use CAN Extended Address hh #v1.4 ;AC ; ATCV 0000 ; CV 0000 ; restore CV value to factory setting #v1.4 ;AC ; ATIB 48 ; IB 48 ; set the ISO Baud rate to 4800 #v1.4 ;AC ; ATIGN ; IGN ; read the IgnMon input level #v1.4 ; ; ; LP ; go to Low Power mode #v1.4 ;AC ; ATPB 01 23 ; PB xx yy ; Protocol B options and baud rate #v1.4 ;AC ; ATRD ; RD ; Read the stored Data #v1.4 ;AC ; ATSD 01 ; SD hh ; Save Data byte hh #v1.4 ;ACH ; ATSP4 ; SP h ; Set Protocol to h and save it #v1.4 ;AC P; ATSI ; SI ; perform a Slow (5 baud) Initiation #v1.4 ;ACH ; ATZ ; Z ; reset all #v1.4 ;ACH ; ATSP5 ; SP h ; Set Protocol to h and save it #v1.4 ;AC P; ATFI ; FI ; perform a Fast Initiation #v1.4 ;ACH ; ATZ ; Z ; reset all #v1.4 ;AC ; ATSS ; SS ; use Standard Search order (J1978) #v1.4 ;AC ; ATTA 12 ; TA hh ; set Tester Address to hh #v1.4 ;ACH ; ATSPA ; SP h ; Set Protocol to h and save it #v1.4 ;AC ; ATCSM1 ; CSM0, CSM1 ; Silent Monitoring off, or on* #v1.4 ;AC ; ATJHF1 ; JHF0, JHF1 ; Header Formatting off, or on* #v1.4 ;AC ; ATJTM1 ; JTM1 ; set Timer Multiplier to 1* #v1.4 ;AC ; ATJTM5 ; JTM5 ; set Timer Multiplier to 5 #v1.4b;AC ; ATCRA ; CRA ; reset the Receive Address filters #v2.0 ;AC ; ATAMC ; AMC ; display Activity Monitor Count #v2.0 ;AC ; ATAMT 20 ; AMT hh ; set the Activity Mon Timeout to hh #v2.1 ;AC ; ATCTM1 ; CTM1 ; set Timer Multiplier to 1* #v2.1 ;AC ; ATCTM5 ; CTM5 ; set Timer Multiplier to 5 #v2.1 ;ACH ; ATZ ; Z ; reset all '''
def get_can_addr(txa): for d in dnat.keys(): if dnat[d].upper() == txa.upper(): return d return None
def getcandnat(addr): a = str(addr).upper() if a in dnat: return dnat[a] return "??"
def getcansnat(addr): a = str(addr).upper() if a in snat: return snat[a] return "??"
def item_count(iter): return sum(1 for _ in iter)
def get_available_ports(): ports = [] portlist = list_ports.comports()
if item_count(portlist) == 0:
return
iterator = sorted(list(portlist))
for port, desc, hwid in iterator:
ports.append((port, desc))
return ports
def reconnect_elm(): ports = get_available_ports() current_adapter = "STD" if options.elm: current_adapter = options.elm.adapter_type for port, desc in ports: if desc == options.port_name: options.elm = ELM(port, options.port_speed, current_adapter) return True return False
def errorval(val): if val not in negrsp: return "not registered error" if val in negrsp.keys(): return negrsp[val]
class Port: '''This is a serial port or a TCP-connection if portName looks like a 192.168.0.10:35000 then it is wifi and we should open tcp connection else try to open serial port ''' connectionStatus = False portType = 0 # 0-serial 1-tcp ipaddr = '192.168.0.10' tcpprt = 35000 portName = "" portTimeout = 5 # don't change it here. Change in ELM class
droid = None
btcid = None
hdr = None
tcp_needs_reconnect = False
def __init__(self, portName, speed, portTimeout):
options.elm_failed = False
self.portTimeout = portTimeout
portName = portName.strip()
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}$", portName):
import socket
self.ipaddr, self.tcpprt = portName.split(':')
self.tcpprt = int(self.tcpprt)
self.portType = 1
self.init_wifi()
else:
self.portName = portName
self.portType = 0
try:
self.hdr = serial.Serial(self.portName, baudrate=speed, timeout=portTimeout,
parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE)
print(self.hdr)
self.connectionStatus = True
return
except Exception as e:
print(_("Error:") + str(e))
print(_("ELM not connected or wrong COM port"), portName)
options.last_error = _("Error:") + str(e)
options.elm_failed = True
def close(self):
try:
self.hdr.close()
print("Port closed")
except:
pass
def init_wifi(self, reinit=False):
'''
Needed for wifi adapters with short connection timeout
'''
if self.portType != 1:
return
import socket
if reinit:
self.hdr.close()
self.hdr = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.hdr.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
self.hdr.connect((self.ipaddr, self.tcpprt))
self.hdr.setblocking(True)
self.connectionStatus = True
except:
options.elm_failed = True
def read(self):
try:
byte = b""
if self.portType == 1:
import socket
try:
byte = self.hdr.recv(1)
print(str(byte))
except socket.timeout:
self.tcp_needs_reconnect = True
except Exception as e:
print(e)
elif self.portType == 2:
if self.droid.bluetoothReadReady():
byte = self.droid.bluetoothRead(1).result
else:
if self.hdr.inWaiting():
byte = self.hdr.read()
except:
print('*' * 40)
print('* ' + _('Connection to ELM was lost'))
self.connectionStatus = False
self.close()
return None
try:
return byte.decode("utf-8")
except:
print("Cannot decode bytes " + str(byte))
return ""
def change_rate(self, rate):
print("Serial port switch to " + str(rate))
self.hdr.baudrate = rate
def write(self, data):
try:
if self.portType == 1:
if self.tcp_needs_reconnect:
self.tcp_needs_reconnect = False
self.init_wifi(True)
return self.hdr.sendall(data)
elif self.portType == 2:
return self.droid.bluetoothWrite(data)
else:
return self.hdr.write(data)
except:
print('*' * 40)
print('* ' + _('Connection to ELM was lost'))
self.connectionStatus = False
self.close()
def expect(self, pattern, time_out=1):
tb = time.time() # start time
self.buff = ""
while True:
if not options.simulation_mode:
byte = self.read()
else:
byte = '>'
if byte == '\r':
byte = '\n'
if byte:
self.buff += byte
tc = time.time()
if pattern in self.buff:
return self.buff
if (tc - tb) > time_out:
return self.buff + _("TIMEOUT")
self.close()
self.connectionStatus = False
return ''
def check_elm(self):
self.hdr.timeout = 2
for s in [38400, 115200, 230400, 57600, 9600, 500000]:
print("\r\t\t\t\t\r" + _("Checking port speed:"), s,)
sys.stdout.flush()
self.hdr.baudrate = s
self.hdr.flushInput()
self.write("\r")
# search > string
tb = time.time() # start time
self.buff = ""
while (True):
if not options.simulation_mode:
byte = self.read()
else:
byte = '>'
self.buff += byte
tc = time.time()
if '>' in self.buff:
options.port_speed = s
print("\n" + _("Start COM speed :"), s)
self.hdr.timeout = self.portTimeout
return True
if (tc - tb) > 1:
break
print("\n" + _("ELM not responding"))
return False
def soft_baudrate(self, baudrate):
if options.simulation_mode:
return
if self.portType == 1: # wifi is not supported
print(_("ERROR - wifi do not support changing baud rate"))
return
print(_("Changing baud rate to:"), baudrate,)
if baudrate == 38400:
self.write("at brd 68\r")
elif baudrate == 57600:
self.write("at brd 45\r")
elif baudrate == 115200:
self.write("at brd 23\r")
elif baudrate == 230400:
self.write("at brd 11\r")
elif baudrate == 500000:
self.write("at brd 8\r")
# search OK
tb = time.time() # start time
self.buff = ""
while (True):
if not options.simulation_mode:
byte = self.read()
else:
byte = 'OK'
if byte == '\r' or byte == '\n':
self.buff = ""
continue
self.buff += byte
tc = time.time()
if 'OK' in self.buff:
break
if (tc - tb) > 1:
print(_("ERROR - command not supported"))
sys.exit()
self.hdr.timeout = 1
if baudrate == 38400:
self.hdr.baudrate = 38400
elif baudrate == 57600:
self.hdr.baudrate = 57600
elif baudrate == 115200:
self.hdr.baudrate = 115200
elif baudrate == 230400:
self.hdr.baudrate = 230400
elif baudrate == 500000:
self.hdr.baudrate = 500000
# search ELM
tb = time.time() # start time
self.buff = ""
while True:
if not options.simulation_mode:
byte = self.read()
else:
byte = 'ELM'
if byte == '\r' or byte == '\n':
self.buff = ""
continue
self.buff += byte
tc = time.time()
if 'ELM' in self.buff:
break
if (tc - tb) > 1:
print(_("ERROR - rate not supported. Let's go back."))
self.hdr.timeout = self.portTimeout
self.hdr.baudrate = options.port_speed
return
self.write("\r")
# search >
tb = time.time() # start time
self.buff = ""
while (True):
if not options.simulation_mode:
byte = self.read()
else:
byte = '>'
if byte == '\r' or byte == '\n':
self.buff = ""
continue
self.buff += byte
tc = time.time()
if '>' in self.buff:
break
if (tc - tb) > 1:
print(_("ERROR - something went wrong. Let's get back."))
self.hdr.timeout = self.portTimeout
self.hdr.baudrate = options.port_speed
return
print("OK")
return
class ELM: '''ELM327 class'''
port = 0
lf = 0
vf = 0
keepAlive = 4 # send startSession to CAN after silence if startSession defined
busLoad = 0 # I am sure than it should be zero
srvsDelay = 0 # the delay next command requested by service
lastCMDtime = 0 # time when last command was sent to bus
portTimeout = 5 # timeout of port (com or tcp)
elmTimeout = 0 # timeout set by ATST
# error counters
error_frame = 0
error_bufferfull = 0
error_question = 0
error_nodata = 0
error_timeout = 0
error_rx = 0
error_can = 0
canline = 0
response_time = 0
buff = ""
currentprotocol = ""
currentsubprotocol = ""
currentaddress = ""
startSession = ""
lastinitrsp = ""
rsp_cache = {}
l1_cache = {}
ATR1 = True
ATCFC0 = False
portName = ""
lastMessage = ""
monitorstop = False
connectionStatus = False
def __init__(self, portName, rate, adapter_type="STD"):
for speed in [int(rate), 38400, 115200, 230400, 57600, 9600, 500000, 1000000, 2000000]:
print(_("Trying to open port") + "%s @ %i" % (portName, speed))
self.sim_mode = options.simulation_mode
self.portName = portName
self.adapter_type = adapter_type
if not options.simulation_mode:
self.port = Port(portName, speed, self.portTimeout)
if options.elm_failed:
self.connectionStatus = False
return
if not os.path.exists("./logs"):
os.mkdir("./logs")
if len(options.log) > 0:
self.lf = open("./logs/elm_" + options.log, "at")
self.vf = open("./logs/ecu_" + options.log, "at")
self.lastCMDtime = 0
self.ATCFC0 = options.opt_cfc0
# Purge unread data
self.port.expect(">")
res = self.send_raw("ATZ")
if not 'ELM' in res:
options.elm_failed = True
options.last_error = _("No ELM interface on port") + " %s" % portName
else:
options.last_error = ""
options.elm_failed = False
self.connectionStatus = True
rate = speed
break
if adapter_type == "OBDLINK" and not options.elm_failed and rate != 2000000:
print("OBDLink Connection OK, attempting full speed UART switch")
res = self.send_raw("ST SBR 2000000")
if "OK" in res:
print("OBDLINK switched to 2Mbs, changing UART speed now...")
self.port.change_rate(2000000)
time.sleep(1)
res = self.send_raw("STI")
if "STN" in res:
print("OBDLink full speed connection OK")
print("OBDLink Version " + res)
else:
print("OBDLink full speed switch failed")
options.elm_failed = True
self.connectionStatus = False
else:
print("Failed to switch to change OBDLink to 2Mbs, using " + str(speed))
def __del__(self):
try:
print("ELM reset...")
self.send_raw("ATZ\r")
except:
pass
def connectionStat(self):
return self.port.connectionStatus
def clear_cache(self):
''' Clear L2 cache before screen update
'''
self.rsp_cache = {}
def request(self, req, positive='', cache=True, serviceDelay="0"):
''' Check if request is saved in L2 cache.
If not then
- make real request
- convert responce to one line
- save in L2 cache
returns response without consistency check
'''
if cache and req in self.rsp_cache.keys():
return self.rsp_cache[req]
# send cmd
rsp = self.cmd(req, serviceDelay)
if 'WRONG' in rsp:
return rsp
res = ""
if self.currentprotocol != "can":
# Trivially reject first line (echo)
rsp_split = rsp.split('\n')[1:]
for s in rsp_split:
if '>' not in s:
res += s.strip() + ' '
else:
# parse response
for s in rsp.split('\n'):
if ':' in s:
res += s[2:].strip() + ' '
else: # response consists only of one frame
if s.replace(' ', '').startswith(positive.replace(' ', '')):
res += s.strip() + ' '
rsp = res
# populate L2 cache
self.rsp_cache[req] = rsp
# save log
if self.vf != 0:
tmstr = datetime.now().strftime("%H:%M:%S.%f")[:-3]
if self.currentaddress in dnat:
self.vf.write(tmstr + ";" + dnat[self.currentaddress] + ";" + req + ";" + rsp + "\n")
else:
print("Unknown address ", self.currentaddress, req, rsp)
self.vf.flush()
return rsp
def errorval(self, val):
if val not in negrsp:
return "not registered error"
if val in negrsp.keys():
return negrsp[val]
def cmd(self, command, serviceDelay="0"):
tb = time.time() # start time
# Ensure time gap between commands
dl = self.busLoad + self.srvsDelay - tb + self.lastCMDtime
if ((tb - self.lastCMDtime) < (self.busLoad + self.srvsDelay)) \
and ("AT" not in command.upper() or "ST" not in command.upper()):
time.sleep(self.busLoad + self.srvsDelay - tb + self.lastCMDtime)
tb = time.time() # renew start time
# If we use wifi and there was more than keepAlive seconds of silence then reinit tcp
if (tb - self.lastCMDtime) > self.keepAlive:
self.port.init_wifi(True)
# If we are on CAN and there was more than keepAlive seconds of silence and
# start_session_can was executed then send startSession command again
# if ((tb-self.lastCMDtime)>self.keepAlive and self.currentprotocol=="can"
if ((tb - self.lastCMDtime) > self.keepAlive
and self.currentprotocol == "can"
and len(self.startSession) > 0):
# log KeepAlive event
if self.lf != 0:
tmstr = datetime.now().strftime("%H:%M:%S.%f")[:-3]
self.lf.write("#[" + tmstr + "]" + "KeepAlive\n")
self.lf.flush()
# send keepalive
self.send_cmd(self.startSession)
self.lastCMDtime = time.time() # for not to get into infinite loop
# send command
cmdrsp = self.send_cmd(command)
self.lastCMDtime = tc = time.time()
# add srvsDelay to time gap before send next command
self.srvsDelay = float(serviceDelay) / 1000.
# check for negative response
for l in cmdrsp.split('\n'):
l = l.strip().upper()
if l.startswith("7F") and len(l) == 8 and l[6:8] in negrsp.keys():
print(l, negrsp[l[6:8]])
if self.lf != 0:
self.lf.write("#[" + str(tc - tb) + "] rsp:" + l + ":" + negrsp[l[6:8]] + "\n")
self.lf.flush()
return cmdrsp
def set_can_timeout(self, value):
val = value / 4
if val > 255:
val = 255
val = hex(val)[2:].upper()
self.cmd("AT ST %s" % val)
def send_cmd(self, command):
if "AT" in command.upper() or "ST" in command.upper() or self.currentprotocol != "can":
return self.send_raw(command)
if self.ATCFC0:
return self.send_can_cfc0(command)
else:
rsp = self.send_can(command)
# Disabled this because it's now possible to control it via UI
# if self.error_frame > 0 and not self.isels: #then fallback to cfc0
# self.ATCFC0 = True
# self.cmd("at cfc0")
# rsp = self.send_can_cfc0(command)
return rsp
def send_can(self, command):
command = command.strip().replace(' ', '')
if len(command) % 2 != 0 or len(command) == 0:
return "ODD ERROR"
if not all(c in string.hexdigits for c in command):
return "HEX ERROR"
# do framing
raw_command = []
cmd_len = int(len(command) / 2)
if cmd_len < 8: # single frame
# check L1 cache here
if command in self.l1_cache.keys():
raw_command.append(("%0.2X" % cmd_len) + command + self.l1_cache[command])
else:
raw_command.append(("%0.2X" % cmd_len) + command)
else:
# first frame
raw_command.append("1" + ("%0.3X" % cmd_len)[-3:] + command[:12])
command = command[12:]
# consecutive frames
frame_number = 1
while (len(command)):
raw_command.append("2" + ("%X" % frame_number)[-1:] + command[:14])
frame_number = frame_number + 1
command = command[14:]
responses = []
# send farmes
for f in raw_command:
# send next frame
frsp = self.send_raw(f)
# analyse response (1 phase)
for s in frsp.split('\n'):
if s.strip() == f: # echo cancelation
continue
s = s.strip().replace(' ', '')
if len(s) == 0: # empty string
continue
if all(c in string.hexdigits for c in s): # some data
if s[:1] == '3': # flow control, just ignore it in this version
continue
responses.append(s)
# analyse response (2 phases)
result = ""
noerrors = True
cframe = 0 # frame counter
nbytes = 0 # number bytes in response
nframes = 0
if len(responses) == 0: # no data in response
return ""
if len(responses) > 1 and responses[0].startswith('037F') and responses[0][6:8] == '78':
responses = responses[1:]
if len(responses) == 1: # single frame response
if responses[0][:1] == '0':
nbytes = int(responses[0][1:2], 16)
nframes = 1
result = responses[0][2:2 + nbytes * 2]
else: # wrong response (not all frames received)
self.error_frame += 1
noerrors = False
else: # multi frame response
if responses[0][:1] == '1': # first frame
nbytes = int(responses[0][1:4], 16)
nframes = nbytes / 7 + 1
cframe = 1
result = responses[0][4:16]
else: # wrong response (first frame omitted)
self.error_frame += 1
noerrors = False
for fr in responses[1:]:
if fr[:1] == '2': # consecutive frames
tmp_fn = int(fr[1:2], 16)
if tmp_fn != (cframe % 16): # wrong response (frame lost)
self.error_frame += 1
noerrors = False
continue
cframe += 1
result += fr[2:16]
else: # wrong response
self.error_frame += 1
noerrors = False
errorstr = "Unknown"
# check for negative response (repeat the same as in cmd())
if result[:2] == '7F':
noerrors = False
if result[4:6] in negrsp.keys():
errorstr = negrsp[result[4:6]]
if self.vf != 0:
tmstr = datetime.now().strftime("%H:%M:%S.%f")[:-3]
self.vf.write(
tmstr + ";" + dnat[self.currentaddress] + ";" + command + ";" + result + ";" + errorstr + "\n")
self.vf.flush()
# populate L1 cache
if noerrors and nframes < 16 and command[:1] == '2' and not options.opt_n1c:
self.l1_cache[command] = str(nframes)
if len(result) / 2 >= nbytes and noerrors:
# Remove unnecessay bytes
result = result[0:nbytes*2]
# split by bytes and return
result = ' '.join(a + b for a, b in zip(result[::2], result[1::2]))
return result
else:
return "WRONG RESPONSE : " + errorstr + "(" + result + ")"
def send_can_cfc0(self, command):
command = command.strip().replace(' ', '')
if len(command) % 2 != 0 or len(command) == 0: return "ODD ERROR"
if not all(c in string.hexdigits for c in command): return "HEX ERROR"
# do framing
raw_command = []
cmd_len = len(command) / 2
if cmd_len < 8: # single frame
raw_command.append(("%0.2X" % cmd_len) + command)
else:
# first frame
raw_command.append("1" + ("%0.3X" % cmd_len)[-3:] + command[:12])
command = command[12:]
# consecutive frames
frame_number = 1
while len(command):
raw_command.append("2" + ("%X" % frame_number)[-1:] + command[:14])
frame_number += 1
command = command[14:]
responses = []
# send frames
BS = 1 # Burst Size
ST = 0 # Frame Interval
Fc = 0 # Current frame
Fn = len(raw_command) # Number of frames
if Fn > 1 or len(raw_command[0])>15: # set elm timeout to 300ms for first response
self.send_raw('ATST4B')
while Fc < Fn:
# enable responses
if not self.ATR1:
frsp = self.send_raw('at r1')
self.ATR1 = True
tb = time.time() # time of sending (ff)
if Fn > 1 and Fc == (Fn-1): # set elm timeout to maximum for last response on long command
self.send_raw('ATSTFF')
self.send_raw('ATAT1')
if (Fc == 0 or Fc == (Fn-1)) and len(raw_command[Fc])<16: #first or last frame in command and len<16 (bug in ELM)
frsp = self.send_raw (raw_command[Fc] + '1') # we'll get only 1 frame: nr, fc, ff or sf
else:
frsp = self.send_raw (raw_command[Fc])
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
continue
s = s.strip().replace(' ', '')
if len(s) == 0: # empty string
continue
if all(c in string.hexdigits for c in s): # some data
if s[:1] == '3': # FlowControl
# extract Burst Size
BS = s[2:4]
BS = int(BS, 16)
# extract Frame Interval
ST = s[4:6]
if ST[:1].upper() == 'F':
ST = int(ST[1:2], 16) * 100
else:
ST = int(ST, 16)
print('BS:', BS, 'ST:', ST)
break # go to sending consequent frames
else:
responses.append(s)
continue
# sending consequent farmes 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
while cf > 0:
cf -= 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))
tb = tc
frsp = self.send_raw(raw_command[Fc])
Fc += 1
# now we are going to receive data. st or ff should be in responses[0]
if len(responses) != 1:
return "WRONG RESPONSE MULTILINE CFC0"
result = ""
noerrors = True
nbytes = 0 # number bytes in response
if responses[0][:1] == '0': # single frame (sf)
nbytes = int(responses[0][1:2], 16)
result = responses[0][2:2 + nbytes * 2]
elif responses[0][:1] == '1': # first frame (ff)
nbytes = int(responses[0][1:4], 16)
nframes = nbytes / 7 + 1
cframe = 1
result = responses[0][4:16]
# receiving consecutive frames
while len(responses) < nframes:
# now we should send ff
sBS = hex(min(nframes - len(responses), 0xf))[2:]
frsp = self.send_raw('300' + sBS + '00' + sBS)
# analyse response
for s in frsp.split('\n'):
if s.strip()[:len(raw_command[Fc - 1])] == raw_command[Fc - 1]: # echo cancelation
continue
s = s.strip().replace(' ', '')
if len(s) == 0: # empty string
continue
if all(c in string.hexdigits for c in s): # some data
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
continue
cframe += 1
result += s[2:16]
continue
else: # wrong response (first frame omitted)
self.error_frame += 1
noerrors = False
errorstr = "Unknown"
# check for negative response (repeat the same as in cmd())
if result[:2] == '7F':
if result[6:8] in negrsp.keys():
errorstr = negrsp[result[4:6]]
noerrors = False
if len(result) / 2 >= nbytes and noerrors:
result = result[0:nbytes*2]
# split by bytes and return
result = ' '.join(a + b for a, b in zip(result[::2], result[1::2]))
return result
else:
return "WRONG RESPONSE CFC0 " + errorstr
def send_raw(self, command):
tb = time.time() # start time
# save command to log
if self.lf != 0:
# tm = str(time.time())
tmstr = datetime.now().strftime("%H:%M:%S.%f")[:-3]
self.lf.write(">[" + tmstr + "]" + command + "\n")
self.lf.flush()
# send command
if not options.simulation_mode:
self.port.write(str(command + "\r").encode("utf-8")) # send command
# receive and parse response
while True:
tc = time.time()
if options.simulation_mode:
break
self.buff = self.port.expect('>', self.portTimeout)
if not self.port.connectionStatus:
break
return ''
tc = time.time()
if (tc - tb) > self.portTimeout and "TIMEOUT" not in self.buff:
self.buff += " TIMEOUT"
if "TIMEOUT" in self.buff:
self.error_timeout += 1
break
if command in self.buff:
break
elif self.lf != 0:
tmstr = datetime.now().strftime("%H:%M:%S.%f")[:-3]
self.lf.write("<[" + tmstr + "]" + self.buff + "<shifted>" + command + "\n")
self.lf.flush()
# count errors
if "?" in self.buff:
self.error_question += 1
if "BUFFER FULL" in self.buff:
self.error_bufferfull += 1
if "NO DATA" in self.buff:
self.error_nodata += 1
if "RX ERROR" in self.buff:
self.error_rx += 1
if "CAN ERROR" in self.buff:
self.error_can += 1
self.response_time = ((self.response_time * 9) + (tc - tb)) / 10
# save response to log
if self.lf != 0:
self.lf.write("<[" + str(round(tc - tb, 3)) + "]" + self.buff + "\n")
self.lf.flush()
return self.buff
def close_protocol(self):
self.cmd("atpc")
def start_session_can(self, start_session):
self.startSession = start_session
retcode = self.cmd(self.startSession)
if retcode.startswith('50'):
return True
return False
def init_can_sniffer(self, filter_addr, br):
if options.simulation_mode:
return
self.cmd('AT WS')
self.cmd("AT E1")
self.cmd("AT L0")
self.cmd("AT H0")
self.cmd("AT D0")
if br == 250000:
self.cmd("AT SP 8")
else:
self.cmd("AT SP 6")
self.cmd("AT S0")
self.cmd("AT AL")
self.cmd("AT CAF0")
if filter_addr:
self.cmd("AT CRA " + filter_addr[-3:])
def monitor_can_bus(self, callback):
if options.simulation_mode:
pass
else:
self.port.write("AT MA\r")
stream = ""
while not self.monitorstop:
byte = self.port.read()
if byte == '\r' or byte == '<' or byte == '\n':
if stream == "AT MA" or stream == "DATA ERROR":
# Prefiltering echo and error reports (if any)
stream = ""
continue
# Ok to handle stream
callback(stream)
stream = ""
elif byte == ">":
break
if byte:
stream += byte
self.port.write("AT\r")
self.port.expect('>')
def init_can(self):
self.currentprotocol = "can"
self.currentaddress = ""
self.startSession = ""
self.lastCMDtime = 0
self.l1_cache = {}
if self.lf != 0:
tmstr = datetime.now().strftime("%x %H:%M:%S.%f")[:-3]
self.lf.write('#' * 60 + "\n#[" + tmstr + "] Init CAN\n" + '#' * 60 + "\n")
self.lf.flush()
self.cmd("AT WS")
self.cmd("AT E1")
self.cmd("AT S0")
self.cmd("AT H0")
self.cmd("AT L0")
self.cmd("AT AL")
self.cmd("AT CAF0")
if self.ATCFC0:
self.cmd("AT CFC0")
self.lastCMDtime = 0
def get_can_addr(self, txa):
for d in dnat.keys():
if dnat[d] == txa:
return d
return None
def set_can_addr(self, addr, ecu, canline=0):
if self.currentprotocol == "can" and self.currentaddress == addr and self.canline == canline:
return
if self.lf != 0:
self.lf.write('#' * 60 + "\n#connect to: " + ecu['ecuname'] + " Addr:" + addr + "\n" + '#' * 60 + "\n")
self.lf.flush()
self.currentprotocol = "can"
self.currentaddress = addr
self.startSession = ""
self.lastCMDtime = 0
self.l1_cache = {}
self.canline = canline
if 'idTx' in ecu and 'idRx' in ecu:
TXa = ecu['idTx']
RXa = ecu['idRx']
self.currentaddress = get_can_addr(TXa)
else:
TXa = dnat[addr]
RXa = snat[addr]
extended_can = False
if len(RXa) == 8:
# Extended (29bits) addressing
extended_can = True
if extended_can:
self.cmd("AT CP " + TXa[:2])
self.cmd("AT SH " + TXa[2:])
else:
self.cmd("AT SH " + TXa)
self.cmd("AT CRA " + RXa)
self.cmd("AT FC SH " + TXa)
self.cmd("AT FC SD 30 00 00") # status BS STmin
self.cmd("AT FC SM 1")
if canline == 0:
# TODO: Find a better way to detect baud rate
if 0 and 'brp' in ecu.keys() and ecu['brp'] == "1": # I suppose that brp=1 means 250kBps CAN
if extended_can:
self.cmd("AT SP 9")
else:
self.cmd("AT SP 8")
else:
if extended_can:
self.cmd("AT SP 7")
else:
self.cmd("AT SP 6")
elif canline == 1:
if extended_can:
self.cmd("AT SP 7")
else:
self.cmd("AT SP 6")
elif canline == 2:
if extended_can:
self.cmd("AT SP 9")
else:
self.cmd("AT SP 8")
else:
self.cmd("STP 53")
if canline == 3:
self.cmd("STPBR 500000")
elif canline == 4:
self.cmd("STPBR 250000")
elif canline == 5:
self.cmd("STPBR 125000")
if options.cantimeout > 0:
self.set_can_timeout(options.cantimeout)
return TXa, RXa
def start_session_iso(self, start_session):
self.startSession = start_session
if len(self.startSession) > 0:
self.lastinitrsp = self.cmd(self.startSession)
if self.lastinitrsp.startswith('50'):
return True
return False
def init_iso(self):
self.currentprotocol = "iso"
self.currentsubprotocol = ""
self.currentaddress = ""
self.startSession = ""
self.lastCMDtime = 0
self.lastinitrsp = ""
if self.lf != 0:
tmstr = datetime.now().strftime("%x %H:%M:%S.%f")[:-3]
self.lf.write('#' * 60 + "\n#[" + tmstr + "] Init ISO\n" + '#' * 60 + "\n")
self.lf.flush()
self.cmd("AT WS")
self.cmd("AT E1")
self.cmd("AT L0")
self.cmd("AT D1")
self.cmd("AT H0") # headers off
self.cmd("AT AL") # Allow Long (>7 byte) messages
self.cmd("AT KW0")
def set_iso8_addr(self, addr, ecu):
if self.currentprotocol == "iso" and self.currentaddress == addr and self.currentsubprotocol == ecu['protocol']:
return
if self.lf != 0:
self.lf.write('#' * 60 + "\n#connect to: " + ecu['ecuname'] + " Addr:" + addr + " Protocol:" + ecu[
'protocol'] + "\n" + '#' * 60 + "\n")
self.lf.flush()
self.currentprotocol = "iso"
self.currentsubprotocol = ecu['protocol']
self.currentaddress = addr
self.startSession = ""
self.lastCMDtime = 0
self.lastinitrsp = ""
self.cmd("AT SH 81 " + addr + " F1") # set address
self.cmd("AT SW 96") # wakeup message period 3 seconds
self.cmd("AT WM 81 " + addr + " F1 3E") # set wakeup message
self.cmd("AT IB10") # baud rate 10400
self.cmd("AT ST FF") # set timeout to 1 second
self.cmd("AT SP 3")
self.cmd("AT AT 0") # enable adaptive timing
self.cmd("AT SI") # ISO8 needs slow init
self.cmd("AT AT 1")
def set_iso_addr(self, addr, ecu):
if self.currentprotocol == "iso" and self.currentaddress == addr and self.currentsubprotocol == ecu['protocol']:
return
if self.lf != 0:
self.lf.write('#' * 60 + "\n#connect to: " + ecu['ecuname'] + " Addr:" + addr + " Protocol:" + ecu[
'protocol'] + "\n" + '#' * 60 + "\n")
self.lf.flush()
self.currentprotocol = "iso"
self.currentsubprotocol = ecu['protocol']
self.currentaddress = addr
self.startSession = ""
self.lastCMDtime = 0
self.lastinitrsp = ""
self.cmd("AT SH 81 " + addr + " F1") # set address
self.cmd("AT SW 96") # wakeup message period 3 seconds
self.cmd("AT WM 81 " + addr + " F1 3E") # set wakeup message
self.cmd("AT IB10") # baud rate 10400
self.cmd("AT ST FF") # set timeout to 1 second
self.cmd("AT AT 0") # disable adaptive timing
if options.opt_si:
self.cmd("AT SP 4") # slow init mode 4
self.cmd("AT IIA " + addr) # address for slow init
rsp = self.lastinitrsp = self.cmd("AT SI") # for slow init mode 4
if 'OK' not in self.lastinitrsp:
self.cmd("AT SP 5") # fast init mode 5
self.lastinitrsp = self.cmd("AT FI") # perform fast init mode 5
if 'OK' not in self.lastinitrsp:
return False
self.cmd("AT AT 1") # enable adaptive timing
return True
def elm_checker(port, speed, logview, app): good = 0 total = 0 pycom = 0 vers = ''
elm = ELM(port, speed)
if options.elm_failed:
return False
elm.portTimeout = 5
for st in cmdb.split('#'):
cm = st.split(';')
if len(cm) > 1:
if 'C' not in cm[1].upper():
continue
if len(cm[2].strip()):
res = elm.send_raw(cm[2])
if 'H' in cm[1].upper():
continue
total += 1
print(cm[2] + " " + res.strip())
if '?' in res:
chre = '<font color=red>[' + _('FAIL') + ']</font>'
if 'P' in cm[1].upper():
pycom += 1
# Timeout is not an error
elif 'TIMEOUT' in res:
chre = '<font color=green>[' + _('OK/TIMEOUT') + ']</font>'
good += 1
vers = cm[0]
else:
chre = '<font color=green>[' + _('OK') + ']</font>'
good += 1
vers = cm[0]
logview.append("%5s %10s %s" % (cm[0], cm[2], chre))
app.processEvents()
if pycom > 0:
logview.append('<font color=red>' + _('Incompatible adapter on ARM core') + '</font> \n')
logview.append(_('Result: ') + str(good) + _(' succeeded from ') + str(total) + '\n' + _('ELM Max version:') + vers + '\n')
return True
what is this about?
the devices that are taken into account are in readme.... I have a WIFI but it has the same standard
My elm.py file looks totally different :
Your file belongs to the python 2 branch. davidea72's fix concerns one of several problems of the python 3 version.
I also don't understand why the python 2 branch was not continued in parallel.