Пример #1
0
 def reset(self, quick=True):
     """Reset the interface (scan tool)
     
     quick -- peform a quick reset (if supported), otherwise perform
         a slower, full reset
     """
     if quick:
         self.at_cmd("ATWS")
     else:
         # Since the baud rate might change after the ATZ, we just wait
         # and clear the receive buffer.
         self._write("ATZ\r")
         time.sleep(ELM32X.ATZ_TIMEOUT)
         self.port.clear_rx_buffer()  # ignore any garbage due to wrong baud rate
         debug("baud on reset = %s" % ELM32X.detect_baudrate(self.port))
     return
Пример #2
0
 def reset(self, quick=True):
     """Reset the interface (scan tool)
     
     quick -- peform a quick reset (if supported), otherwise perform
         a slower, full reset
     """
     if quick:
         self.at_cmd("ATWS")
     else:
         # Since the baud rate might change after the ATZ, we just wait
         # and clear the receive buffer.
         self._write("ATZ\r")
         time.sleep(ELM32X.ATZ_TIMEOUT)
         self.port.clear_rx_buffer()  # ignore any garbage due to wrong baud rate
         debug("baud on reset = %s" % ELM32X.detect_baudrate(self.port))
     return
Пример #3
0
 def _message_bytes_from_ascii(self, ascii_messages):
     """Convert each ASCII message into a list of raw bytes
     and return the list of raw messages.
     """
     raw_messages = []
     debug(ascii_messages)
     for message in ascii_messages:
         message = message.replace(" ", "")
         # pad 11-bit CAN headers out to 32 bits for consistency,
         # since ELM already does this for 29-bit CAN headers
         if len(message) & 1: message = "00000" + message
         raw_message = []
         for i in range(0, len(message), 2):
             raw_message.append(int(message[i:i+2], 16))
         raw_messages.append(raw_message)
     debug(raw_messages)
     return raw_messages
Пример #4
0
 def _message_bytes_from_ascii(self, ascii_messages):
     """Convert each ASCII message into a list of raw bytes
     and return the list of raw messages.
     """
     raw_messages = []
     debug(ascii_messages)
     for message in ascii_messages:
         message = message.replace(" ", "")
         # pad 11-bit CAN headers out to 32 bits for consistency,
         # since ELM already does this for 29-bit CAN headers
         if len(message) & 1: message = "00000" + message
         raw_message = []
         for i in range(0, len(message), 2):
             raw_message.append(int(message[i:i + 2], 16))
         raw_messages.append(raw_message)
     debug(raw_messages)
     return raw_messages
Пример #5
0
def create(port, callback=None, baud=None):
    """Create an instance of the appropriate ELM32X subclass at the
    given port
    
    port -- the SerialPort subclass to which the interface is attached
    callback -- the callback function used to provide status updates
        (default None)
    baud -- the baud rate to use, or None (default) to auto-detect
    """
    # Use the appropriate baud rate, auto-detecting if requested
    #print "Reached here"
    if baud:
        if port.get_baudrate() != baud:
            untested("specifying the baud rate for obd.interface.elm.create()")
            port.set_baudrate(baud)
    else:
        current_baud = ELM32X.detect_baudrate(port)
        if not current_baud:
            raise InterfaceError("Unable to connect to ELM; does it have power?")
    #print "Reached here"
    # Query the interface for its identity
    identifier = ELM32X._at_cmd(port, "ATI")
    if identifier.startswith("ATI\r"): identifier = identifier[4:]
    debug(identifier)
    chip_identifier, chip_version = identifier.split(" ")
    #print identifier
     
    # Check for extended command set
    extended = ELM32X._at_cmd(port, "STI")
    if extended.startswith("STI\r"): extended = extended[4:]
    if extended != "?":
        untested(extended)
        chip_identifier, chip_version = extended.rsplit(" ", 1)
    print extended
    # Create an instance of the appropriate ELM32X subclass
    try:
        elm_class = _classes[chip_identifier]
    except KeyError as e:
        untested("unknown ELM response to ATI")
        raise InterfaceError("Unknown response to ATI: %r" % identifier)
    interface = elm_class(port, chip_identifier, callback=callback)

    debug("%s detected on port %s at %d baud" %
          (chip_identifier, interface.port.name, interface.port.get_baudrate()))
    return interface
