Exemplo n.º 1
0
 def __init__(self, id):
     untested("ELM-specific exception")
     InterfaceError.__init__(
         self,
         message="Internal ELM error; contact interface vendor",
         raw=id)
     return
Exemplo n.º 2
0
    def get_protocol(self):
        """Return the current protocol being used in communication with the
        vehicle.

        Raises an exception if not connected with a vehicle.
        """
        if not self.connected_to_vehicle:
            raise CommandNotSupported("Not connected to vehicle")
        response = self.at_cmd("ATDPN")
        # suppress any "automatic" prefix
        if len(response) > 1 and response.startswith("A"):
            response = response[1:]
        # get the protocol object identified by the response
        try:
            protocol = self._supported_protocols[response]
        except KeyError as e:
            untested("unknown protocol returned by ELM")
            raise InterfaceError("Unknown protocol %r" % response)
        # bark if the protocol changed out from under us
        if self._protocol_response is None:
            self._protocol_response = response
        else:
            if response != self._protocol_response:
                untested("unexpected change in protocol")
                raise InterfaceError("Protocol changed unexpectedly")
        # return a copy to prevent muddling the internal list
        return copy.copy(protocol)
Exemplo n.º 3
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
Exemplo n.º 4
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
Exemplo n.º 5
0
 def __init__(self, id):
     untested("ELM-specific exception")
     InterfaceError.__init__(self, message="Internal ELM error; contact interface vendor", raw=id)
     return
Exemplo n.º 6
0
    def set_baudrate(self, new_baud):
        """Change the baud rate between computer and ELM327 interface.

        Raises an exception if unable to change the baud rate as requested.
        """
        self.open()
        old_baud = self.port.get_baudrate()
        succeeded = False

        # Compute the divisor for the requested baud rate
        divisor = round(4000000.0 / new_baud, 0)
        if (divisor < 8 or divisor > 255):
            # Limits specified on p.46 of ELM327 datasheet
            raise ValueError("Baud rate %d out of range for ELM" % new_baud)

        # Get the AT I string (which BRD returns to confirm the new baud rate)
        identifier = self.at_cmd("ATI")

        # Send the BRD command
        command = "ATBRD %02X\r" % divisor
        self._write(command)
        self._set_timeout(ELM32X.AT_TIMEOUT)
        try:
            response = self._read_until_string(str="OK\r")
        except Timeout:
            pass

        # Check whether AT BRD is supported
        if (not response.endswith("OK\r")):
            raise CommandNotSupported("Scanner doesn't support AT BRD; " +
                                      "staying at %d" % old_baud)
        try:
            # Set the port to the new baud rate
            self.port.set_baudrate(new_baud)

            # Check whether the test transmission came through OK
            self._set_timeout(0.1)
            try:
                response = self._read_until_string(str="\r")
            except Timeout:
                pass
            if (response != identifier):
                raise InterfaceError("Test of %d baud failed" % new_baud)

            # If test transmission was OK, send a CR to confirm the change...
            self._write("\r")

            # ...and check for the scanner's confirmation
            self._set_timeout(0.1)
            try:
                response = self._read_until_string(str=ELM32X.PROMPT)
            except Timeout:
                pass
            if (not response.endswith("OK\r\r>")):
                raise InterfaceError("Scanner failed to confirm %d baud" %
                                     new_baud)
        except InterfaceError as e:
            self.port.set_baudrate(self, old_baud)
            raise InterfaceError(str(e) + ("; reverted to %d" % old_baud))

        return
Exemplo n.º 7
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