Vehicle communication
Communication with ECUs in a vehicle can take place in several different ways, depending on the communication protocol used.
This page lists examples of different communication protocols that are supported by openOBD.
These examples use the openobd Python module. For more extensive background information on implementing these
examples in a different programming language, please see the reference documentation on vehicle communication.
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.
python -m openobd run --file academy/vehicle_communication/isotp.py --ticket <TICKET_NR>
from openobd import *
class IsotpCommunicationExample(OpenOBDFunction):
def run(self):
# 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)))
]
# Set the bus configuration
set_bus_configuration(self.openobd_session, 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 an IsotpSocket to communicate with the engine, using the 'with' statement ensures that the stream will be neatly
# closed after this block finishes
with IsotpSocket(self.openobd_session, ecm_channel) as ecm:
# 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}")
# Finish the function with a successful result
self.result = ServiceResult(result=[Result.RESULT_SUCCESS])
TP 2.0 communication
The example below demonstrates how a VIN can be read from a vehicle using the TP 2.0 protocol. This is done by configuring the required bus, specifying a communication channel with a logical address, and starting a stream to communicate with that ECU.
python -m openobd run --file academy/vehicle_communication/tp20.py --ticket <TICKET_NR>
from openobd import *
class Tp20CommunicationExample(OpenOBDFunction):
def run(self):
# Define a bus with the 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_TP20,
can_bit_rate=CanBitRate.CAN_BIT_RATE_500,
transceiver=TransceiverSpeed.TRANSCEIVER_SPEED_HIGH)))
]
# Set the bus configuration
set_bus_configuration(self.openobd_session, bus_configs)
print("Buses have been configured.")
# Define the engine control module, which is present on bus pins 6, 14 with the logical address 0x1F
ecm_channel = Tp20Channel(bus_name="bus_6_14", logical_address=0x1F)
# Start an Tp20Socket to communicate with the engine, using the 'with' statement ensures that the stream will be neatly
# closed after this block finishes
with Tp20Socket(self.openobd_session, ecm_channel) as ecm:
# 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)
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}")
# Finish the function with a successful result
self.result = ServiceResult(result=[Result.RESULT_SUCCESS])
Raw CAN frame communication
The raw 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.
python -m openobd run --file academy/vehicle_communication/raw.py --ticket <TICKET_NR>
from openobd import *
class RawCommunicationExample(OpenOBDFunction):
def run(self):
# 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)))
]
# Set the bus configuration
set_bus_configuration(self.openobd_session, 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 an RawSocket to communicate with the engine, using the 'with' statement ensures that the stream will be neatly
# closed after this block finishes
with RawSocket(self.openobd_session, ecm_channel) as ecm:
# 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.")
# Finish the function with a successful result
self.result = ServiceResult(result=[Result.RESULT_SUCCESS])
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.
python -m openobd run --file academy/vehicle_communication/kline.py --ticket <TICKET_NR>
from openobd import *
class KlineCommunicationExample(OpenOBDFunction):
def run(self):
# 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))),
]
# Set the bus configuration
set_bus_configuration(self.openobd_session, 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 KlineSocket to communicate with the engine, using the 'with' statement ensures that the stream will be neatly
# closed after this block finishes
with KlineSocket(self.openobd_session, immo_channel) as immo:
# 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}")
# Finish the function with a successful result
self.result = ServiceResult(result=[Result.RESULT_SUCCESS])
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.
python -m openobd run --file academy/vehicle_communication/terminal15.py --ticket <TICKET_NR>
from openobd import *
class Terminal15CommunicationExample(OpenOBDFunction):
def run(self):
# 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)))
]
# Set the bus configuration
set_bus_configuration(self.openobd_session, bus_configs)
print("Buses have been configured.")
# Start an Terminal15Socket to communicate with the engine, using the 'with' statement ensures that the stream will be neatly
# closed after this block finishes
with Terminal15Socket(self.openobd_session, timeout=10) as terminal15_state:
# 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 = terminal15_state.receive()
print(f" [i] Current terminal15 state: {current_state}")
except OpenOBDStreamTimeoutException as e:
print(f"Request failed: {e}")
# Finish the function with a successful result
self.result = ServiceResult(result=[Result.RESULT_SUCCESS])
DoIP communication
Below is an example on how to read the VIN from an ECU using DoIP. The example creates a DoIP bus and channel, and opens a stream for communication with the ECU.
python -m openobd run --file academy/vehicle_communication/doip.py --ticket <TICKET_NR>
from openobd import *
class DoipCommunicationExample(OpenOBDFunction):
def run(self):
# Define a DoIP bus and specify the network configuration used for Volvo
print("Configuring buses...")
bus_configs = [
(BusConfiguration(bus_name="bus_1",
doip_bus=DoipBus(doip_option=DoipOption.DOIP_OPTION_1,
doip_network_configuration=DoipNetworkConfiguration.DOIP_NETWORK_CONFIGURATION_2))),
]
# Set the bus configuration
# Note: this can take up to 90 seconds for DoIP
set_bus_configuration(self.openobd_session, bus_configs)
print("Buses have been configured.")
# Define the instrument panel, assuming it is present on the bus with DoIP option 1, is behind a gateway with ID 1001, and has ECU ID 1801
ipc_channel = DoipChannel(bus_name="bus_1", gateway_id=0x1001, tester_id=0xE80, ecu_id=0x1801)
# Start an DoipSocket to communicate with the engine, using the 'with' statement ensures that the stream will be neatly
# closed after this block finishes
with DoipSocket(self.openobd_session, ipc_channel) as ipc:
# Start an extended diagnostic session, with silent set to True so no ResponseException will be raised on an incorrect response
print("Sending 1003...")
response = ipc.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 = ipc.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}")
# Finish the function with a successful result
self.result = ServiceResult(result=[Result.RESULT_SUCCESS])