Пример #6
0
def create(port, callback=None, baud=None):
    """Create an instance of the appropriate ELM32X subclass at the
    given port
    
    port -- the SerialPort subclass to which the interface is attached
    callback -- the callback function used to provide status updates
        (default None)
    baud -- the baud rate to use, or None (default) to auto-detect
    """
    # Use the appropriate baud rate, auto-detecting if requested
    if baud:
        if port.get_baudrate() != baud:
            untested("specifying the baud rate for obd.interface.elm.create()")
            port.set_baudrate(baud)
    else:
        current_baud = ELM32X.detect_baudrate(port)
        if not current_baud:
            raise InterfaceError(
                "Unable to connect to ELM; does it have power?")
    # Query the interface for its identity
    identifier = ELM32X._at_cmd(port, "ATI")
    if identifier.startswith("ATI\r"): identifier = identifier[4:]
    debug(identifier)
    chip_identifier, chip_version = identifier.split(" ")

    # Check for extended command set
    extended = ELM32X._at_cmd(port, "STI")
    if extended.startswith("STI\r"): extended = extended[4:]
    if extended != "?":
        untested(extended)
        chip_identifier, chip_version = extended.rsplit(" ", 1)

    # Create an instance of the appropriate ELM32X subclass
    try:
        elm_class = _classes[chip_identifier]
    except KeyError as e:
        untested("unknown ELM response to ATI")
        raise InterfaceError("Unknown response to ATI: %r" % identifier)
    interface = elm_class(port, chip_identifier, callback=callback)

    debug(
        "%s detected on port %s at %d baud" %
        (chip_identifier, interface.port.name, interface.port.get_baudrate()))
    return interface
Пример #7
0
    def search_for_protocol(self):
        """Perform a robust search for the vehicle's protocol and
        initiate a communication session with the vehicle's ECU,
        returning the session protocol if successful.

        This may take many seconds, as some protocols require a
        delay after a failed test.  To mitigate this, the specified
        callback function will be called before trying each
        supported protocol.
        
        Raises an exception if unable to determine the protocol
        and establish the connection.
        """
        search_order = [
            [obd.protocol.PWM(), 1.0],
            [obd.protocol.VPW(), 0.0],
            [obd.protocol.ISO9141_2(), 5.0],
            [obd.protocol.ISO14230_4("5BAUD"), 5.0],
            [obd.protocol.ISO14230_4("FAST"), 0.0],
            [obd.protocol.ISO15765_4(id_length=11, baud=500000), 0.0],
            [obd.protocol.ISO15765_4(id_length=29, baud=500000), 0.0],
            [obd.protocol.ISO15765_4(id_length=11, baud=250000), 0.0],
            [obd.protocol.ISO15765_4(id_length=29, baud=250000), 0.0],
        ]
        supported_protocols = self.get_supported_protocols()

        for protocol, delay in search_order:
            if protocol not in supported_protocols:
                continue
            self._status_callback("Trying %s protocol..." % str(protocol))
            self.set_protocol(protocol)
            try:
                self.connect_to_vehicle()
                break
            except obd.exception.ConnectionError as e:
                debug("%s, delaying %f" % (str(e), delay))
                time.sleep(delay)
        else:
            raise obd.exception.ProtocolError(
                "unable to determine vehicle protocol")

        return self.get_protocol()
Пример #8
0
    def search_for_protocol(self):
        """Perform a robust search for the vehicle's protocol and
        initiate a communication session with the vehicle's ECU,
        returning the session protocol if successful.

        This may take many seconds, as some protocols require a
        delay after a failed test.  To mitigate this, the specified
        callback function will be called before trying each
        supported protocol.
        
        Raises an exception if unable to determine the protocol
        and establish the connection.
        """
        search_order = [
            [obd.protocol.PWM(), 1.0],
            [obd.protocol.VPW(), 0.0],
            [obd.protocol.ISO9141_2(), 5.0],
            [obd.protocol.ISO14230_4("5BAUD"), 5.0],
            [obd.protocol.ISO14230_4("FAST"), 0.0],
            [obd.protocol.ISO15765_4(id_length=11, baud=500000), 0.0],
            [obd.protocol.ISO15765_4(id_length=29, baud=500000), 0.0],
            [obd.protocol.ISO15765_4(id_length=11, baud=250000), 0.0],
            [obd.protocol.ISO15765_4(id_length=29, baud=250000), 0.0],
            ]
        supported_protocols = self.get_supported_protocols()

        for protocol, delay in search_order:
            if protocol not in supported_protocols:
                continue
            self._status_callback("Trying %s protocol..." % str(protocol))
            self.set_protocol(protocol)
            try:
                self.connect_to_vehicle()
                break
            except obd.exception.ConnectionError as e:
                debug("%s, delaying %f" % (str(e), delay))
                time.sleep(delay)
        else:
            raise obd.exception.ProtocolError("unable to determine vehicle protocol")

        return self.get_protocol()
