Vehicle communication
Communication with ECUs in a vehicle can take place in several different ways, depending on the protocol used. This page lists examples of different ways in which such communication can take place. See the documentation page on vehicle communication for more information.
ISO-TP communication
The example below demonstrates how a VIN can be read from a vehicle using the ISO-TP protocol. This is done by configuring the required bus, specifying an ECU, and starting a stream to communicate with that ECU.
from openobd import *
# Start an openOBD session on a ticket
openobd = OpenOBD()
openobd_session = openobd.start_session_on_ticket(TICKET_ID) # The ticket ID needs to be filled in (as a string)
# Start the SessionTokenHandler to ensure the openOBD session remains authenticated
SessionTokenHandler(openobd_session)
# Define two buses with the ISO-TP protocol
print("Configuring buses...")
bus_configs = [
(BusConfiguration(bus_name="bus_6_14",
can_bus=CanBus(pin_plus=6, pin_min=14, can_protocol=CanProtocol.CAN_PROTOCOL_ISOTP,
can_bit_rate=CanBitRate.CAN_BIT_RATE_500,
transceiver=TransceiverSpeed.TRANSCEIVER_SPEED_HIGH))),
(BusConfiguration(bus_name="bus_3_11",
can_bus=CanBus(pin_plus=3, pin_min=11, can_protocol=CanProtocol.CAN_PROTOCOL_ISOTP,
can_bit_rate=CanBitRate.CAN_BIT_RATE_500,
transceiver=TransceiverSpeed.TRANSCEIVER_SPEED_HIGH)))
]
# Open a configureBus stream, send the bus configurations, and close the stream
bus_config_stream = StreamHandler(openobd_session.configure_bus)
bus_config_stream.send_and_close(bus_configs)
print("Buses have been configured.")
# Define the engine control module, which is present on bus pins 6, 14, with CAN IDs 7E0-7E8, and turn on frame padding
ecm_channel = IsotpChannel(bus_name="bus_6_14", request_id=0x7E0, response_id=0x7E8, padding=Padding.PADDING_ENABLED)
# Start a stream to communicate with the engine
ecm = IsotpSocket(openobd_session, ecm_channel)
# Start an extended diagnostic session, with silent set to True so no ResponseException will be raised on an incorrect response
print("Sending 1003...")
response = ecm.request("1003", silent=True)
print(f"Response: {response}")
try:
# Request the VIN from the engine, attempting it up to 2 times, each attempt waiting a maximum of 5 seconds for a response
print("Sending 22F190...")
response = ecm.request("22F190", tries=2, timeout=5)
print(f"Response: {response}")
# Decode and print the VIN, ignoring the first 3 bytes of the response
vin = bytes.fromhex(response[6:]).decode("utf-8")
print(f"VIN: {vin}")
# Catch any exceptions that are raised because of the ECU returning a negative response, or not responding at all
except ResponseException as e:
print(f"Request failed: {e}")
finally:
# Close the stream regardless of the result
ecm.stop_stream()
# Close the session with a successful result
result = ServiceResult(result=[Result.RESULT_SUCCESS])
openobd_session.finish(result)
CAN frame communication
The frames protocol can be used when it is desired to have direct control over the frames that are being exchanged with an ECU. Below is an example in which a VIN is read from an ECU while using the frames protocol. It demonstrates how to set up a frames stream and how to handle multi-frame messages.
from openobd import *
# Start an openOBD session on a ticket
openobd = OpenOBD()
openobd_session = openobd.start_session_on_ticket(TICKET_ID) # The ticket ID needs to be filled in (as a string)
# Start the SessionTokenHandler to ensure the openOBD session remains authenticated
SessionTokenHandler(openobd_session)
# Define a bus with the frames protocol
print("Configuring buses...")
bus_configs = [
(BusConfiguration(bus_name="bus_6_14",
can_bus=CanBus(pin_plus=6, pin_min=14, can_protocol=CanProtocol.CAN_PROTOCOL_FRAMES,
can_bit_rate=CanBitRate.CAN_BIT_RATE_500,
transceiver=TransceiverSpeed.TRANSCEIVER_SPEED_HIGH)))
]
# Open a configureBus stream, send the bus configurations, and close the stream
StreamHandler(openobd_session.configure_bus).send_and_close(bus_configs)
print("Buses have been configured.")
# Define the engine control module, which is present on bus pins 6, 14, with CAN IDs 7E0-7E8
ecm_channel = RawChannel(bus_name="bus_6_14", request_id=0x7E0, response_id=0x7E8)
# Start a stream to communicate with the engine
ecm = RawSocket(openobd_session, ecm_channel)
# Start an extended diagnostic session
try:
print("Sending 1003...")
# Send the entire 8-byte long CAN frame to the engine
ecm.send("0210030000000000")
# Wait max 5 seconds for a response
response = ecm.receive(timeout=5)
print(f"Received frame: {response}")
except OpenOBDStreamTimeoutException:
print("No frame received.")
# Send a "tester present" message to the engine control module to keep its diagnostic session active
ecm.send("023E800000000000")
# Request the VIN, which does not fit in a single frame and therefore requires multiple frames to be received
try:
print("Sending 22F190...")
ecm.send("0322F19000000000")
response = ecm.receive() # Default timeout is 10 seconds
print(f"Received frame: {response}")
# Determine how long the response will be using info from the first frame
total_bytes = int(response[1:4], 16)
received_payload = response[4:]
received_bytes = 6
# Send a flow control frame to indicate that the engine should continue sending frames
ecm.send("3000000000000000")
# Continue receiving frames until all the bytes have been received
while received_bytes < total_bytes:
response = ecm.receive(timeout=2)
print(f"Received frame: {response}")
received_payload += response[2:]
received_bytes += 7
# Decode and print the VIN, ignoring the first 3 bytes of the response
vin = bytes.fromhex(received_payload[6:]).decode("utf-8")
print(f"VIN: {vin}")
except OpenOBDStreamTimeoutException:
print("No frame received.")
# Close the stream
ecm.stop_stream()
# Close the session with a successful result
result = ServiceResult(result=[Result.RESULT_SUCCESS])
openobd_session.finish(result)
K-Line communication
Below is an example on how to read the DTCs from an ECU using K-Line. It creates a K-Line bus and channel, and opens a stream for communication with the ECU.
from openobd import *
# Start an openOBD session on a ticket
openobd = OpenOBD()
openobd_session = openobd.start_session_on_ticket(TICKET_ID) # The ticket ID needs to be filled in (as a string)
# Start the SessionTokenHandler to ensure the openOBD session remains authenticated
SessionTokenHandler(openobd_session)
# Define a bus with the K-Line protocol
print("Configuring buses...")
bus_configs = [
(BusConfiguration(bus_name="bus_7",
kline_bus=KlineBus(pin=7, kline_protocol=KlineProtocol.KLINE_PROTOCOL_ISO14230_FAST,
kline_bit_rate=KlineBitRate.KLINE_BIT_RATE_10400))),
]
# Open a configureBus stream, send the bus configurations, and close the stream
StreamHandler(openobd_session.configure_bus).send_and_close(bus_configs)
print("Buses have been configured.")
# Define the immobilizer, assuming it is present on bus pin 7, with ECU ID C1
immo_channel = KlineChannel(bus_name="bus_7", ecu_id=0xC1)
# Start a stream to communicate with the immobilizer
immo = KlineSocket(openobd_session, immo_channel)
# Start a default diagnostic session, with silent set to True so no ResponseException will be raised on an incorrect response
print("Sending 81...")
response = immo.request("81", silent=True)
print(f"Response: {response}")
try:
# Request the DTCs stored in the ECU
print("Sending 17FF00...")
response = immo.request("17FF00")
print(f"Response: {response}")
# Print the amount of DTCs present in the ECU
dtc_count = int(response[2:4], 16)
if dtc_count == 0:
print("No DTCs present in the ECU.")
else:
print(f"Found {dtc_count} DTCs: {response[4:]}")
# Catch any exceptions that are raised because of the ECU returning a negative response, or not responding at all
except ResponseException as e:
print(f"Request failed: {e}")
finally:
# Close the stream regardless of the result
immo.stop_stream()
# Close the session with a successful result
result = ServiceResult(result=[Result.RESULT_SUCCESS])
openobd_session.finish(result)
Terminal15 communication
Below is an example on how to read the state of the ignition switch (Terminal15). It creates a Terminal15 bus, and opens a stream to listen for updates.
from openobd import *
# Start an openOBD session on a ticket
openobd = OpenOBD()
openobd_session = openobd.start_session_on_ticket(TICKET_ID) # The ticket ID needs to be filled in (as a string)
# Start the SessionTokenHandler to ensure the openOBD session remains authenticated
SessionTokenHandler(openobd_session)
# Define a bus with the Terminal15 protocol for listening on pin 1
print("Configuring buses...")
bus_configs = [
(BusConfiguration(bus_name="terminal15",
terminal15_bus=Terminal15Bus(pin=1)))
]
# Open a configureBus stream, send the bus configurations, and close the stream
StreamHandler(openobd_session.configure_bus).send_and_close(bus_configs)
print("Buses have been configured.")
# Start communication for terminal15 signals on pin 1
print(" [i] Starting Terminal15 communication")
socket = Terminal15Socket(session, timeout = 10)
# Keep listening to the terminal15 signal for 30 seconds and show the state updates
try:
current_time = time.time()
while current_time + 30 > time.time():
current_state = socket.receive()
print(f" [i] Current terminal15 state: {current_state}")
except ResponseException as e:
print(f"Request failed: {e}")
finally:
socket.stop_stream()
print() # Empty line
# Close the session with a successful result
result = ServiceResult(result=[Result.RESULT_SUCCESS])
openobd_session.finish(result)