Example #1
0
class LoopbackTester(object):
    def __init__(self):
        self.device = Device()

    def test_loopback(self, lengths):
        self.device.flush()
        time.sleep(0.1)
        for l in lengths:
            test_str = test_string(l)
            if self.device.write(test_str) != len(test_str):
                sys.stdout.write('*')
            time.sleep(0.1)
            result = ''
            for _ in range(3):
                result = self.device.read(l)
                if result:
                    break
            if result != test_str:
                self.device.flush()
                time.sleep(0.25)
            yield result == test_str

    def main(self):
        for bd in [9600, 31250, 115200, 1152000]:
            self.device.baudrate = bd

            for result in self.test_loopback(range(1, 50) + range(100, 500, 100) +
                                             range(1000, 5000, 1000)):
                if result:
                    sys.stdout.write('+')
                else:
                    sys.stdout.write('!')
            sys.stdout.write('\n')
Example #2
0
class LoopbackTester(object):
    def __init__(self):
        self.device = Device()

    def test_loopback(self, lengths):
        self.device.flush()
        time.sleep(0.1)
        for l in lengths:
            test_str = test_string(l)
            if self.device.write(test_str) != len(test_str):
                sys.stdout.write('*')
            time.sleep(0.1)
            result = ''
            for _ in range(3):
                result = self.device.read(l)
                if result:
                    break
            if result != test_str:
                self.device.flush()
                time.sleep(0.25)
            yield result == test_str

    def main(self):
        for bd in [9600, 31250, 115200, 1152000]:
            self.device.baudrate = bd

            for result in self.test_loopback(
                    range(1, 50) + range(100, 500, 100) +
                    range(1000, 5000, 1000)):
                if result:
                    sys.stdout.write('+')
                else:
                    sys.stdout.write('!')
            sys.stdout.write('\n')
Example #3
0
class CommunicationThread(threading.Thread):
    """Thread for fast serial communication"""

    BAUD_RATE = 2000000  # baud rate for serial communication
    BUFFER_SIZE = 64  # read buffer size in bytes

    def __init__(self, queue):
        """Initialize the communication worker"""

        # call constructor of super class
        threading.Thread.__init__(self)

        # set queue
        self.queue = queue

        # set FTDI device for communication with iCEstick
        try:
            self.dev = Device(mode='b', interface_select=INTERFACE_B)

            # set baudrate
            self.dev.baudrate = self.BAUD_RATE
        except:
            global exit_flag
            print(fg.li_red +
                  "[-] Could not connect to FTDI serial interface" + fg.rs)
            exit(1)

    def run(self):
        """Receive data"""
        global exit_flag

        while not exit_flag:
            if not self.queue.full():
                item = self.dev.read(self.BUFFER_SIZE)
                if item != b'':
                    self.queue.put(item)