Пример #9
0
    def _process_obd_response(self, raw_frames):
        """Reassemble a list of raw frames into complete BusMessages,
        assuming that the list of raw frames constitutes a complete
        response.
        """
        # When we get an OBD response from the interface, we assume that it's
        # basically complete, having taken into account any relevant timeouts
        for frame in raw_frames:
            self._received_obd_frame(frame)
        self._flush_frames()
        result = []
        try:
            while True:
                bus_message = self._complete_messages.get(False)
                result.append(bus_message)
        except Queue.Empty as e:
            # The queue is exhausted, which just means we're done.
            pass

        debug([str(r) for r in result])
        return result
Пример #10
0
    def _process_obd_response(self, raw_frames):
        """Reassemble a list of raw frames into complete BusMessages,
        assuming that the list of raw frames constitutes a complete
        response.
        """
        # When we get an OBD response from the interface, we assume that it's
        # basically complete, having taken into account any relevant timeouts
        for frame in raw_frames:
            self._received_obd_frame(frame)
        self._flush_frames()
        result = []
        try:
            while True:
                bus_message = self._complete_messages.get(False)
                result.append(bus_message)
        except Queue.Empty as e:
            # The queue is exhausted, which just means we're done.
            pass

        debug([str(r) for r in result])
        return result
Пример #11
0
def enumerate(callback=None):
    """Return a list of attached ELM32x OBD-II interfaces, each
    of which is an instance of the appropriate ELM32X subclass.
    
    callback -- the callback function to pass to the initializer
    """
    interfaces = []
    ports = obd.serialport.SerialPort.enumerate()
    for port in ports:
        try:
            serialport = obd.serialport.SerialPort(port)
            baud = ELM32X.detect_baudrate(serialport)
            if baud:
                interface = create(serialport, callback, baud)
                interfaces.append(interface)
        except serial.serialutil.SerialException as e:
            raise InterfaceError(str(e))
        except InterfaceError as e:
            pass
        except Exception as e:
            debug(e)
    return interfaces
Пример #12
0
def enumerate(callback=None):
    """Return a list of attached ELM32x OBD-II interfaces, each
    of which is an instance of the appropriate ELM32X subclass.
    
    callback -- the callback function to pass to the initializer
    """
    interfaces = []
    ports = obd.serialport.SerialPort.enumerate()
    for port in ports:
        try:
            serialport = obd.serialport.SerialPort(port)
            baud = ELM32X.detect_baudrate(serialport)
            if baud:
                interface = create(serialport, callback, baud)
                interfaces.append(interface)
        except serial.serialutil.SerialException as e:
            raise InterfaceError(str(e))
        except InterfaceError as e:
            pass
        except Exception as e:
            debug(e)
    return interfaces
