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
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
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
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
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
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()
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()
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
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
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
def __init__(self, message="Timeout", response=None): OBDException.__init__(self, message) self.response = response debug("response=%r" % response) return
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
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