class FtdiSyncInterface:
    ##################################################################
    # Construction
    ##################################################################
    def __init__(self, iface=None):
        self.interface = iface
        self.target = None
        self.prog_cb = None
        self.CMD_NOP = 0x0
        self.CMD_WR = 0x1
        self.CMD_RD = 0x2
        self.CMD_GP_WR = 0x3
        self.CMD_GP_RD = 0x4
        self.HDR_SIZE = 6
        self.MAX_SIZE = 255
        self.BLOCK_SIZE_WR = 64  # Really 2048
        self.BLOCK_SIZE_RD = 64
        self.MAGIC_ADDR = 0xF0000000

        # User specified (device_id)
        self.dev_id = None
        if iface != None and iface != "":
            self.dev_id = iface

    ##################################################################
    # set_progress_cb: Set progress callback
    ##################################################################
    def set_progress_cb(self, prog_cb):
        self.prog_cb = prog_cb

    ##################################################################
    # connect: Open serial connection
    ##################################################################
    def connect(self):
        self.target = Device(device_id=self.dev_id, interface_select=1)
        self.target.flush()
        time.sleep(0.01)

        BITMODE_SYNCFF = 0x40
        SIO_RTS_CTS_HS = (0x1 << 8)
        self.target.ftdi_fn.ftdi_set_bitmode(0, BITMODE_SYNCFF)
        self.target.ftdi_fn.ftdi_setflowctrl(SIO_RTS_CTS_HS)
        self.target.flush()

    ##################################################################
    # write: Write a block of data to a specified address
    ##################################################################
    def write(self, addr, data, length, addr_incr=True, max_block_size=-1):
        # Connect if required
        if self.target == None:
            self.connect()

        # Write blocks
        idx = 0
        remainder = length

        if self.prog_cb != None:
            self.prog_cb(0, length)

        if max_block_size == -1:
            max_block_size = self.BLOCK_SIZE_WR

        while remainder > 0:
            l = max_block_size
            if l > remainder:
                l = remainder

            cmd = bytearray(2 + 4 + l)
            cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_WR
            cmd[1] = l & 0xFF
            cmd[2] = (addr >> 24) & 0xFF
            cmd[3] = (addr >> 16) & 0xFF
            cmd[4] = (addr >> 8) & 0xFF
            cmd[5] = (addr >> 0) & 0xFF

            for i in range(l):
                cmd[6 + i] = data[idx]
                idx += 1

            # Write to interface
            self.target.write(cmd)

            # Update display
            if self.prog_cb != None:
                self.prog_cb(idx, length)

            if addr_incr:
                addr += l
            remainder -= l

    ##################################################################
    # read: Read a block of data from a specified address
    ##################################################################
    def read(self, addr, length, addr_incr=True, max_block_size=-1):
        # Connect if required
        if self.target == None:
            self.connect()

        idx = 0
        remainder = length
        data = bytearray(length)

        if self.prog_cb != None:
            self.prog_cb(0, length)

        if max_block_size == -1:
            max_block_size = self.BLOCK_SIZE_RD

        while remainder > 0:
            l = max_block_size
            if l > remainder:
                l = remainder

            cmd = bytearray(2 + 4)
            cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_RD
            cmd[1] = l & 0xFF
            cmd[2] = (addr >> 24) & 0xFF
            cmd[3] = (addr >> 16) & 0xFF
            cmd[4] = (addr >> 8) & 0xFF
            cmd[5] = (addr >> 0) & 0xFF

            # Write to serial port
            self.target.write(cmd)

            # Read block response
            for i in range(l):

                x = bytearray()
                while len(x) == 0:
                    x = self.target.read(1)

                data[idx] = ord(x) & 0xFF
                idx += 1

            # Update display
            if self.prog_cb != None:
                self.prog_cb(idx, length)

            if addr_incr:
                addr += l
            remainder -= l

        return data

    ##################################################################
    # read32: Read a word from a specified address
    ##################################################################
    def read32(self, addr):
        # Connect if required
        if self.target == None:
            self.connect()

        # Send read command
        cmd = bytearray([
            self.CMD_RD, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
            (addr >> 8) & 0xFF, (addr >> 0) & 0xFF
        ])
        self.target.write(cmd)

        value = 0
        idx = 0
        while (idx < 4):
            b = self.target.read(1)
            value |= (ord(b) << (idx * 8))
            idx += 1

        return value

    ##################################################################
    # write32: Write a word to a specified address
    ##################################################################
    def write32(self, addr, value):
        # Connect if required
        if self.target == None:
            self.connect()

        # Send write command
        cmd = bytearray([
            self.CMD_WR, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
            (addr >> 8) & 0xFF, (addr >> 0) & 0xFF, (value >> 0) & 0xFF,
            (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF
        ])
        self.target.write(cmd)

    ##################################################################
    # read_gpio: Read GPIO bus
    ##################################################################
    def read_gpio(self):
        # Connect if required
        if self.target == None:
            self.connect()

        # Send read command
        cmd = bytearray([self.CMD_GP_RD])
        self.target.write(cmd)

        return ord(self.target.read(1))

    ##################################################################
    # write_gpio: Write a byte to GPIO
    ##################################################################
    def write_gpio(self, value):
        # Connect if required
        if self.target == None:
            self.connect()

        # Send write command
        cmd = bytearray([self.CMD_GP_WR, (value >> 0) & 0xFF])
        self.target.write(cmd)
Example #5
0
class Ecu:
    def __init__(self):
        self.ser = Device(mode='b', lazy_open=True)

    def slowInit11(self):
        # Take the one-byte address to "bit bang" and bang the port
        self.bbser = BitBangDevice()
        print("beginning slow init")
        self.bbser.open()
        self.bbser.direction = 0x01
        self.bbser.port = 1
        time.sleep(.5)
        self.bbser.port = 0
        time.sleep(.2)
        self.bbser.port = 1
        time.sleep(.2)
        self.bbser.port = 0
        time.sleep(1.4)
        self.bbser.port = 1
        time.sleep(.2)
        self.bbser.close()
        print("slow init sent")

    def initialize(self, connect):
        self.connect = connect
        if self.connect == "SLOW-0x11":
            self.ser.close()
            time.sleep(.5)

            self.ecuconnect = False
            while self.ecuconnect == False:
                print("Attempting ECU connect: " + self.connect)

                # Bit bang the K-line
                self.slowInit11()
                self.ser.open()
                self.ser.ftdi_fn.ftdi_set_line_property(8, 1, 0)
                self.ser.baudrate = 10400
                self.ser.flush()

                # Wait for ECU response to bit bang
                waithex = [0x55, 0xef, 0x8f, 1]
                print("Wating for init response")
                response = self.waitfor(waithex)
                print(f"Init response: {hexlist(response[2])}")
                # Wait a bit
                time.sleep(.026)

                # Send 0x70
                self.send([0x70])

                # 0xee means that we're talking to the ECU
                waithex = [0xfe, 1]
                print("waiting for ECU reponse")
                response = self.waitfor(waithex)
                print(f"ecu connection response: {hexlist(response[2])}")
                if response[0] == True:
                    self.ecuconnect = True
                else:
                    print("ECU Connect Failed.  Retrying.")
                    return
                print("INIT done")

    def waitfor(self, wf):
        # This was used for debugging and really is only used for the init at this point.
        # wf should be a list with the timeout in the last element
        self.wf = wf
        isfound = False
        idx = 0
        foundlist = []
        capturebytes = []
        to = self.wf[-1]
        timecheck = time.time()
        while (time.time() <= (timecheck + to)) & (isfound == False):
            try:
                recvbyte = self.recvraw(1)
                if recvbyte != b"":
                    recvdata = ord(recvbyte)
                    capturebytes = capturebytes + [recvdata]
                    if recvdata == self.wf[idx]:
                        foundlist = foundlist + [recvdata]
                        idx = idx + 1
                    else:
                        foundlist = []
                        idx = 0
                    if idx == len(self.wf) - 1:
                        isfound = True
            except e:
                print([isfound, foundlist, capturebytes])
                print('error', e)
                break
        return [isfound, foundlist, capturebytes]

    def send(self, sendlist):
        self.sendlist = sendlist
        # Puts every byte in the sendlist out the serial port
        for i in self.sendlist:
            self.ser.write(chr(i))

    def recvraw(self, bytes):
        self.bytes = bytes
        recvdata = self.ser.read(self.bytes)
        return recvdata

    def recv(self, bytes):
        self.bytes = bytes
        isread = False
        while isread == False:
            recvbyte = self.ser.read(self.bytes)
            if recvbyte != b"":
                recvdata = recvbyte
                isread = True
        return recvdata

    def sendcommand(self, sendlist):
        # Wraps raw KWP command in a length byte and a checksum byte and hands it to send()
        self.sendlist = sendlist
        csum = 0
        self.sendlist = [len(self.sendlist)] + self.sendlist
        csum = self.checksum(self.sendlist)
        self.sendlist = self.sendlist + [csum]
        self.send(self.sendlist)
        print(f"sendcommand() sent: {hexlist(self.sendlist)}")
        cmdval = self.commandvalidate(self.sendlist)
        return cmdval

    def commandvalidate(self, command):
        # Every KWP command is echoed back.  This clears out these bytes.
        self.command = command
        cv = True
        for i in range(len(self.command)):
            recvdata = self.recv(1)
            if ord(recvdata) != self.command[i]:
                cv = cv & False
        return cv

    def checksum(self, checklist):
        # Calculates the simple checksum for the KWP command bytes.
        self.checklist = checklist
        csum = 0
        for i in self.checklist:
            csum = csum + i
        csum = (csum & 0xFF) % 0xFF
        return csum

    # used exclusivly for the errorhandling section
    def _raise(self, ex):
        raise ex

    def getresponse(self):
        # gets a properly formated KWP response from a command and returns the data.
        debugneeds = 4
        numbytes = 0
        # This is a hack because sometimes responses have leading 0x00's.  Why?  This removes them.
        while numbytes == 0:
            numbytes = ord(self.recv(1))
        gr = [numbytes]
        if debug >= debugneeds:
            print("Get bytes: " + hex(numbytes))
        for i in range(numbytes):
            recvdata = ord(self.recv(1))
            if debug >= debugneeds:
                print("Get byte" + str(i) + ": " + hex(recvdata))
            gr = gr + [recvdata]
        checkbyte = self.recv(1)
        if debug >= debugneeds:
            print(f"getresponse recieved: {hexlist(gr)}")
        if debug >= debugneeds:
            print("GR: " + hex(ord(checkbyte)) + "<-->" +
                  hex(self.checksum(gr)))
        if (gr[1] == 0x7f):
            return {  # returning the result so 0x78 (responsePending) can re-execute
                0x10:
                lambda: self._raise(Exception("generalReject", gr)),
                0x11:
                lambda: self._raise(Exception("busyRepeatRequest", gr)),
                0x12:
                lambda: self._raise(
                    Exception("subFunctionNotSupported / invalidFormat", gr)),
                0x21:
                lambda: self._raise(Exception("busyRepeatRequest", gr)),
                0x22:
                lambda: self._raise(
                    Exception("conditionsNotCorrectOrRequestSequenceError", gr)
                ),
                0x23:
                lambda: self._raise(Exception("routineNotComplete", gr)),
                0x31:
                lambda: self._raise(Exception("requestOutOfRange", gr)),
                0x33:
                lambda: self._raise(
                    Exception("securityAccessDenied / securityAccessRequested",
                              gr)),
                0x35:
                lambda: self._raise(Exception("invalidKey", gr)),
                0x36:
                lambda: self._raise(Exception("exceedNumberOfAttempts", gr)),
                0x37:
                lambda: self._raise(
                    Exception("requiredTimeDelayNotExpired", gr)),
                0x40:
                lambda: self._raise(Exception("downloadNotAccepted")),
                0x41:
                lambda: self._raise(Exception("improperDownloadType")),
                0x42:
                lambda: self._raise(
                    Exception("canNotDownloadToSpecifiedAddress", gr)),
                0x43:
                lambda: self._raise(
                    Exception("canNotDownloadNumberOfBytesRequested", gr)),
                0x50:
                lambda: self._raise(Exception("uploadNotAccepted", gr)),
                0x51:
                lambda: self._raise(Exception("improperUploadType", gr)),
                0x52:
                lambda: self._raise(
                    Exception("canNotUploadFromSpecifiedAddress", gr)),
                0x53:
                lambda: self._raise(
                    Exception("canNotUploadNumberOfBytesRequested", gr)),
                0x71:
                lambda: self._raise(Exception("transferSuspended", gr)),
                0x72:
                lambda: self._raise(Exception("transferAborted", gr)),
                0x74:
                lambda: self._raise(
                    Exception("illegalAddressInBlockTransfer", gr)),
                0x75:
                lambda: self._raise(
                    Exception("illegalByteCountInBlockTransfer", gr)),
                0x76:
                lambda: self._raise(Exception("illegalBlockTransferType", gr)),
                0x77:
                lambda: self._raise(
                    Exception("blockTransferDataChecksumError", gr)),
                0x78:
                self.getresponse,
                0x79:
                lambda: self._raise(
                    Exception("incorrectByteCountDuringBlockTransfer", gr)),
                0x80:
                lambda: self._raise(
                    Exception("serviceNotSupportedInActiveDiagnosticMode", gr)
                ),
                0x90:
                lambda: self._raise(Exception("noProgramm", gr)),
                0x91:
                lambda: self._raise(
                    Exception("requiredTimeDelayNotExpired", gr))
            }.get(
                gr[3], lambda: self._raise(
                    Exception("Generic KWP negative response", gr)))()
        return gr

    def readecuid(self, paramdef):
        #KWP2000 command to pull the ECU ID
        self.paramdef = paramdef
        debugneeds = 3
        response = self.sendcommand([0x10, 0x85])  # setup diag session
        reqserviceid = [0x1A]
        sendlist = reqserviceid + self.paramdef
        if debug >= debugneeds:
            print(f"readecuid sending: {hexlist(sendlist)}")
        self.sendcommand(sendlist)
        response = self.getresponse()
        if debug >= debugneeds:
            print(f"readecuid got: {hexlist(response)}")
        return response

    def securityAccessL3(self):
        ### Begin - level 3 security
        self.sendcommand([0x27, 0x03])
        response = self.getresponse()
        print(f"Seed: {hexlist(response)}")

        ## highBytes(uint8_t) = value(uint16_t) >> 8
        ## lowBytes(uint8_t) = value(uint16_t) & 0xff
        ## value(uint16_t) = (high << 8) + low

        seed = (response[3] << 24) + (response[4] << 16) + (
            response[5] << 8) + response[6]
        print(f"Seed: {seed}")

        key = seed + 12233  # standard VW access
        keyHex = [
            key >> 24 & 0xff, key >> 16 & 0xff, key >> 8 & 0xff, key & 0xff
        ]
        print(f"Seed: {hexlist(keyHex)}")
        self.sendcommand([0x27, 0x04] + keyHex)
        try:
            response = self.getresponse()  #sometimes this doesn't work
        except:
            response = self.getresponse()
        print(hexlist(response))
        if (response[3] != 0x34):
            raise Exception("failed to get L3 auth")

        print("End security level 3 access")

    # def securityAccessL1(self):
    #     pass
    #     ### Begin - Level 1 key/seed
    #     ## request seed 27 01
    #     self.sendcommand([0x27,0x01])
    #     response = self.getresponse()
    #     print(hexlist(response))
    #     # len? secreq  level    seed h    seed l    checksum
    #     # 0x06  0x67    0x01    0x6D 0x20 0xFC 0xB1   0xA8
    #     seedH = (response[3] << 8) + response[4]
    #     seedL = (response[5] << 8) + response[6]

    #     magicValue = 0x1c60020
    #     for count in range(5):
    #         tempstring = seedH &0x8000
    #         seedH = seedH << 1
    #         if(tempstring&0xFFFF == 0):
    #             temp2 = seedL&0xFFFF
    #             temp3 = tempstring&0xFFFF0000
    #             tempstring = temp2+temp3
    #             seedH = seedH&0xFFFE
    #             temp2 = tempstring&0xFFFF
    #             temp2 = temp2 >> 0x0F
    #             tempstring = tempstring&0xFFFF0000
    #             tempstring = tempstring+temp2
    #             seedH = seedH|tempstring
    #             seedL = seedL << 1
    #         else:
    #             tempstring = seedL+seedL
    #             seedH = seedH & 0xFFFE
    #             temp2 = tempstring & 0xFF #Same as EDC15 until this point
    #             temp3 = magicValue & 0xFFFFFF00
    #             temp2 = temp2 | 1
    #             magicValue = temp2 + temp3
    #             magicValue = magicValue & 0xFFFF00FF
    #             magicValue = magicValue | tempstring
    #             temp2 = seedL & 0xFFFF
    #             temp3 = tempstring & 0xFFFF0000
    #             temp2 = temp2 >> 0x0F
    #             tempstring = temp2 + temp3
    #             tempstring = tempstring | seedH
    #             magicValue = magicValue ^ 0x1289
    #             tempstring = tempstring ^ 0x0A22
    #             seedL = magicValue
    #             seedH = tempstring
    #     print(f"H:{seedH} L:{seedL}")
    #     ## high bytes = value >> 8
    #     ## low bytes = value & 0xff
    #     ## value = (high<<8) + low
    #     self.sendcommand([0x27,0x02, seedH & 0xff, seedH >>8, seedL & 0xff, seedL >> 8])
    #     # self.sendcommand([0x27,0x02, 0xff, 0xff, 0xff, 0xff])
    #     response = self.getresponse()

    def securityAccessL1(self):
        self.sendcommand([0x27, 0x01])
        response = self.getresponse()
        print(hexlist(response))
        seed = (response[3] << 24) + (response[4] << 16) + (
            response[5] << 8) + response[6]
        print(f"L1 seed: {seed}")
        '''
        for (byte i = 0; i < 5; i++) {
            if ((seed & 0x80000000) == 0x80000000) { // Check carry
                seed = (SEED_DATA[ecuID]) ^ ((seed << 1) | (seed >>> 31)); // rotate left and xor
            }
            else {
                seed = ((seed << 1) | (seed >>> 31)); // rotate left only
            }
        }
        return seed;
        '''
        magicValue = 0x1c60020

        def rshift(val, n):
            return (val >> n) & (0x7fffffff >> (n - 1))

        for x in range(5):
            if ((seed & 0x80000000) == 0x80000000):
                seed = magicValue ^ ((seed << 1) | rshift(seed, 31))
            else:
                seed = ((seed << 1) | rshift(seed, 31))
        print(f"L1 key: {seed}")
        keyHex = [
            seed >> 24 & 0xff, seed >> 16 & 0xff, seed >> 8 & 0xff, seed & 0xff
        ]
        self.sendcommand([0x27, 0x02] + keyHex)
        # self.sendcommand([0x27,0x02, 0xff, 0xff, 0xff, 0xff])
        return self.getresponse()

    def stopcomm(self):
        # KWP2000 command to tell the ECU that the communications is finished
        stopcommunication = [0x82]
        self.sendcommand(stopcommunication)
        response = self.getresponse()
        return response

    def startdiagsession(self, bps):
        # KWP2000 setup that sets the baud for the logging session
        self.bps = bps
        startdiagnosticsession = [0x10]
        sessionType = [0x86]
        #   if self.bps == 10400:
        #      bpsout = [ 0x?? ]
        #   if self.bps == 14400:
        #      bpsout = [ 0x?? ]
        if self.bps == 19200:
            bpsout = [0x30]
        if self.bps == 38400:
            bpsout = [0x50]
        if self.bps == 56000:
            bpsout = [0x63]
        if self.bps == 57600:
            bpsout = [0x64]
        if self.bps == 124800:
            bpsout = [0x87]
        if self.bps == 250000:
            bpsout = [0xA7]
        if (self.bps != 0):
            sendlist = startdiagnosticsession + sessionType + bpsout
        else:
            sendlist = startdiagnosticsession + sessionType
        self.sendcommand(sendlist)
        response = self.getresponse()
        self.ser.baudrate = self.bps
        time.sleep(1)
        return response

    def accesstimingparameter(self, params):
        # KWP2000 command to access timing parameters
        self.params = params
        accesstiming_setval = [0x83, 0x03]
        accesstiming = accesstiming_setval + self.params
        sendlist = accesstiming
        self.sendcommand(sendlist)
        response = self.getresponse()
        return response

    def readmembyaddr(self, readvals):
        # Function to read an area of ECU memory.
        debugneeds = 4
        self.readvals = readvals
        rdmembyaddr = [0x23]
        sendlist = rdmembyaddr + self.readvals
        if debug >= debugneeds:
            print("readmembyaddr() sendlist: " + hexlist(sendlist))
        self.sendcommand(sendlist)
        response = self.getresponse()
        if debug >= debugneeds:
            print("readmembyaddr() response: " + hexlist(response))
        return response

    def writemembyaddr(self, writevals):
        # Function to write to an area of ECU memory.
        debugneeds = 4
        self.writevals = writevals
        wrmembyaddr = [0x3D]
        sendlist = wrmembyaddr + self.writevals
        if debug >= debugneeds:
            print("writemembyaddr() sendlist: " + hexlist(sendlist))
        self.sendcommand(sendlist)
        response = self.getresponse()
        if debug >= debugneeds:
            print("writemembyaddr() response: " + hexlist(response))
        return response

    def testerpresent(self):
        # KWP2000 TesterPresent command
        tp = [0x3E]
        self.sendcommand(tp)
        response = self.getresponse()
        return response

    def setuplogrecord(self, logline):
        # KWP2000 command to access timing parameters
        self.logline = logline
        response = []
        sendlist = [0xb7]  # is 0xB7 the "locator?"
        sendlist = sendlist + [0x03]  # Number of bytes per field ?
        sendlist = sendlist + self.logline
        self.sendcommand(sendlist)
        response = self.getresponse()
        return response

    def getlogrecord(self):
        # Command to request a logging record
        gr = [0xb7]
        self.sendcommand(gr)
        response = self.getresponse()
        return response

    def sendhexstring(self, dumpstring):
        # Takes a list of characters as a string, turns every two characters into a hex byte and sends it raw.
        # used as needed for dev/test/debug
        self.dumpstring = dumpstring
        for i in range(len(self.dumpstring) / 2):
            self.send([int('0x' + self.dumpstring[i * 2:(i * 2) + 2], 16)])
Example #6
0
class Py64drive:
    def __init__(self):
        self.device = Device(device_id="64drive USB device")

    def _send_cmd(self, cmd, params):
        txbuf = struct.pack(">B3s",cmd,b"CMD")
        if len(params) >= 1:
            txbuf += struct.pack(">L",params[0])
        if len(params) >= 2:
            txbuf += struct.pack(">L",params[1])

        self.device.write(txbuf)

    def _recv_resp(self):
        rxbuf = self.device.read(4)
        return struct.unpack(">L", rxbuf)

    def _recv_ver_resp(self):
        rxbuf = self.device.read(8)
        return struct.unpack(">L4s", rxbuf)

    def _recv_success(self,command):
        while True:
            rxbuf = self.device.read(4)
            if rxbuf: break

        if rxbuf != b"CMP" + bytes([command]):
            #print("Got {}, expected CMP{}",rxbuf,command,file=sys.stderr)
            return False

        return True

    def load_image(self, data, bank, ram_addr=0):
        length = len(data)

        if length % 512:
            #print("File was truncated during loading.", file=sys.stderr)
            length -= length % 512

        for offset in range(0, length, CHUNK_SIZE):
            size = min(CHUNK_SIZE, length - offset)

            block = data[offset:offset+size]

            self._send_cmd(0x20, (ram_addr + offset, (bank << 24) | (size & 0xffffff)))
            self.device.write(block)

            self._recv_success(0x20)

    def dump_image(self, length, bank, ram_addr=0):		
        data = b""

        if length % 512:
            #print("File was truncated during dumping.", file=sys.stderr)
            length -= length % 512

        for offset in range(0, length, CHUNK_SIZE):
            size = min(CHUNK_SIZE, length - offset)

            self._send_cmd(0x30, (ram_addr + offset, (bank << 24) | (size & 0xffffff)))
            data += self.device.read(size)

            self._recv_success(0x30)

        return data

    def set_save(self, save_type):
        self._send_cmd(0x70, (save_type,))
        self._recv_success(0x70)

    def read_version(self):
        self._send_cmd(0x80,())

        val,magic = self._recv_ver_resp()
        if val == 0 or magic != b"UDEV":
            #print("Incorrect 64drive version reported.", file=sys.stderr)
            return False

        self._recv_success(0x80)
        return True

    def pi_write_block(self,*args):
        raise NotImplementedError("Not implemented yet")

    def pi_write_block_long(self,*args):
        raise NotImplementedError("Not implemented yet")

    def pi_read_int32(self):
        raise NotImplementedError("Not implemented yet")

    def pi_write_int32(self,num):
        raise NotImplementedError("Not implemented yet")
Example #7
0
class MDB(object):
  def __init__(self, deviceid):
    self.__deviceid = deviceid
    self.__scaling = 0
    self.__coins = {}
    self.__deposited = 0

  def _ftdisend(self, data, mode):
    data_parity = self._parityOf(int(hexlify(data), 16))

    if data_parity == -1:
      if mode:
        #parity = serial.PARITY_EVEN
        parity = 2
      else:
        #parity = serial.PARITY_ODD
        parity = 1
    else:
      if mode:
        #parity = serial.PARITY_ODD
        parity = 1
      else:
        #parity = serial.PARITY_EVEN
        parity = 2
    try:
        self.__device = Device(self.__deviceid)
        self.__device.ftdi_fn.ftdi_set_line_property(8, 1, parity)
        self.__device.baudrate = 9600
        self.__device.write(data)
        self.__device.flush()
    except pylibftdi.FtdiError:
        print "FtdiError"
        self._ftdisend(data, mode)

  def _parityOf(self, int_type):
      parity = 0
      while (int_type):
          parity = ~parity
          int_type = int_type & (int_type - 1)
      return(parity)

  def _read(self):
    returndata = []
    for i in self.__device.read(100):
      returndata.append(i)

    return returndata

  def _send(self, data):
    mode = 1

    for element in data:
      self._ftdisend(element, mode)
      mode = 0

    self._ftdisend(self._calcchk(data), 0)

    time.sleep(0.1)
    return self._read()

  def _calcchk(self, data):
    sum = 0
    for byte in data:
        sum += int(hexlify(byte), 16)
    return unhexlify('{:02x}'.format(sum % 256))

  def _getbits(self, byte):
    return bin(int(hexlify(byte), 16))[2:].zfill(8)

# CoinChanger
  def reset(self):
    print "OUT: Reset"
    answer = self._send(data = ['\x08'])
    if (len(answer) == 1) and (answer[0] == '\x00'):
      print "IN : OK"
      #self.ack()
    else:
      print "IN: Fail"
      print answer

  def poll(self):
    print "OUT: Poll"
    answer = self._send(data = ['\x0B'])

    i = 0
    while i < len(answer):
      if answer[i] == '\x00':
        message = "ACK"
      elif answer[i] == '\xFF':
        message = "NACK"
      elif '\x01' <= answer[i] <= '\x0D':
        message = {
          '\x01': 'Escrow request - An escrow lever activation has been detected.',
          '\x02': 'Changer Payout Busy - The changer is busy activating payout devices',
          '\x03': 'No Credit - A coin was validated but did not get to the place in the system when credit is given',
          '\x04': 'Defective Tube Sensor - The changer hasdetected one of the tube sensors behaving abnormally',
          '\x05': 'Double Arrival - Two coins were detected too close together to validate either one',
          '\x06': 'Acceptor Unplugged - The changer has detected that the acceptor has been removed',
          '\x07': 'Tube Jam - A tube payout attempt has resulted in jammed condition',
          '\x08': 'ROM checksum error - The changers internal checksum does not match the calculated checksum',
          '\x09': 'Coin Routing Error - A coin has been validated, but did not follow the intended routing',
          '\x0A': 'Changer Busy - The changer is busy and can not answer a detailed command right now',
          '\x0B': 'Changer was Reset - The changer has detected an Reset condition and has returned to its power-on idle condition',
          '\x0C': 'Coin Jam - A coin(s) has jammed in the acceptance path',
          '\x0D': 'Possible Credited Coin Removal - There has been an attempt to remove a credited coin',
        }.get(answer[i])
        print "IN: " + message
      elif '\x20' <= answer[i] <= '\x3F':
        print "IN: Slugs deposited: " + str(int(self._getbits(answer[i])[3:], 2))
      elif '\x40' <= answer[i] <= '\x7F':
        bits = self._getbits(answer[i])

        if bits[2:4] == '00':
          routing = "Cash Box"
        elif bits[2:4] == '01':
          routing = "Tubes"
        elif bits[2:4] == '10':
          routing = "Not used"
        elif bits[2:4] == '11':
          routing = "Rejected"
        else:
          routing = "Unknown"

        cointype = int(bits[4:8], 2)
        coinsinroutingpath = str(int(self._getbits(answer[i+1]), 2))

        print "IN: Coin deposited: Type " + str(cointype) + ", sent to " + routing  + ". Now " + coinsinroutingpath + " coins there"
        self.__deposited += self.__coins[cointype] * self.__scaling
        i += 1
        break;
      elif '\x80' <= answer[i] <= '\xFE':
        bits = self._getbits(answer[i])
        
        number = str(int(bits[1:4], 2))
        cointype = str(int(bits[4:8], 2))
        coinsintube = str(int(self._getbits(answer[i+1]), 2))
        print "IN: Coins dispensed manually: " + number + " coins of type " + cointype  + ". Now " + coinsintube + " coins there"
        i += 1
        break;
      else: #\x0E -> \x1F
        print "IN: Unknown Poll Status reply" + hexlify(answer[i])
      i += 1
    self.ack()

  def setup(self):
    print "OUT: Setup"
    answer = self._send(data = ['\x09'])
    if len(answer) == 24:
      print "IN: Changer Feature Level: " + str(ord(answer[0])) + " (" + hex(ord(answer[0])) + ")"
      print "IN: Country/Currency-Code: " + hex(ord(answer[1])) + " " + hex(ord(answer[2]))
      print "IN: Coin Scaling Factor: " + str(ord(answer[3])) + " (" + hex(ord(answer[3])) + ")"
      self.__scaling = int(ord(answer[3]))
      print "IN: Decimal Places: " + str(ord(answer[4])) + " (" + hex(ord(answer[4])) + ")"
      canberoutedtotube = (self._getbits(answer[5]) + self._getbits(answer[6]))[::-1]
      for i in range(7, 23):
        print "IN: Coin Type: " + str(i-7) + ", Value: " + str(ord(answer[i])) + ", Can be routed to tube: " + canberoutedtotube[i-7]
        self.__coins[(i-7)] = int(ord(answer[i]))
      self.ack()
    else:
      print "IN: Fail - " + answer

  def expansionidentification(self):
    print "OUT: Expansion Identification"
    answer = self._send(data = ['\x0F', '\x00'])
    if len(answer) == 34:
      print "IN: Manufacturer Code: " + str(answer[0]) + str(answer[1]) + str(answer[2]) + " (" + hex(ord(answer[0])) + " " + hex(ord(answer[1])) + " " + hex(ord(answer[2])) + ")"
      print "IN: Serial Number: " + ''.join(answer[i] for i in range(3, 15)) + " (" + " ".join(hex(ord(answer[i])) for i in range(3, 15)) + ")"
      print "IN: Model #/Tuning Revision: " + ''.join(answer[i] for i in range(15, 27)) + " (" + " ".join(hex(ord(answer[i])) for i in range(15, 27)) + ")"
      print "IN: Software Version: " + hex(ord(answer[27])) + " " + hex(ord(answer[28]))
      optionalfeatures = (self._getbits(answer[29]) + self._getbits(answer[30]) + self._getbits(answer[31]) + self._getbits(answer[32]))[::-1]
      print "IN: Optional Feature: Alternative Payout method: " + optionalfeatures[0]
      print "IN: Optional Feature: Extended Diagnostic command supported: " + optionalfeatures[1]
      print "IN: Optional Feature: Controlled Manual Fill and Payout commands supported: " + optionalfeatures[2]
      print "IN: Optional Feature: File Transport Layer (FTL) supported: " + optionalfeatures[3]
      print "IN: Optional Features: Future extensions: " + optionalfeatures[4:]
      self.ack()
      features = []
      for i in range (29, 33):
        features.append(ord(answer[i]))
      return features
    else:
      print "IN: Fail - " + answer

  def expansionfeatureenable(self, features):
    print "OUT: Expansion Feature Enable"
    answer = self._send(data = ['\x0F', '\x01'] + features)
    if (len(answer) == 1) and (answer[0] == '\x00'):
      print "IN : OK"
      self.ack()
    else:
     print "IN: Fail - " + answer

  def expansiondiagnosticstatus(self):
    print "OUT: Expansion Diagnostic Status"
    answer = self._send(data = ['\x0F', '\x05'])
    if len(answer) == 3:
      print "IN: Main-Code: " + hex(ord(answer[0]))
      print "IN: Sub-Code: " + hex(ord(answer[1]))
      message = {
        '\x01': 'Powering up',
        '\x02': 'Powering down',
        '\x03': 'OK',
        '\x04': 'Keypad shifted',
        '\x05': '',
        '\x06': 'Inhibited by VMC',
        '\x10': '',
        '\x11': '',
        '\x12': '',
        '\x13': '',
        '\x14': '',
        '\x15': '',
        'unknown': 'Unknown Main-Code',
      }.get(answer[0], 'unknown')

      if ( (answer[0] == '\x05') and (answer[1] == '\x10') ):
        message = "Manual Fill / Payout active"

      if ( (answer[0] == '\x05') and (answer[1] == '\x20') ):
        message = "New Inventory Information Available"

      if answer[0] == '\x10':
        message = "General changer error: " + {
          '\x00': 'Non specific error',
          '\x01': 'Check sum error #1. A check sum error over a particular data range of configuration field detected.',
          '\x02': 'Check sum error #2. A check sum error over a secondary data range or configuration field detected.',
          '\x03': 'Low line voltage detected. The changer has disabled acceptance or payout due to a low voltage condition',
          'unknown': 'Unknown Sub-Code',
        }.get(answer[1], 'unknown')

      if answer[0] == '\x11':
        message = "Discriminator module error: " + {
          '\x00': 'Non specific discriminator error.',
          '\x10': 'Flight deck open.',
          '\x11': 'Escrow Return stuck open.',
          '\x30': 'Coin jam in sensor.',
          '\x41': 'Discrimination below specified standard.',
          '\x50': 'Validation sensor A out of range. The acceptor detects a problem with sensor A.',
          '\x51': 'Validation sensor B out of range. The acceptor detects a problem with sensor B.',
          '\x52': 'Validation sensor C out of range. The acceptor detects a problem with sensor C.',
          '\x53': 'Operating temperature exceeded. The acceptor detects the ambient temperature has exceeded the changer\'s operating range, thus possibly affecting the acceptance rate.',
          '\x54': 'Sizing optics failure. The acceptor detects an error in the sizing optics.',
          'unknown': 'Unknown Sub-Code',
      }.get(answer[1], 'unknown')

      if answer[0] == '\x12':
        message = "Accept gate module error: " + {
          '\x00': 'Non specific accept gate error',
          '\x30': 'Coins entered gate, but did not exit.',
          '\x31': 'Accept gate alarm active.',
          '\x40': 'Accept gate open, but no coin detected.',
          '\x50': 'Post gate sensor covered before gate opened.',
          'unknown': 'Unknown Sub-Code',
      }.get(answer[1], 'unknown')

      if answer[0] == '\x13':
        message = "Separator module error: " + {
          '\x00': 'Non specific separator error',
          '\x10': 'Sort sensor error. The acceptor detects an error in the sorting sensor',
          'unknown': 'Unknown Sub-Code',
      }.get(answer[1], 'unknown')

      if answer[0] == '\x14':
        message = "Dispenser module error: " + {
          '\x00': 'Non specific dispenser error.',
          'unknown': 'Unknown Sub-Code',
      }.get(answer[1], 'unknown')

      if answer[0] == '\x15':
        message = "Coin Cassette / tube module error: " + {
          '\x00': 'Non specific cassette error.',
          '\x02': 'Cassette removed.',
          '\x03': 'Cash box sensor error. The changer detects an error in a cash box sensor.',
          '\x04': 'Sunlight on tube sensors. The changer detects too much ambient light on one or more of the tube sensors.',
          'unknown': 'Unknown Sub-Code',
      }.get(answer[1], 'unknown')

      print "IN: Message: " + message
      self.ack()
    else:
      print "IN: Fail - " + answer

  def tubestatus(self):
    print "OUT: Tube Status"
    answer = self._send(data = ['\x0A'])
    if len(answer) == 19:
      print "IN: Tube Full Status: " + hex(ord(answer[0])) + " " + hex(ord(answer[1]))
      for i in range(2, 18):
        print "IN: Tube Status: " + str(i-2) + " --> " + str(ord(answer[i])) + " (" + hex(ord(answer[i])) + ")"
      self.ack()
    else:
      print "IN: Fail - " + answer

  def enableall(self, manual = False):
    if manual == True:
      self.cointype('\xFF', '\xFF', '\xFF', '\xFF')
    else:
      self.cointype('\xFF', '\xFF', '\x00', '\x00')

  def disableall(self, manual = False):
    if manual == True:
      self.cointype('\x00', '\x00', '\xFF', '\xFF')
    else:
      self.cointype('\x00', '\x00', '\x00', '\x00')

  def cointype(self, enable1, enable2, manual1, manual2):
    print "OUT: Coin type"
    answer = self._send(data = ['\x0C', enable1, enable2, manual1, manual2])
    if (len(answer) == 1) and (answer[0] == '\x00'):
      print "IN : OK"
      self.ack()
    else:
      print "IN: Fail - " + answer

  def ack(self):
    print "OUT: ACK"
    # ACK, NACK and RET don't take the mode-Bit
    self._ftdisend('\x00', 0)

  def payout(self, value):
    print "OUT: Payout"
    self._send(data = ['\x0F', '\x02', unhexlify('{:02x}'.format(int(value) / self.__scaling)) ])

  def payoutpoll(self):
    print "OUT: Payout Poll"
    self._send(data = ['\x0F', '\x04'])

  def payoutstatus(self):
    print "OUT: Payout Status"
    self._send(data = ['\x0F', '\x03'])

  def getdeposited(self):
    return self.__deposited

  def cleardeposited(self):
    self.__deposited = 0
Example #8
0
class Ecu:

   def __init__(self):
      self.ser = Device(mode='b', lazy_open=True)


   def bbang(self, bba):
      # Take the one-byte address to "bit bang" and bang the port
      self.bba = bba
      self.bbser = BitBangDevice()
      self.bbser.open()
      self.bbser.direction = 0x01
      self.bbser.port = 1
      time.sleep(.5)
      self.bbser.port = 0
      outstr = "><"
      sys.stdout.write('\r' + outstr)
      sys.stdout.flush()
      time.sleep(.2)
      bbbitmask = 1
      for i in range(8):
         if (self.bba[0] & bbbitmask) > 0:
            outbit = 1
         else:
            outbit = 0   
         self.bbser.port = outbit
         outstr = ">" + str(outbit) + outstr[1:] 
         sys.stdout.write('\r' + outstr)
         sys.stdout.flush()
         bbbitmask = bbbitmask * 2
         time.sleep(.2)
      self.bbser.port = 1
      sys.stdout.write("\n")
      self.bbser.close()

   def initialize(self, connect):
      self.connect = connect
      if self.connect == "SLOW-0x11":
         self.ser.close()
         time.sleep(.5)

         self.ecuconnect = False
         while self.ecuconnect == False:
            print("Attempting ECU connect: " + self.connect )

            # Bit bang the K-line
            bbseq = [ 0x11 ]
            self.bbang(bbseq)
            self.ser.open()
            self.ser.ftdi_fn.ftdi_set_line_property(8, 1, 0)
            self.ser.baudrate = 10400
            self.ser.flush()

            # Wait for ECU response to bit bang
            waithex = [ 0x55, 0xef, 0x8f, 1 ]
            self.waitfor(waithex)
            # Wait a bit
            time.sleep(.026)

            # Send 0x70
            self.send([ 0x70 ])
   
            # 0xee means that we're talking to the ECU
            waithex = [ 0xee, 1 ]
            response = self.waitfor(waithex)
            if response[0] == True:
                self.ecuconnect = True
            else:
                print("ECU Connect Failed.  Retrying.")

   def waitfor(self, wf):
      # This was used for debugging and really is only used for the init at this point.
      # wf should be a list with the timeout in the last element
      self.wf = wf
      isfound = False
      idx = 0
      foundlist = []
      capturebytes = []
      to = self.wf[-1]
      timecheck = time.time()
      while (time.time() <= (timecheck+to)) & (isfound == False): 
         try:
            recvbyte = self.recvraw(1)
            if recvbyte != "":
               recvdata = ord(recvbyte)
               capturebytes = capturebytes + [ recvdata ]
               if recvdata == self.wf[idx]: 
                  foundlist = foundlist + [ recvdata ]
                  idx = idx + 1
               else: 
                  foundlist = []
                  idx = 0
               if idx == len(self.wf)-1:
                  isfound = True
         except:
            print('error')
            break
      return [ isfound, foundlist, capturebytes ]

   def send(self, sendlist):
      self.sendlist = sendlist
      # Puts every byte in the sendlist out the serial port
      for i in self.sendlist:
         self.ser.write(chr(i))

   def recvraw(self, bytes):
      self.bytes = bytes
      recvdata = self.ser.read(self.bytes)
      return recvdata

   def recv(self, bytes):
      self.bytes = bytes
      isread = False
      while isread == False:
         recvbyte = self.ser.read(self.bytes)
         if recvbyte != "":
            recvdata = recvbyte
            isread = True
      return recvdata      

   def sendcommand(self, sendlist):
      # Wraps raw KWP command in a length byte and a checksum byte and hands it to send()
      self.sendlist = sendlist
      csum = 0
      self.sendlist = [len(self.sendlist)] + self.sendlist 
      csum = self.checksum(self.sendlist)
      self.sendlist = self.sendlist + [csum] 
      self.send(self.sendlist)
      cmdval = self.commandvalidate(self.sendlist)
      return cmdval

   def commandvalidate(self, command):
      # Every KWP command is echoed back.  This clears out these bytes.
      self.command = command
      cv = True
      for i in range(len(self.command)):
         recvdata = self.recv(1)
         if ord(recvdata) != self.command[i]:
            cv = cv & False
      return cv   

   def checksum(self, checklist):
      # Calculates the simple checksum for the KWP command bytes.
      self.checklist = checklist
      csum = 0
      for i in self.checklist:
         csum = csum + i
      csum = (csum & 0xFF) % 0xFF
      return csum

   def getresponse(self):
      # gets a properly formated KWP response from a command and returns the data. 
      debugneeds = 4
      numbytes = 0
      while numbytes == 0:     # This is a hack because sometimes responses have leading 0x00's.  Why?  This removes them.
         numbytes = ord(self.recv(1))
      gr = [ numbytes ]
      if debug >= debugneeds: print("Get bytes: " + hex(numbytes))
      for i in range(numbytes):
         recvdata = ord(self.recv(1))
         if debug >= debugneeds: print("Get byte" + str(i) + ": " + hex(recvdata))
         gr = gr + [ recvdata ]
      checkbyte = self.recv(1)
      if debug >= debugneeds: print(gr)
      if debug >= debugneeds: print("GR: " + hex(ord(checkbyte)) + "<-->" + hex(self.checksum(gr))) 
      return (gr + [ ord(checkbyte) ])

   def readecuid(self, paramdef):      
      # KWP2000 command to pull the ECU ID
      self.paramdef = paramdef
      debugneeds = 4
      reqserviceid = [ 0x1A ]
      sendlist = reqserviceid + self.paramdef
      if debug >= debugneeds: print( sendlist )
      self.sendcommand(sendlist)
      response = self.getresponse()
      if debug >= debugneeds: print(response)
      return response

   def stopcomm(self):
      # KWP2000 command to tell the ECU that the communications is finished
      stopcommunication = [ 0x82 ]
      self.sendcommand(stopcommunication)
      response = self.getresponse()
      return response

   def startdiagsession(self, bps):
      # KWP2000 setup that sets the baud for the logging session
      self.bps = bps
      startdiagnosticsession = [ 0x10 ]
      setbaud = [ 0x86 ]  #Is this the actual function of 0x86?
   #   if self.bps == 10400:
   #      bpsout = [ 0x?? ]
   #   if self.bps == 14400:
   #      bpsout = [ 0x?? ]
      if self.bps == 19200:
         bpsout = [ 0x30 ]
      if self.bps == 38400:
         bpsout = [ 0x50 ]
      if self.bps == 56000:
         bpsout = [ 0x63 ]
      if self.bps == 57600:
         bpsout = [ 0x64 ]
   #   if self.bps == 125000:
   #      bpsout = [ 0x?? ]
      sendlist = startdiagnosticsession + setbaud + bpsout
      self.sendcommand(sendlist)
      response = self.getresponse()
      self.ser.baudrate = self.bps
      time.sleep(1)
      return response

   def accesstimingparameter(self, params):
      # KWP2000 command to access timing parameters
      self.params = params
      accesstiming_setval = [ 0x083, 0x03 ]
      accesstiming = accesstiming_setval + self.params
      sendlist = accesstiming
      self.sendcommand(sendlist)
      response = self.getresponse()
      return response
   
   def readmembyaddr(self, readvals):
      # Function to read an area of ECU memory.
      debugneeds = 4
      self.readvals = readvals
      rdmembyaddr = [ 0x23 ]
      sendlist = rdmembyaddr + self.readvals
      if debug >= debugneeds: print("readmembyaddr() sendlist: " + sendlist)
      self.sendcommand(sendlist)
      response = self.getresponse()
      if debug >= debugneeds: print("readmembyaddr() response: " + response)
      return response

   def writemembyaddr(self, writevals):
      # Function to write to an area of ECU memory.
      debugneeds = 4
      self.writevals = writevals
      wrmembyaddr = [ 0x3D ]
      sendlist = wrmembyaddr + self.writevals
      if debug >= debugneeds: print("writemembyaddr() sendlist: " + sendlist)
      self.sendcommand(sendlist)
      response = self.getresponse()
      if debug >= debugneeds: print("writemembyaddr() response: " + response)
      return response

   def testerpresent(self):
      # KWP2000 TesterPresent command
      tp = [ 0x3E ]
      self.sendcommand(tp)
      response = self.getresponse()
      return response

   def setuplogrecord(self, logline):
      # KWP2000 command to access timing parameters
      self.logline = logline
      response = []
      sendlist = [ 0xb7 ]                           # is 0xB7 the "locator?"
      sendlist = sendlist + [ 0x03 ]           # Number of bytes per field ?
      sendlist = sendlist + self.logline                
      self.sendcommand(sendlist)
      response = self.getresponse()
      return response

   def getlogrecord(self):
      # Command to request a logging record
      gr = [ 0xb7 ]
      self.sendcommand(gr)
      response = self.getresponse()
      return response

   def sendhexstring(self, dumpstring):
      # Takes a list of characters as a string, turns every two characters into a hex byte and sends it raw.
      # used as needed for dev/test/debug
      self.dumpstring = dumpstring
      for i in range(len(self.dumpstring)/2):
         self.send([ int('0x'+self.dumpstring[i*2:(i*2)+2],16) ])
Example #9
0
try:
  dev_list  = Driver().list_devices()
  if len(dev_list) != 0:
    print "\n\nFollowing devices found: \n"
    for device_ in dev_list:
      print device_
    
    dev = Device(device_id="FTZ17IRO", mode='b', interface_select=2)
    
    dev.open()
    
    tx_data = bytearray(range(0, 256))
    dev.write(tx_data)
    
    rx_data = bytearray(dev.read(257))#, timeout = 0))
    
    if len(rx_data) == 256 :
      print "\n\n[Test] Test 1 Passed: Sent 256 bytes of data, received 256 bytes of data"
    
    failed = False
    for i in range(256):
      if ((tx_data[i]+1)%256) != rx_data[i]:
        print "[Test] Test 2: Data verification failed! , tx_data : ", tx_data[i], " =/= rx_data : ", rx_data[i]
        failed = True	
    
    if not(failed):
      print "[Test] Test 2 Successful: Data transmit and receive verified!"

    dev.close()
if len(dev_list) != 0:
    print "Following devices found:"
for device_ in dev_list:
    print device_

dev = Device(device_id="FTZ17IRO", mode='b', interface_select=2)
dev.open()

epochs = 1024 * 10
BLOCK_LEN = 2048
tx_data = bytearray([random.randrange(0, 256) for i in range(BLOCK_LEN)])
ts = time.time()
while epochs:

    dev.write(tx_data)
    rx_data = bytearray(dev.read(BLOCK_LEN))

    #print "Epoch: {}".format(epochs)
    failed = False
    for i in range(BLOCK_LEN):
        if ((tx_data[i] + 1) % 256) != rx_data[i]:
            print "Epoch: {}".format(epochs)
            print "[Test] Test 2: Data verification failed! , tx_data : ", tx_data[
                i], " =/= rx_data : ", rx_data[i]
            failed = True
            print "Breaking..."
            break
    if failed:
        break

    epochs -= 1
Example #11
0
class FtdiAsyncInterface:
    ##################################################################
    # Construction
    ##################################################################
    def __init__(self, iface=None):
        self.interface = iface
        self.target = None
        self.prog_cb = None
        self.CMD_NOP = 0x0
        self.CMD_WR = 0x1
        self.CMD_RD = 0x2
        self.CMD_GP_WR = 0x3
        self.CMD_GP_RD = 0x4
        self.HDR_SIZE = 6
        self.MAX_SIZE = 255
        self.BLOCK_SIZE_WR = 16  # Really 2048
        self.BLOCK_SIZE_RD = 32
        self.MAGIC_ADDR = 0xF0000000

        # Default
        self.dev_id = None
        self.dev_iface = 2

        # User specified (device_id.iface)
        if iface != None and iface != "":
            parts = iface.split(".")
            self.dev_id = parts[0]
            if len(parts) > 1:
                self.dev_iface = int(parts[1])

    ##################################################################
    # set_progress_cb: Set progress callback
    ##################################################################
    def set_progress_cb(self, prog_cb):
        self.prog_cb = prog_cb

    ##################################################################
    # connect: Open serial connection
    ##################################################################
    def connect(self):
        self.target = Device(device_id=self.dev_id,
                             interface_select=self.dev_iface)

    ##################################################################
    # write: Write a block of data to a specified address
    ##################################################################
    def write(self, addr, data, length, addr_incr=True, max_block_size=-1):
        # Connect if required
        if self.target == None:
            self.connect()

        # Write blocks
        idx = 0
        remainder = length

        if self.prog_cb != None:
            self.prog_cb(0, length)

        if max_block_size == -1:
            max_block_size = self.BLOCK_SIZE_WR

        while remainder > 0:
            l = max_block_size
            if l > remainder:
                l = remainder

            cmd = bytearray(2 + 4 + l)
            cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_WR
            cmd[1] = l & 0xFF
            cmd[2] = (addr >> 24) & 0xFF
            cmd[3] = (addr >> 16) & 0xFF
            cmd[4] = (addr >> 8) & 0xFF
            cmd[5] = (addr >> 0) & 0xFF

            for i in range(l):
                cmd[6 + i] = data[idx]
                idx += 1

            # Write to interface
            self.target.write(cmd)

            # Update display
            if self.prog_cb != None:
                self.prog_cb(idx, length)

            if addr_incr:
                addr += l
            remainder -= l

    ##################################################################
    # read: Read a block of data from a specified address
    ##################################################################
    def read(self, addr, length, addr_incr=True, max_block_size=-1):
        # Connect if required
        if self.target == None:
            self.connect()

        idx = 0
        remainder = length
        data = bytearray(length)

        if self.prog_cb != None:
            self.prog_cb(0, length)

        if max_block_size == -1:
            max_block_size = self.BLOCK_SIZE_RD

        while remainder > 0:
            l = max_block_size
            if l > remainder:
                l = remainder

            cmd = bytearray(2 + 4)
            cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_RD
            cmd[1] = l & 0xFF
            cmd[2] = (addr >> 24) & 0xFF
            cmd[3] = (addr >> 16) & 0xFF
            cmd[4] = (addr >> 8) & 0xFF
            cmd[5] = (addr >> 0) & 0xFF

            # Write to serial port
            self.target.write(cmd)

            # Read block response
            for i in range(l):
                data[idx] = ord(self.target.read(1)) & 0xFF
                idx += 1

            # Update display
            if self.prog_cb != None:
                self.prog_cb(idx, length)

            if addr_incr:
                addr += l
            remainder -= l

        return data

    ##################################################################
    # read32: Read a word from a specified address
    ##################################################################
    def read32(self, addr):
        # Connect if required
        if self.target == None:
            self.connect()

        #if addr == self.MAGIC_ADDR:
        #    return self.read_gpio()

        # Send read command
        cmd = bytearray([
            self.CMD_RD, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
            (addr >> 8) & 0xFF, (addr >> 0) & 0xFF
        ])
        self.target.write(cmd)

        value = 0
        idx = 0
        while (idx < 4):
            b = self.target.read(1)
            value |= (ord(b) << (idx * 8))
            idx += 1

        return value

    ##################################################################
    # write32: Write a word to a specified address
    ##################################################################
    def write32(self, addr, value):
        # Connect if required
        if self.target == None:
            self.connect()

        #if addr == self.MAGIC_ADDR:
        #    self.write_gpio(value)
        #    return

        # Send write command
        cmd = bytearray([
            self.CMD_WR, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
            (addr >> 8) & 0xFF, (addr >> 0) & 0xFF, (value >> 0) & 0xFF,
            (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF
        ])
        self.target.write(cmd)

    ##################################################################
    # read_gpio: Read GPIO bus
    ##################################################################
    def read_gpio(self):
        # Connect if required
        if self.target == None:
            self.connect()

        # Send read command
        cmd = bytearray([self.CMD_GP_RD])
        self.target.write(cmd)

        return ord(self.target.read(1))

    ##################################################################
    # write_gpio: Write a byte to GPIO
    ##################################################################
    def write_gpio(self, value):
        # Connect if required
        if self.target == None:
            self.connect()

        # Send write command
        cmd = bytearray([self.CMD_GP_WR, (value >> 0) & 0xFF])
        self.target.write(cmd)
Example #12
0
class Glitcher():
    """Simple iCEstick voltage glitcher"""
    def __init__(self,
                 start_offset=0,
                 end_offset=5000,
                 offset_step=1,
                 duration_step=1,
                 start_duration=1,
                 end_duration=30,
                 retries=2):
        """Initialize the glitcher"""

        # set FTDI device for communication with iCEstick
        self.dev = Device(mode='b', interface_select=INTERFACE_B)

        # set baudrate
        self.dev.baudrate = 115200

        # set offset and duration steps
        self.offset_step = offset_step
        self.duration_step = duration_step
        self.start_offset = start_offset
        self.end_offset = end_offset
        self.start_duration = start_duration
        self.end_duration = end_duration
        self.retries = retries

    def read_data(self, terminator=b"\r\n", echo=True):
        """Read UART data"""

        # if echo is on, read the echo first
        if echo:
            c = b"\x00"
            while c != b"\r":
                c = self.dev.read(1)

        data = b""
        count = 0
        while True:
            count += 1
            data += self.dev.read(1)
            if data[-2:] == CRLF:
                break
            if count > MAX_BYTES:
                return "UART_TIMEOUT"

        # return read bytes without terminator
        return data.replace(terminator, b"")

    def synchronize(self):
        """UART synchronization with auto baudrate detection"""

        # use auto baudrate detection
        cmd = b"?"
        data = CMD_PASSTHROUGH + pack("B", len(cmd)) + cmd
        self.dev.write(data)

        # receive synchronized message
        resp = self.read_data(echo=False)

        if resp != SYNCHRONIZED:
            return False

        # respond with "Synchronized"
        cmd = SYNCHRONIZED + CRLF
        data = CMD_PASSTHROUGH + pack("B", len(cmd)) + cmd
        self.dev.write(data)

        # read response, should be "OK"
        resp = self.read_data()
        if resp != OK:
            return False

        # send crystal frequency (in kHz)
        self.dev.write(CMD_PASSTHROUGH + b"\x07" + CRYSTAL_FREQ)

        # read response, should be "OK"
        resp = self.read_data()
        if resp != OK:
            return False

        return True

    def read_command_response(self,
                              response_count,
                              echo=True,
                              terminator=b"\r\n"):
        """Read command response from target device"""

        result = []
        data = b""

        # if echo is on, read the sent back ISP command before the actual response
        count = 0
        if echo:
            c = b"\x00"
            while c != b"\r":
                count += 1
                c = self.dev.read(1)

                if count > MAX_BYTES:
                    return "TIMEOUT"

        # read return code
        data = b""
        old_len = 0
        count = 0
        while True:
            data += self.dev.read(1)

            # if data[len(terminator) * -1:] == terminator:
            if data[-2:] == terminator:
                break

            if len(data) == old_len:
                count += 1

                if count > MAX_BYTES:
                    return "TIMEOUT"
            else:
                old_len = len(data)

        # add return code to result
        return_code = data.replace(CRLF, b"")
        result.append(return_code)

        # check return code and return immediately if it is not "CMD_SUCCESS"
        if return_code != b"0":
            return result

        # read specified number of responses
        for i in range(response_count):
            data = b""
            count = 0
            old_len = 0
            while True:
                data += self.dev.read(1)
                if data[-2:] == terminator:
                    break

                if len(data) == old_len:
                    count += 1

                    if count > MAX_BYTES:
                        return "TIMEOUT"
                else:
                    old_len = len(data)

            # add response to result
            result.append(data.replace(CRLF, b""))

        return result

    def send_target_command(self,
                            command,
                            response_count=0,
                            echo=True,
                            terminator=b"\r\n"):
        """Send command to target device"""

        # send command
        cmd = command + b"\x0d"
        data = CMD_PASSTHROUGH + pack("B", len(cmd)) + cmd
        self.dev.write(data)

        # read response
        resp = self.read_command_response(response_count, echo, terminator)

        return resp

    def reset_target(self):
        """Reset target device"""

        # send command
        self.dev.write(CMD_RESET)

    def set_glitch_duration(self, duration):
        """Send config command to set glitch duration in FPGA clock cycles"""

        # send command
        data = CMD_SET_DURATION + pack("<L", duration)
        self.dev.write(data)

    def set_glitch_offset(self, offset):
        """Send config command to set glitch offset in FPGA clock cycles"""

        # send command
        data = CMD_SET_OFFSET + pack("<L", offset)
        self.dev.write(data)

    def start_glitch(self):
        """Start glitch (actually start the offset counter)"""

        # send command
        self.dev.write(CMD_START_GLITCH)

    def dump_memory(self):
        """Dump the target device memory"""

        # dump the 32 kB flash memory and save the content to a file
        with open(DUMP_FILE, "wb") as f:

            # read all 32 kB of flash memory
            for i in range(1023):
                # first send "OK" to the target device
                resp = self.send_target_command(OK, 1, True, b"\r\n")

                # then a read command for 32 bytes
                cmd = "R {} 32".format(i * 32).encode("utf-8")
                resp = self.send_target_command(cmd, 1, True, b"\r\n")

                if resp[0] == b"0":
                    # read and decode uu-encodod data in a somewhat "hacky" way
                    data = b"begin 666 <data>\n" + resp[1] + b" \n \nend\n"
                    raw_data = decode(data, "uu")
                    print(fg.li_blue + bytes.hex(raw_data) + fg.rs)
                    f.write(raw_data)

        print(fg.li_white +
              "[*] Dumped memory written to '{}'".format(DUMP_FILE) + fg.rs)

    def run(self):
        """Run the glitching process with the current configuration"""

        # # reset target
        # self.reset_target()
        #
        # # read and show the UID of the target device
        # print(fg.li_white + "[*] Read target device UID" + fg.rs)
        # resp = self.send_target_command(b"N", 4, True, b"\r\n")
        #
        # if resp[0] == b"0" and len(resp) == 5:
        #     uid = "{} {} {} {}".format(resp[4].decode("ascii"), resp[3].decode("ascii"), resp[2].decode("ascii"), resp[1].decode("ascii"))
        # else:
        #     uid = "<unknown>"
        #     print(fg.li_red + "[-] Could not read target device UID" + fg.rs)
        #
        # # read part identification number
        # print(fg.li_white + "[*] Read target device part ID" + fg.rs)
        # resp = self.send_target_command(b"J", 1, True, b"\r\n")
        #
        # if resp[0] == b"0":
        #     part_id = "{}".format(resp[1].decode("ascii"))
        # else:
        #     part_id = "<unknown>"
        #     print(fg.li_red + "[-] Could not read target part ID" + fg.rs)
        #
        # # show target device info
        # print(fg.li_white + "[*] Target device info:\n" +
        #         "    UID:                        {}\n".format(uid) +
        #         "    Part identification number: {}".format(part_id))
        #
        # print(fg.li_white + "[*] Press <ENTER> to start the glitching process" + fg.rs)
        # input()

        # measure the time
        start_time = datetime.now()

        for offset in range(self.start_offset, self.end_offset,
                            self.offset_step):
            # duration in 10 ns increments
            for duration in range(self.start_duration, self.end_duration,
                                  self.duration_step):
                # better test more than once
                for i in range(self.retries):

                    # set glitch config
                    print(fg.li_white +
                          "[*] Set glitch configuration ({},{})".format(
                              offset, duration) + fg.rs)
                    self.set_glitch_offset(offset)
                    self.set_glitch_duration(duration)

                    # start glitch (start the offset counter)
                    self.start_glitch()

                    # reset target device
                    self.reset_target()

                    # synchronize with target
                    if not self.synchronize():
                        print(fg.li_red + "[-] Error during sychronisation" +
                              fg.rs)
                        continue

                    # read flash memory address
                    resp = self.send_target_command(READ_FLASH_CHECK, 1, True,
                                                    b"\r\n")

                    if resp[0] == b"0":
                        # measure the time again
                        end_time = datetime.now()

                        print(
                            ef.bold + fg.green + "[*] Glitching success!\n"
                            "    Bypassed the readout protection with the following glitch parameters:\n"
                            "        offset   = {}\n        duration = {}\n".
                            format(offset, duration) +
                            "    Time to find this glitch: {}".format(
                                end_time - start_time) + fg.rs)

                        # save successful glitching configuration in file
                        config = "{},{},{},{}\n".format(
                            offset, duration, resp[0], resp[1])
                        with open(RESULTS_FILE, "a") as f:
                            f.write(config)

                        # dump memory
                        print(fg.li_white +
                              "[*] Dumping the flash memory ..." + fg.rs)
                        self.dump_memory()

                        return True

                    elif resp[0] != b"19":
                        print(fg.li_red +
                              "[?] Unexpected response: {}".format(resp) +
                              fg.rs)

        return False
from pylibftdi import Device
import asterisk.manager, binascii, time
#'\x01\xe7\x6e'
USER_DATA = {
    '\x01\xe7l\x02': {'nick': 'swiss',       'song': 'custom/youngbuk2'},
    '\x01\xe7_Y':    {'nick': 'Joule_Thief', 'song': 'custom/smokeonwater'},
    '\x01\xe7ma':    {'nick': 'elimisteve',  'song': 'custom/loop'},
    '\x01\xe7f\x8d': {'nick': 'luckyaba',    'song': 'custom/goodfoot'}
}

KEY_LENGTH = 6

dev = Device(mode='b')
dev.baudrate = 9600
while True:
    string = dev.read(KEY_LENGTH).strip()
    old_string = '' # Not yet used to compare string and old_string

    if string != "":
        if string in USER_DATA:
            print "Hex:", binascii.b2a_hex(string)
            dev.write('U')

            # Play ringtone/theme song
            manager = asterisk.manager.Manager()
            manager.connect('192.168.1.200')
            manager.login('steve', 'amp111')

            # manager.originate('', '')
            data = {"Action": "Originate",
                    "Channel": "SIP/180",
Example #14
0
class Ecu:
    def __init__(self):
        self.ser = Device(mode='b', lazy_open=True)

    def bbang(self, bba):
        # Take the one-byte address to "bit bang" and bang the port
        self.bba = bba
        self.bbser = BitBangDevice()
        self.bbser.open()
        self.bbser.direction = 0x01
        self.bbser.port = 1
        time.sleep(.5)
        self.bbser.port = 0
        outstr = "><"
        sys.stdout.write('\r' + outstr)
        sys.stdout.flush()
        time.sleep(.2)
        bbbitmask = 1
        for i in range(8):
            if (self.bba[0] & bbbitmask) > 0:
                outbit = 1
            else:
                outbit = 0
            self.bbser.port = outbit
            outstr = ">" + str(outbit) + outstr[1:]
            sys.stdout.write('\r' + outstr)
            sys.stdout.flush()
            bbbitmask = bbbitmask * 2
            time.sleep(.2)
        self.bbser.port = 1
        sys.stdout.write("\n")
        self.bbser.close()

    def initialize(self, connect):
        self.connect = connect
        if self.connect == "SLOW-0x11":
            self.ser.close()
            time.sleep(.5)

            self.ecuconnect = False
            while self.ecuconnect == False:
                print("Attempting ECU connect: " + self.connect)

                # Bit bang the K-line
                bbseq = [0x11]
                self.bbang(bbseq)
                self.ser.open()
                self.ser.ftdi_fn.ftdi_set_line_property(8, 1, 0)
                self.ser.baudrate = 10400
                self.ser.flush()

                # Wait for ECU response to bit bang
                waithex = [0x55, 0xef, 0x8f, 1]
                self.waitfor(waithex)
                # Wait a bit
                time.sleep(.026)

                # Send 0x70
                self.send([0x70])

                # 0xee means that we're talking to the ECU
                waithex = [0xee, 1]
                response = self.waitfor(waithex)
                if response[0] == True:
                    self.ecuconnect = True
                else:
                    print("ECU Connect Failed.  Retrying.")

    def waitfor(self, wf):
        # This was used for debugging and really is only used for the init at this point.
        # wf should be a list with the timeout in the last element
        self.wf = wf
        isfound = False
        idx = 0
        foundlist = []
        capturebytes = []
        to = self.wf[-1]
        timecheck = time.time()
        while (time.time() <= (timecheck + to)) & (isfound == False):
            try:
                recvbyte = self.recvraw(1)
                if recvbyte != "":
                    recvdata = ord(recvbyte)
                    capturebytes = capturebytes + [recvdata]
                    if recvdata == self.wf[idx]:
                        foundlist = foundlist + [recvdata]
                        idx = idx + 1
                    else:
                        foundlist = []
                        idx = 0
                    if idx == len(self.wf) - 1:
                        isfound = True
            except:
                print('error')
                break
        return [isfound, foundlist, capturebytes]

    def send(self, sendlist):
        self.sendlist = sendlist
        # Puts every byte in the sendlist out the serial port
        for i in self.sendlist:
            self.ser.write(chr(i))

    def recvraw(self, bytes):
        self.bytes = bytes
        recvdata = self.ser.read(self.bytes)
        return recvdata

    def recv(self, bytes):
        self.bytes = bytes
        isread = False
        while isread == False:
            recvbyte = self.ser.read(self.bytes)
            if recvbyte != "":
                recvdata = recvbyte
                isread = True
        return recvdata

    def sendcommand(self, sendlist):
        # Wraps raw KWP command in a length byte and a checksum byte and hands it to send()
        self.sendlist = sendlist
        csum = 0
        self.sendlist = [len(self.sendlist)] + self.sendlist
        csum = self.checksum(self.sendlist)
        self.sendlist = self.sendlist + [csum]
        self.send(self.sendlist)
        cmdval = self.commandvalidate(self.sendlist)
        return cmdval

    def commandvalidate(self, command):
        # Every KWP command is echoed back.  This clears out these bytes.
        self.command = command
        cv = True
        for i in range(len(self.command)):
            recvdata = self.recv(1)
            if ord(recvdata) != self.command[i]:
                cv = cv & False
        return cv

    def checksum(self, checklist):
        # Calculates the simple checksum for the KWP command bytes.
        self.checklist = checklist
        csum = 0
        for i in self.checklist:
            csum = csum + i
        csum = (csum & 0xFF) % 0xFF
        return csum

    def getresponse(self):
        # gets a properly formated KWP response from a command and returns the data.
        debugneeds = 4
        numbytes = 0
        while numbytes == 0:  # This is a hack because sometimes responses have leading 0x00's.  Why?  This removes them.
            numbytes = ord(self.recv(1))
        gr = [numbytes]
        if debug >= debugneeds: print("Get bytes: " + hex(numbytes))
        for i in range(numbytes):
            recvdata = ord(self.recv(1))
            if debug >= debugneeds:
                print("Get byte" + str(i) + ": " + hex(recvdata))
            gr = gr + [recvdata]
        checkbyte = self.recv(1)
        if debug >= debugneeds: print(gr)
        if debug >= debugneeds:
            print("GR: " + hex(ord(checkbyte)) + "<-->" +
                  hex(self.checksum(gr)))
        return (gr + [ord(checkbyte)])

    def readecuid(self, paramdef):
        # KWP2000 command to pull the ECU ID
        self.paramdef = paramdef
        debugneeds = 4
        reqserviceid = [0x1A]
        sendlist = reqserviceid + self.paramdef
        if debug >= debugneeds: print(sendlist)
        self.sendcommand(sendlist)
        response = self.getresponse()
        if debug >= debugneeds: print(response)
        return response

    def stopcomm(self):
        # KWP2000 command to tell the ECU that the communications is finished
        stopcommunication = [0x82]
        self.sendcommand(stopcommunication)
        response = self.getresponse()
        return response

    def startdiagsession(self, bps):
        # KWP2000 setup that sets the baud for the logging session
        self.bps = bps
        startdiagnosticsession = [0x10]
        setbaud = [0x86]  #Is this the actual function of 0x86?
        #   if self.bps == 10400:
        #      bpsout = [ 0x?? ]
        #   if self.bps == 14400:
        #      bpsout = [ 0x?? ]
        if self.bps == 19200:
            bpsout = [0x30]
        if self.bps == 38400:
            bpsout = [0x50]
        if self.bps == 56000:
            bpsout = [0x63]
        if self.bps == 57600:
            bpsout = [0x64]

    #   if self.bps == 125000:
    #      bpsout = [ 0x?? ]
        sendlist = startdiagnosticsession + setbaud + bpsout
        self.sendcommand(sendlist)
        response = self.getresponse()
        self.ser.baudrate = self.bps
        time.sleep(1)
        return response

    def accesstimingparameter(self, params):
        # KWP2000 command to access timing parameters
        self.params = params
        accesstiming_setval = [0x083, 0x03]
        accesstiming = accesstiming_setval + self.params
        sendlist = accesstiming
        self.sendcommand(sendlist)
        response = self.getresponse()
        return response

    def readmembyaddr(self, readvals):
        # Function to read an area of ECU memory.
        debugneeds = 4
        self.readvals = readvals
        rdmembyaddr = [0x23]
        sendlist = rdmembyaddr + self.readvals
        if debug >= debugneeds: print("readmembyaddr() sendlist: " + sendlist)
        self.sendcommand(sendlist)
        response = self.getresponse()
        if debug >= debugneeds: print("readmembyaddr() response: " + response)
        return response

    def writemembyaddr(self, writevals):
        # Function to write to an area of ECU memory.
        debugneeds = 4
        self.writevals = writevals
        wrmembyaddr = [0x3D]
        sendlist = wrmembyaddr + self.writevals
        if debug >= debugneeds: print("writemembyaddr() sendlist: " + sendlist)
        self.sendcommand(sendlist)
        response = self.getresponse()
        if debug >= debugneeds: print("writemembyaddr() response: " + response)
        return response

    def testerpresent(self):
        # KWP2000 TesterPresent command
        tp = [0x3E]
        self.sendcommand(tp)
        response = self.getresponse()
        return response

    def setuplogrecord(self, logline):
        # KWP2000 command to access timing parameters
        self.logline = logline
        response = []
        sendlist = [0xb7]  # is 0xB7 the "locator?"
        sendlist = sendlist + [0x03]  # Number of bytes per field ?
        sendlist = sendlist + self.logline
        self.sendcommand(sendlist)
        response = self.getresponse()
        return response

    def getlogrecord(self):
        # Command to request a logging record
        gr = [0xb7]
        self.sendcommand(gr)
        response = self.getresponse()
        return response

    def sendhexstring(self, dumpstring):
        # Takes a list of characters as a string, turns every two characters into a hex byte and sends it raw.
        # used as needed for dev/test/debug
        self.dumpstring = dumpstring
        for i in range(len(self.dumpstring) / 2):
            self.send([int('0x' + self.dumpstring[i * 2:(i * 2) + 2], 16)])