Пример #13
0
    def connect_to_vehicle(self):
        """Initiate a communication session with the vehicle's ECU
        and return the session protocol.

        This may take several seconds, particularly if automatic protocol
        detection is being used.  Where possible, the specified callback
        function will be used to provide status updates.
        
        Raises an exception if unable to establish the connection.

        On an ELM32x interface, this sends an initial OBD command to
        initiate the connection.
        """
        self._current_status = ""
        self.open()
        if self.connected_to_vehicle:
            raise CommandNotSupported("Already connected to vehicle")
        self._protocol_response = None
        
        self._status_callback("Connecting to vehicle...")
        self._write("0100\r")  # must be supported by all OBD-II vehicles
        self._set_timeout(Interface.OBD_REQUEST_TIMEOUT)

        # Read a complete line, allowing for incremental status updates
        line = ""
        status_line = False
        while not line.endswith("\r"):
            try:
                line += self._read_until_string("")
            except obd.exception.ReadTimeout as e:
                raise InterfaceError(raw=line+e.response)

            if not status_line:
                if line.startswith("SEARCHING..."):
                    status_line = True
                    self._status_callback("Searching for protocol...")
                elif line.startswith("BUS INIT: "):
                    status_line = True
                    self._status_callback("Initializing bus...")

            if status_line and line == "SEARCHING...\r":
                # The subsequent line will either be the error message
                # or the OBD response, so eat this line and keep going.
                status_line = False
                line = ""
            continue

        # Handle connection failures
        line = line[:-1] # strip \r
        try:
            if line.startswith("STOPPED"):
                raise obd.exception.InterfaceBusy(line)
            if line.endswith("UNABLE TO CONNECT") or line.endswith("ERROR"):
                raise obd.exception.ConnectionError(raw=line)
            if line.startswith("BUS INIT: ") and not line.endswith("OK"):
                raise obd.exception.ConnectionError(raw=line)
            if line == "NO DATA":
                raise obd.exception.ConnectionError(raw=line) # probably not SAE J1850
        except obd.exception.OBDException as e:
            self._read_until_prompt() # swallow the rest of the response
            raise e
        line += "\r" # re-add \r
    
        # Read the actual OBD response
        if status_line: line = ""  # swallow any status line
        lines = self._read_response(previous_data=line)
        debug("result: " + str(lines))

        # Determine and verify the protocol established
        self.connected_to_vehicle = True
        self.vehicle_protocol = self.get_protocol()

        # Process the response to make sure we got valid data
        raw_frames = self._message_bytes_from_ascii(lines)
        self._process_obd_response(raw_frames)

        # Return the actual protocol established
        return self.vehicle_protocol
Пример #14
0
 def __init__(self, message="Timeout", response=None):
     OBDException.__init__(self, message)
     self.response = response
     debug("response=%r" % response)
     return
Пример #15
0
 def __init__(self, message, raw=None):
     if raw:
         message = "%s (%s)" % (message, repr(raw))
     Exception.__init__(self, message)
     debug("%s: %s" % (type(self), message))
     return
Пример #16
0
    def connect_to_vehicle(self):
        """Initiate a communication session with the vehicle's ECU
        and return the session protocol.

        This may take several seconds, particularly if automatic protocol
        detection is being used.  Where possible, the specified callback
        function will be used to provide status updates.
        
        Raises an exception if unable to establish the connection.

        On an ELM32x interface, this sends an initial OBD command to
        initiate the connection.
        """
        self._current_status = ""
        self.open()
        if self.connected_to_vehicle:
            raise CommandNotSupported("Already connected to vehicle")
        self._protocol_response = None

        self._status_callback("Connecting to vehicle...")
        self._write("0100\r")  # must be supported by all OBD-II vehicles
        self._set_timeout(Interface.OBD_REQUEST_TIMEOUT)

        # Read a complete line, allowing for incremental status updates
        line = ""
        status_line = False
        while not line.endswith("\r"):
            try:
                line += self._read_until_string("")
            except obd.exception.ReadTimeout as e:
                raise InterfaceError(raw=line + e.response)

            if not status_line:
                if line.startswith("SEARCHING..."):
                    status_line = True
                    self._status_callback("Searching for protocol...")
                elif line.startswith("BUS INIT: "):
                    status_line = True
                    self._status_callback("Initializing bus...")

            if status_line and line == "SEARCHING...\r":
                # The subsequent line will either be the error message
                # or the OBD response, so eat this line and keep going.
                status_line = False
                line = ""
            continue

        # Handle connection failures
        line = line[:-1]  # strip \r
        try:
            if line.startswith("STOPPED"):
                raise obd.exception.InterfaceBusy(line)
            if line.endswith("UNABLE TO CONNECT") or line.endswith("ERROR"):
                raise obd.exception.ConnectionError(raw=line)
            if line.startswith("BUS INIT: ") and not line.endswith("OK"):
                raise obd.exception.ConnectionError(raw=line)
            if line == "NO DATA":
                raise obd.exception.ConnectionError(
                    raw=line)  # probably not SAE J1850
        except obd.exception.OBDException as e:
            self._read_until_prompt()  # swallow the rest of the response
            raise e
        line += "\r"  # re-add \r

        # Read the actual OBD response
        if status_line: line = ""  # swallow any status line
        lines = self._read_response(previous_data=line)
        debug("result: " + str(lines))

        # Determine and verify the protocol established
        self.connected_to_vehicle = True
        self.vehicle_protocol = self.get_protocol()

        # Process the response to make sure we got valid data
        raw_frames = self._message_bytes_from_ascii(lines)
        self._process_obd_response(raw_frames)

        # Return the actual protocol established
        return self.vehicle_protocol