class HondaECU(object): def __init__(self, device_id=None): super(HondaECU, self).__init__() self.device_id = device_id self.dev = None self.error = 0 self.resets = 0 self.reset() def reset(self): if self.dev != None: del self.dev self.dev = None self.dev = Device(self.device_id) def setup(self): self.dev.ftdi_fn.ftdi_usb_reset() self.dev.ftdi_fn.ftdi_usb_purge_buffers() self.dev.ftdi_fn.ftdi_set_line_property(8, 1, 0) self.dev.baudrate = 10400 def _break(self, ms, debug=False): self.dev.ftdi_fn.ftdi_set_bitmode(1, 0x01) self.dev._write(b'\x00') time.sleep(ms) self.dev._write(b'\x01') self.dev.ftdi_fn.ftdi_set_bitmode(0, 0x00) self.dev.flush() def init(self, debug=False): ret = False self._break(.070) time.sleep(.130) self.dev.flush() info = self.send_command([0xfe],[0x72], debug=debug, retries=0) # 0xfe <- KWP2000 fast init all nodes ? if info != None and ord(info[0]) > 0: if ord(info[2]) == 0x72: ret = True return ret def kline(self): b = create_string_buffer(2) self.dev.ftdi_fn.ftdi_poll_modem_status(b) return b.raw[1] & 16 == 0 def send(self, buf, ml, timeout=.5): self.dev.flush() msg = "".join([chr(b) for b in buf]).encode("latin1") self.dev._write(msg) r = len(msg) to = time.time() while r > 0: r -= len(self.dev._read(r)) if time.time() - to > timeout: return None buf = bytearray() r = ml+1 while r > 0: tmp = self.dev._read(r) r -= len(tmp) buf.extend(tmp) if time.time() - to > timeout: return None r = buf[-1]-ml-1 while r > 0: tmp = self.dev._read(r) r -= len(tmp) buf.extend(tmp) if time.time() - to > timeout: return None return buf def send_command(self, mtype, data=[], retries=10, debug=False): msg, ml, dl = format_message(mtype, data) first = True while first or retries > 0: first = False if debug: sys.stderr.write("> [%s]" % ", ".join(["%02x" % m for m in msg])) resp = self.send(msg, ml) ret = None if resp == None: if debug: sys.stderr.write(" !%d \n" % (retries)) retries -= 1 time.sleep(0) continue else: if debug: sys.stderr.write("\n") if debug: sys.stderr.write("< [%s]" % ", ".join(["%02x" % r for r in resp])) invalid = (resp[-1] != checksum8bitHonda([r for r in resp[:-1]])) if invalid: if debug: sys.stderr.write(" !%d \n" % (retries)) retries -= 1 time.sleep(0) continue else: if debug: sys.stderr.write("\n") sys.stderr.flush() rmtype = resp[:ml] rml = resp[ml:(ml+1)] rdl = ord(rml) - 2 - len(rmtype) rdata = resp[(ml+1):-1] return (rmtype, rml, rdata, rdl) def do_init_recover(self, debug=False): self.send_command([0x7b], [0x00, 0x01, 0x03], debug=debug) self.send_command([0x7b], [0x00, 0x01, 0x01], debug=debug) self.send_command([0x7b], [0x00, 0x01, 0x02], debug=debug) self.send_command([0x7b], [0x00, 0x01, 0x03], debug=debug) self.send_command([0x7b], [0x00, 0x02, 0x76, 0x03, 0x17], debug=debug) # seed/key? self.send_command([0x7b], [0x00, 0x03, 0x75, 0x05, 0x13], debug=debug) # seed/key? def do_init_write(self, debug=False): # is this the command to erase the ECU? self.send_command([0x7d], [0x01, 0x01, 0x00], debug=debug) self.send_command([0x7d], [0x01, 0x01, 0x01], debug=debug) self.send_command([0x7d], [0x01, 0x01, 0x02], debug=debug) self.send_command([0x7d], [0x01, 0x01, 0x03], debug=debug) self.send_command([0x7d], [0x01, 0x02, 0x50, 0x47, 0x4d], debug=debug) # seed/key? self.send_command([0x7d], [0x01, 0x03, 0x2d, 0x46, 0x49], debug=debug) # seed/key? def do_pre_write(self, debug=False): self.send_command([0x7e], [0x01, 0x01, 0x00], debug=debug) time.sleep(11) self.send_command([0x7e], [0x01, 0x02], debug=debug) self.send_command([0x7e], [0x01, 0x03, 0x00, 0x00], debug=debug) self.send_command([0x7e], [0x01, 0x01, 0x00], debug=debug) self.send_command([0x7e], [0x01, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff], debug=debug) # password? self.send_command([0x7e], [0x01, 0x01, 0x00], debug=debug) self.send_command([0x7e], [0x01, 0x0e, 0x01, 0x90], debug=debug) self.send_command([0x7e], [0x01, 0x01, 0x01], debug=debug) self.send_command([0x7e], [0x01, 0x04, 0xff], debug=debug) self.send_command([0x7e], [0x01, 0x01, 0x00], debug=debug) def do_pre_write_wait(self, debug=False): while True: info = self.send_command([0x7e], [0x01, 0x05], debug=debug) if info[2][1] == 0x00: break self.send_command([0x7e], [0x01, 0x01, 0x00], debug=debug)
class HondaECU(object): def __init__(self, device_id=None, latency=None, baudrate=10400): super(HondaECU, self).__init__() self.device_id = device_id self.dev = None self.error = 0 self.resets = 0 self.latency = latency self.baudrate = baudrate self.reset() def reset(self): if self.dev != None: del self.dev self.dev = None self.dev = Device(self.device_id, auto_detach=(platform.system() != "Windows")) self.setup() def setup(self): self.dev.ftdi_fn.ftdi_usb_reset() self.dev.ftdi_fn.ftdi_usb_purge_buffers() self.dev.ftdi_fn.ftdi_set_line_property(8, 1, 0) self.dev.baudrate = self.baudrate if self.latency: self.dev.ftdi_fn.ftdi_set_latency_timer(self.latency) latency = c_ubyte() self.dev.ftdi_fn.ftdi_get_latency_timer(byref(latency)) def _break(self, ms): self.dev.ftdi_fn.ftdi_set_bitmode(1, 0x01) self.dev._write(b'\x00') time.sleep(ms) self.dev._write(b'\x01') self.dev.ftdi_fn.ftdi_set_bitmode(0, 0x00) self.dev.flush() def wakeup(self): self._break(.070) time.sleep(.130) def ping(self): return self.send_command([0xfe], [0x72], retries=0) != None def probe_tables(self, tables=None): if not tables: tables = [ 0x10, 0x11, 0x17, 0x20, 0x21, 0x60, 0x61, 0x67, 0x70, 0x71, 0xd0, 0xd1 ] ret = {} for t in tables: info = self.send_command([0x72], [0x71, t]) if info: if info[3] > 2: ret[t] = [info[3], info[2]] else: return {} return ret def init(self): self.wakeup() return self.ping() def kline_new(self): pin_byte = c_ubyte() self.dev.ftdi_fn.ftdi_read_pins(byref(pin_byte)) return (pin_byte.value == 0xff) def kline(self, timeout=.05): self.dev.flush() self.dev._write(b"\x00") to = time.time() while time.time() - to < timeout: tmp = self.dev._read(1) if len(tmp) == 1: return tmp == b"\x00" return False def kline_alt(self): self.dev.flush() self.dev._write(b"\xff") return self.dev._read(1) == b"\xff" def kline_old(self): b = create_string_buffer(2) self.dev.ftdi_fn.ftdi_poll_modem_status(b) return b.raw[1] & 16 == 0 def send(self, buf, ml, timeout=.001): self.dev.flush() msg = "".join([chr(b) for b in buf]).encode("latin1") self.dev._write(msg) r = len(msg) timeout = .05 + timeout * r to = time.time() while r > 0: r -= len(self.dev._read(r)) if time.time() - to > timeout: return None buf = bytearray() r = ml + 1 while r > 0: tmp = self.dev._read(r) r -= len(tmp) buf.extend(tmp) if time.time() - to > timeout: return None r = buf[-1] - ml - 1 while r > 0: tmp = self.dev._read(r) r -= len(tmp) buf.extend(tmp) if time.time() - to > timeout: return None return buf def send_command(self, mtype, data=[], retries=1): msg, ml, dl = format_message(mtype, data) r = 0 while r <= retries: dispatcher.send(signal="ecu.debug", sender=self, msg="%d > [%s]" % (r, ", ".join(["%02x" % m for m in msg]))) resp = self.send(msg, ml) if resp: if checksum8bitHonda(resp[:-1]) == resp[-1]: dispatcher.send(signal="ecu.debug", sender=self, msg="%d < [%s]" % (r, ", ".join(["%02x" % r for r in resp]))) rmtype = resp[:ml] valid = False if ml == 3: valid = (rmtype[:2] == bytearray( map(lambda x: x | 0x10, mtype[:2]))) elif ml == 1: valid = (rmtype == bytearray( map(lambda x: x & 0xf, mtype))) if valid: rml = resp[ml:(ml + 1)] rdl = ord(rml) - 2 - len(rmtype) rdata = resp[(ml + 1):-1] return (rmtype, rml, rdata, rdl) else: return None r += 1 def detect_ecu_state_new(self): t0 = self.send_command([0x72], [0x71, 0x00], retries=0) if t0 is None: self.wakeup() self.ping() t0 = self.send_command([0x72], [0x71, 0x00], retries=0) if not t0 is None: if bytes(t0[2][5:7]) != b"\x00\x00": return ECUSTATE.OK else: if self.send_command([0x7d], [0x01, 0x01, 0x00], retries=0): return ECUSTATE.RECOVER_OLD if self.send_command([0x7b], [0x00, 0x01, 0x01], retries=0): return ECUSTATE.RECOVER_NEW else: writestatus = self.send_command([0x7e], [0x01, 0x01, 0x00], retries=0) if not writestatus is None: if writestatus[2][1] == 0x0f: return ECUSTATE.WRITE_GOOD elif writestatus[2][1] == 0x10: return ECUSTATE.WRITE_INIT_OLD elif writestatus[2][1] == 0x20: return ECUSTATE.WRITE_INIT_NEW elif writestatus[2][1] == 0x30: return ECUSTATE.ERASE elif writestatus[2][1] == 0x40: return ECUSTATE.WRITE elif writestatus[2][1] == 0x0: return ECUSTATE.WRITE_UNKNOWN1 else: return ECUSTATE.ERROR else: readinfo = self.send_command([0x82, 0x82, 0x00], [0x00, 0x00, 0x00, 0x08], retries=0) if not readinfo is None: return ECUSTATE.READ return ECUSTATE.OFF if not self.kline() else ECUSTATE.UNKNOWN def do_init_recover(self): self.send_command([0x7b], [0x00, 0x01, 0x01]) self.send_command([0x7b], [0x00, 0x01, 0x02]) self.send_command([0x7b], [0x00, 0x01, 0x03]) self.send_command([0x7b], [0x00, 0x02, 0x76, 0x03, 0x17]) self.send_command([0x7b], [0x00, 0x03, 0x75, 0x05, 0x13]) def do_init_write(self): self.send_command([0x7d], [0x01, 0x01, 0x01]) self.send_command([0x7d], [0x01, 0x01, 0x02]) self.send_command([0x7d], [0x01, 0x01, 0x03]) self.send_command([0x7d], [0x01, 0x02, 0x50, 0x47, 0x4d]) self.send_command([0x7d], [0x01, 0x03, 0x2d, 0x46, 0x49]) def do_erase(self): self.send_command([0x7e], [0x01, 0x02]) self.send_command([0x7e], [0x01, 0x03, 0x00, 0x00]) self.send_command([0x7e], [0x01, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff]) self.send_command([0x7e], [0x01, 0x0e, 0x01, 0x90]) self.send_command([0x7e], [0x01, 0x01, 0x01]) self.send_command([0x7e], [0x01, 0x04, 0xff]) def do_erase_wait(self): cont = 1 while cont: info = self.send_command([0x7e], [0x01, 0x05]) if info: if info[2][1] == 0x00: cont = 0 else: cont = -1 if cont == 0: into = self.send_command([0x7e], [0x01, 0x01, 0x00]) def do_post_write(self): self.send_command([0x7e], [0x01, 0x09]) time.sleep(.5) self.send_command([0x7e], [0x01, 0x0a]) time.sleep(.5) self.send_command([0x7e], [0x01, 0x0c]) time.sleep(.5) info = self.send_command([0x7e], [0x01, 0x0d]) if info: return (info[2][1] == 0x0f) def get_faults(self): faults = {'past': [], 'current': []} for i in range(1, 0x0c): info_current = self.send_command([0x72], [0x74, i])[2] for j in [3, 5, 7]: if info_current[j] != 0: faults['current'].append( "%02d-%02d" % (info_current[j], info_current[j + 1])) if info_current[2] == 0: break for i in range(1, 0x0c): info_past = self.send_command([0x72], [0x73, i])[2] for j in [3, 5, 7]: if info_past[j] != 0: faults['past'].append("%02d-%02d" % (info_past[j], info_past[j + 1])) if info_past[2] == 0: break return faults
class HondaECU(object): def __init__(self, device_id=None, dprint=None, latency=None, baudrate=10400): super(HondaECU, self).__init__() self.device_id = device_id self.dev = None self.error = 0 self.resets = 0 self.latency = latency self.baudrate = baudrate if not dprint: self.dprint = self.__dprint else: self.dprint = dprint self.reset() def __dprint(self, msg): sys.stderr.write(msg) sys.stderr.write("\n") sys.stderr.flush() def reset(self): if self.dev != None: del self.dev self.dev = None self.dev = Device(self.device_id, auto_detach=(platform.system() != "Windows")) self.setup() self.starttime = time.time() def time(self): return time.time() - self.starttime def setup(self): self.dev.ftdi_fn.ftdi_usb_reset() self.dev.ftdi_fn.ftdi_usb_purge_buffers() self.dev.ftdi_fn.ftdi_set_line_property(8, 1, 0) self.dev.baudrate = self.baudrate if self.latency: self.dev.ftdi_fn.ftdi_set_latency_timer(self.latency) latency = c_ubyte() self.dev.ftdi_fn.ftdi_get_latency_timer(byref(latency)) def _break(self, ms, debug=False): self.dev.ftdi_fn.ftdi_set_bitmode(1, 0x01) self.dev._write(b'\x00') time.sleep(ms) self.dev._write(b'\x01') self.dev.ftdi_fn.ftdi_set_bitmode(0, 0x00) self.dev.flush() def wakeup(self): self._break(.070) time.sleep(.130) def ping(self, debug=False): return self.send_command([0xfe], [0x72]) != None def probe_tables(self, tables=None): if not tables: tables = [ 0x10, 0x11, 0x17, 0x20, 0x21, 0x60, 0x61, 0x67, 0x70, 0x71, 0xd0, 0xd1 ] ret = {} for t in tables: info = self.send_command([0x72], [0x71, t]) if info: if info[3] > 2: ret[t] = [info[3], info[2]] else: return {} return ret def init(self, debug=False): self.wakeup() return self.ping(debug) def kline_new(self): pin_byte = c_ubyte() self.dev.ftdi_fn.ftdi_read_pins(byref(pin_byte)) return (pin_byte.value == 0xff) def kline(self, timeout=.05): self.dev.flush() self.dev._write(b"\x00") to = time.time() while time.time() - to < timeout: tmp = self.dev._read(1) if len(tmp) == 1: return tmp == b"\x00" return False def kline_alt(self): self.dev.flush() self.dev._write(b"\xff") return self.dev._read(1) == b"\xff" def kline_old(self): b = create_string_buffer(2) self.dev.ftdi_fn.ftdi_poll_modem_status(b) return b.raw[1] & 16 == 0 def send(self, buf, ml, timeout=.001): self.dev.flush() msg = "".join([chr(b) for b in buf]).encode("latin1") self.dev._write(msg) r = len(msg) timeout = .05 + timeout * r to = time.time() while r > 0: r -= len(self.dev._read(r)) if time.time() - to > timeout: return None buf = bytearray() r = ml + 1 while r > 0: tmp = self.dev._read(r) r -= len(tmp) buf.extend(tmp) if time.time() - to > timeout: return None r = buf[-1] - ml - 1 while r > 0: tmp = self.dev._read(r) r -= len(tmp) buf.extend(tmp) if time.time() - to > timeout: return None return buf def send_command(self, mtype, data=[], debug=False, retries=1): msg, ml, dl = format_message(mtype, data) r = 0 while r <= retries: self.dprint("%d > [%s]" % (r, ", ".join(["%02x" % m for m in msg]))) resp = self.send(msg, ml) if resp: if checksum8bitHonda(resp[:-1]) == resp[-1]: self.dprint("%d < [%s]" % (r, ", ".join(["%02x" % r for r in resp]))) rmtype = resp[:ml] valid = False if ml == 3: valid = (rmtype[:2] == bytearray( map(lambda x: x | 0x10, mtype[:2]))) elif ml == 1: valid = (rmtype == bytearray( map(lambda x: x & 0xf, mtype))) if valid: rml = resp[ml:(ml + 1)] rdl = ord(rml) - 2 - len(rmtype) rdata = resp[(ml + 1):-1] return (rmtype, rml, rdata, rdl) else: print("shit") r += 1 def detect_ecu_state(self, wakeup=False): states = [ "unknown", # 0 "ok", # 1 "recover", # 2 "recover (old)", # 3 "init", # 4 "unlock", # 5 "erase", # 6 "write", # 7 "finalize", # 8 "incomplete", # 9 "reset", # 10 "error", # 11 "read", # 12 "off", # 13 ] state = 0 if wakeup: self.wakeup() if self.ping(): rinfo = self.send_command([0x7b], [0x00, 0x01, 0x01]) winfo = self.send_command([0x7d], [0x01, 0x01, 0x01]) if winfo: state = 1 else: state = 2 else: einfo = self.send_command([0x7e], [0x01, 0x01, 0x00]) if einfo: if einfo[2][1] == 0x00: state = 3 elif einfo[2][1] == 0x10: state = 4 elif einfo[2][1] == 0x20: state = 5 elif einfo[2][1] == 0x30: state = 6 elif einfo[2][1] == 0x40: state = 7 elif einfo[2][1] == 0x50: state = 8 elif einfo[2][1] == 0x0d: state = 9 elif einfo[2][1] == 0x0f: state = 10 elif einfo[2][1] == 0xfa: state = 11 else: print(hex(einfo[2][1])) else: dinfo = self.send_command([0x82, 0x82, 0x00], [0x00, 0x00, 0x00, 0x08]) if dinfo: state = 12 if state == 0: if not wakeup: state, _ = self.detect_ecu_state(wakeup=True) elif not self.kline(): state = 13 return state, states[state] def do_init_recover(self, debug=False): self.send_command([0x7b], [0x00, 0x01, 0x01]) self.send_command([0x7b], [0x00, 0x01, 0x02]) self.send_command([0x7b], [0x00, 0x01, 0x03]) self.send_command([0x7b], [0x00, 0x02, 0x76, 0x03, 0x17]) self.send_command([0x7b], [0x00, 0x03, 0x75, 0x05, 0x13]) def do_init_write(self, debug=False): self.send_command([0x7d], [0x01, 0x01, 0x01]) self.send_command([0x7d], [0x01, 0x01, 0x02]) self.send_command([0x7d], [0x01, 0x01, 0x03]) self.send_command([0x7d], [0x01, 0x02, 0x50, 0x47, 0x4d]) self.send_command([0x7d], [0x01, 0x03, 0x2d, 0x46, 0x49]) def do_erase(self, debug=False): self.send_command([0x7e], [0x01, 0x02]) self.send_command([0x7e], [0x01, 0x03, 0x00, 0x00]) self.send_command([0x7e], [0x01, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff]) self.send_command([0x7e], [0x01, 0x0e, 0x01, 0x90]) self.send_command([0x7e], [0x01, 0x01, 0x01]) self.send_command([0x7e], [0x01, 0x04, 0xff]) def do_erase_wait(self, debug=False): cont = 1 while cont: info = self.send_command([0x7e], [0x01, 0x05]) if info: if info[2][1] == 0x00: cont = 0 else: cont = -1 if cont == 0: into = self.send_command([0x7e], [0x01, 0x01, 0x00]) def do_post_write(self, debug=False): self.send_command([0x7e], [0x01, 0x09]) time.sleep(.5) self.send_command([0x7e], [0x01, 0x0a]) time.sleep(.5) self.send_command([0x7e], [0x01, 0x0c]) time.sleep(.5) info = self.send_command([0x7e], [0x01, 0x0d]) if info: return (info[2][1] == 0x0f) def get_faults(self, debug=False): faults = {'past': [], 'current': []} for i in range(1, 0x0c): info_current = self.send_command([0x72], [0x74, i])[2] for j in [3, 5, 7]: if info_current[j] != 0: faults['current'].append( "%02d-%02d" % (info_current[j], info_current[j + 1])) if info_current[2] == 0: break for i in range(1, 0x0c): info_past = self.send_command([0x72], [0x73, i])[2] for j in [3, 5, 7]: if info_past[j] != 0: faults['past'].append("%02d-%02d" % (info_past[j], info_past[j + 1])) if info_past[2] == 0: break return faults def do_read_flash(self, binfile): readsize = 12 location = 0 nl = False with open(binfile, "wb") as fbin: while True: info = self.send_command([0x82, 0x82, 0x00], format_read(location) + [readsize]) if not info: readsize -= 1 if readsize < 1: break else: fbin.write(info[2]) fbin.flush() location += readsize with open(binfile, "rb") as fbin: nbyts = os.path.getsize(binfile) byts = bytearray(fbin.read(nbyts)) _, status = do_validation(byts) return status == "good" def do_write_flash(self, byts, offset=0): print("write start") writesize = 128 maxi = len(byts) / 128 i = 0 while i < maxi: w = (i * writesize) bytstart = [s for s in struct.pack(">H", offset + (8 * i))] if i + 1 == maxi: bytend = [s for s in struct.pack(">H", offset)] else: bytend = [s for s in struct.pack(">H", offset + (8 * (i + 1)))] d = list(byts[((i + 0) * 128):((i + 1) * 128)]) x = bytstart + d + bytend c1 = checksum8bit(x) c2 = checksum8bitHonda(x) x = [0x01, 0x06] + x + [c1, c2] info = self.send_command([0x7e], x) if not info or ord(info[1]) != 5: return False i += 1 if i % 2 == 0: self.send_command([0x7e], [0x01, 0x08]) return True