Open Source Vehicle ECU Diagnostics and Testing Platform: University of Reading Department of Computer Science
Open Source Vehicle ECU Diagnostics and Testing Platform: University of Reading Department of Computer Science
Open Source Vehicle ECU Diagnostics and Testing Platform: University of Reading Department of Computer Science
Ashcon Mohseninia
Ashcon Mohseninia
April 29, 2021
i
Abstract
With the complexity of electronics in consumer vehicles, there currently only exists proprietary
tools produced by various OEMs to diagnose their own vehicles. Each OEM has its own tool,
and there is no easy way for a consumer to diagnose their own vehicle.
This project will explore the possibility of creating an entirely open source diagnostics
software stack that will work with all ready existing diagnostic adapters that utilize the Passthru
API (Which is used for a PC to communicate with a diagnostic adapter plugged into a vehicles
OBD-II port). Additionally, this project will also explore creating an entirely open source
Passthru API driver for an open source OBD-II adapter, whilst additionally porting the API
from Win32 only to UNIX based operating systems such as Linux and OSX, allowing for a
wider target audience compared to traditional diagnostic applications and adapters which only
target Windows.
ii
Acknowledgements
For their contributions to this project, I would like to acknowledge the following parties:
• Julian Kunkel - For his supervision of the project and consistently giving me tips on how
I can improve various aspects of this report.
• JinGen Lim https://github.com/jglim - For his efforts with reverse engineering the
CBF file format
• Collin Kidder https://github.com/collin80 - For consistent support with his due can
library used for CANBUS communication on the M2 hardware.
iii
Contents
1 Introduction 1
1.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Problem statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1 Lack of continuity or standards between OEMs diagnostic tools . . . 2
1.2.2 Proprietary diagnostic hardware . . . . . . . . . . . . . . . . . . . . . 2
1.3 Aims and objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Solution approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4.1 JSON schema designing and converting . . . . . . . . . . . . . . . . 3
1.4.2 Passthru driver creation . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4.3 Application creation . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5 Summary of contributions and achievements . . . . . . . . . . . . . . . . . . 4
1.5.1 Passthru driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5.2 JSON Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.3 Diagnostic Application (OpenVehicleDiag) . . . . . . . . . . . . . . . 5
1.6 Organization of the report . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Literature Review 6
2.1 Communication protocols found in vehicles . . . . . . . . . . . . . . . . . . . 6
2.1.1 CAN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1.2 ISO-TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1.3 LIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Diagnostic adapter hardware and APIs . . . . . . . . . . . . . . . . . . . . . 12
2.2.1 Hardware APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.2 Hardware adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3 ECU Diagnostic protocols . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3.1 OBD-II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.3.2 KWP2000 and UDS . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.4 Existing diagnostic software . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.4.1 Torque for Android (Generic OBD) . . . . . . . . . . . . . . . . . . . 18
2.4.2 Carly (Third party software) . . . . . . . . . . . . . . . . . . . . . . 18
2.4.3 Xentry (Dealer software) . . . . . . . . . . . . . . . . . . . . . . . . 18
2.5 Open Diagnostics eXchange (ODX) . . . . . . . . . . . . . . . . . . . . . . 19
2.6 The OBD-II port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.7 Comparisons to the proposed project . . . . . . . . . . . . . . . . . . . . . . 20
2.7.1 Hardware adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.7.2 Diagnostic software . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
iv
CONTENTS v
3 Methodology 22
3.1 Test setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2 Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3 JSON schema creation and CBF parsing . . . . . . . . . . . . . . . . . . . . 23
3.3.1 JSON structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3.2 Code implementation of the JSON Schema . . . . . . . . . . . . . . 27
3.3.3 Parsing Daimler CBF Files to JSON . . . . . . . . . . . . . . . . . . 29
3.4 Cross platform Passthru adapter . . . . . . . . . . . . . . . . . . . . . . . . 32
3.4.1 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4.2 Creating the driver in Rust . . . . . . . . . . . . . . . . . . . . . . . 34
3.4.3 Communication between the adapter and driver . . . . . . . . . . . . 35
3.4.4 Reading battery voltage . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.4.5 ISO-TP Communication . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.4.6 Porting the Passthru API to Linux and OSX . . . . . . . . . . . . . . 39
3.4.7 Logging activity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.4.8 Performance optimizations with CAN Interrupts . . . . . . . . . . . . 41
3.5 Diagnostic GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.5.1 Diagnostic server architecture . . . . . . . . . . . . . . . . . . . . . . 43
3.5.2 Communication server architecture . . . . . . . . . . . . . . . . . . . 43
3.5.3 Implementation of the Passthru API . . . . . . . . . . . . . . . . . . 45
3.5.4 User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.5.5 KWP2000 and UDS implementations . . . . . . . . . . . . . . . . . 53
3.5.6 Automated ECU Scanner . . . . . . . . . . . . . . . . . . . . . . . . 55
3.5.7 JSON Diagnostic session . . . . . . . . . . . . . . . . . . . . . . . . 59
3.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6 Reflection 71
Appendices 74
3.1 Mock-up of how the ECUs are connected in the test setup . . . . . . . . . . 22
3.2 UML representation of ovdECU Root object, ECUVariantDefinition and Con-
nection properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.3 UML representation of JSON Service and ECUDTC . . . . . . . . . . . . . . 24
3.4 UML representation of JSON DataFormat . . . . . . . . . . . . . . . . . . . 25
3.5 Simplified UML representation of the data structure in a CBF File . . . . . . 30
3.6 Macchina’s M2 Under the dash OBD-II module . . . . . . . . . . . . . . . . 32
3.7 Macchina M2 board layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.8 Expanded sequence diagram of communication server . . . . . . . . . . . . . 36
3.9 Voltage reading comparison between M2 (Stock and corrected) and Multimeter 37
3.10 Sequence diagram for sending ISO-TP Data to an ECU . . . . . . . . . . . . 38
3.11 Sequence diagram for receiving ISO-TP Data from an ECU . . . . . . . . . . 39
3.12 Diag servers UML overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.13 UML of ComServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.14 OpenVehicleDiag’s launcher (Passthru device enumeration) . . . . . . . . . . 49
3.15 OpenVehicleDiag’s launcher displaying Passthru error . . . . . . . . . . . . . 50
3.16 Standard CAN display (Hex) vs Binary CAN . . . . . . . . . . . . . . . . . . 53
3.17 Warning message presented to the user prior to the ECU scan . . . . . . . . 55
3.18 Listing to existing CAN traffic . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.19 Locating potential ISO-TP endpoints . . . . . . . . . . . . . . . . . . . . . . 56
3.20 Finalizing ISO-TP scan results . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.21 Scan progress for UDS compatible ECUs . . . . . . . . . . . . . . . . . . . . 58
3.22 Instrument cluster warning lights being displayed during the final stages of
ECU detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.23 Results page of ECU Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.24 JSON Session home with CRD ECU . . . . . . . . . . . . . . . . . . . . . . 60
A.1 Xentry Diagnostics establishing communication with all the ECUs within a
vehicle. During this stage of diagnostics, Xentry is trying to locate all the
ECUs on the vehicle, and checking what variation each ECU is in order to
parse their diagnostic data correctly . . . . . . . . . . . . . . . . . . . . . . 74
vii
LIST OF FIGURES viii
A.2 Xentry diagnostics with a list of all possible ECUs in the vehicle to talk to,
each in their own category . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
A.3 Obtaining advanced data from the ECU in Xentry - Querying various attributes
about the ESP ECU. The serial number of this part has been hidden. . . . . 75
A.4 Xentry showing a live ’actuation’ value of certain items the ESP ECU controls 75
A.5 Show Xentry obtaining real-time data from the CDI Engine ECU. The values
in Green are within tolerance, and values in red are outside tolerance. Black
values indicate no tolerances are specified for the value . . . . . . . . . . . . 76
A.6 Xentry showing advanced real-time data from the CDI Engine ECU. This allows
for advanced analytics of how the engine is performing. . . . . . . . . . . . . 76
A.7 More advanced real-time diagnostics with the CDI Engine ECU. This shows
the injector calibration values for the number 1 cylinder . . . . . . . . . . . . 77
A.8 OVD Home page (Dark theme) . . . . . . . . . . . . . . . . . . . . . . . . . 77
A.9 OVD Home page (Light theme) . . . . . . . . . . . . . . . . . . . . . . . . 78
A.10 OVD Can Scanner (Hex mode) . . . . . . . . . . . . . . . . . . . . . . . . . 78
A.11 OVD Can Scanner (Binary mode) . . . . . . . . . . . . . . . . . . . . . . . 78
A.12 Loading a ECU Scan save file in OVD . . . . . . . . . . . . . . . . . . . . . 79
A.13 Selected CRD ECU in OVD . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
A.14 OVD KWP2000 generic session - Home . . . . . . . . . . . . . . . . . . . . 79
A.15 OVD KWP2000 generic session - Scanning DTCs . . . . . . . . . . . . . . . 79
A.16 OVD KWP2000 generic session - Clearing DTCs . . . . . . . . . . . . . . . . 80
A.17 OVD KWP2000 generic session - Sending valid manual payload . . . . . . . 80
A.18 OVD KWP2000 generic session - Sending invalid manual payload . . . . . . 80
A.19 OVD Json session - Connected to CRD engine ECU . . . . . . . . . . . . . . 80
A.20 OVD Json session - ECU Info page . . . . . . . . . . . . . . . . . . . . . . . 81
A.21 OVD Json session - DTC page . . . . . . . . . . . . . . . . . . . . . . . . . 81
A.22 OVD Json session - DTC page with Freeze frame interpretation . . . . . . . 82
A.23 OVD Json session - Selecting function to read data from . . . . . . . . . . . 82
A.24 OVD Json session - Data presentation . . . . . . . . . . . . . . . . . . . . . 83
A.25 OVD Json session - Reading data from EGS52 transmission ECU . . . . . . . 83
List of Tables
ix
List of Abbreviations
x
Chapter 1
Introduction
In this chapter the current state of car diagnostics will be discussed, as well as a high level
overview of the project breakdown and how it will hopefully change the current state of car
diagnostics for consumers.
1.1 Background
Since the early 2000s, the automotive industry has seen an exponential increase in both the
complexity of ECUs and the number of ECUs in consumer vehicles, with modern vehicles
having more than 30 ECUs.
With increased complexity comes more points of failure. With modern ECUs being able
to register fault codes at the slightest hint of trouble, and also requiring specialist software to
calibrate them after certain mechanical parts are either replaced or modified on a vehicle.
This presents a unique problem. While DIY consumers have traditionally been able to
easily replace or modify components on their vehicles, software issues such as ECU fault codes
still require proprietary software which is only used by the OEM itself, or licensed to specific
workshops as a huge premium, and also requires proprietary multiplexer hardware to plug into
the cars OBD-II port, which is also expensive.
Currently, there are simple OBD-II applications that can only communicate with the engine
ECU in a vehicle to clear standard OBD-II error codes or read sensor data that is only outlined
by the OBD-II specification, but there is no easily available software outside of the OEM’s
own software (Which is licensed to workshops) which can diagnose all ECU’s within a vehicle,
or run more complex diagnostic routines and tests. Also, the vast majority of OEM software
currently available is only compiled for win32 (32bit Windows), and therefore is not up to date
with modern computing, and also does not run on Linux or OSX. This is primarily because
OEMs over the years have simply been adding features to their old diagnostic software, rather
than spending resources on creating a whole new platform for more modern vehicles.
Therefore, in this project, the possibility and process of creating such an application that
allows for communicating with all ECUs within a vehicle, and to run more advanced diagnostic
functions on them, without the OEM’s own software will be explored. This will also include
writing a open source driver for Macchina’s M2 OBD-II module to turn it into a diagnostic
adapter using the J2534 / Passthru API, which can be used by any application utilizing the
protocol, including some OEM software (Such as Daimler’s Xentry Passthru diagnostic suite).
Additionally, the application and J2534 API will be ported to both 64bit versions of Linux and
OSX unofficially, allowing for a much wider target audience of the system, since individuals
would no longer be limited to just older versions of Windows.
Another objective of this project is to create a simple, easy to use database format in
1
CHAPTER 1. INTRODUCTION 2
JSON. Traditionally, OEM software uses proprietary binary based file formats to describe how
the software communicates with ECUs within a vehicle, as well as how to interpret the ECUs
responses.
1. Windows only support. Since these API’s are designed for diagnostic software, and those
are only designed for windows, there is currently no known diagnostic API that includes
support for Linux or OSX.
2. Closed source. Although some of the API documentation is made public, vendors of the
diagnostic multiplexers that utilize either API creates proprietary hardware with closed-
source controller firmware, and sells the adapters at a premium, making it hard for the
average consumer to easily attain one. There are chinese ’clone’ adapters that can be
purchased on ebay for a cheap price, however these tend to not work or will encounter
massive stability issues, so should never be trusted to work reliably.
• Build a cross-platform, Graphical ECU diagnostic application (Using the J2534 passthru
API)
• Write a custom J2534 driver for Macchina’s1 M2 Under the dash OBD-II module,
allowing it to work on all 3 operating systems.
1
macchina.cc
CHAPTER 1. INTRODUCTION 3
• Define a JSON schema for describing the capabilities, fault codes, and diagnostic func-
tions that can be ran on an ECU, and make it a viable replacement to proprietary data
formats.
NOTE: ECU firmware Flashing or updating will not be part of this project. This is due to
liability concerns of leaving an individuals ECU in a bricked state, and also the difficulty in
locating a legitamate software version for an ECU.
Objectives: To achieve the aims, the project has the following objectives:
• Read and clear non standard (OBD-II) ECU error codes from a test ECU, and a real
vehicle
• Show that the custom J2534 compatible adapter works with real OEM software that
uses the J2534 API
1. ECU software version names (An ECU can have different software versions)
4. List of all error codes, as well as a readable description of the error codes
5. Interpretation data for converting an ECU response packet into human readable text
Important. Since this will extract data from Daimler’s own CBF files, there are some social
and legal issues to account for. Contained within the CBF files is data related to SCN coding.
SCN Coding (Software Calibration number coding), is a way to write a coding string to an
ECU in order to enable or disable features on it. The CBF files contain data regarding which
regions in the ECU’s EEPROM relate to which features. Because SCN coding is something
that Daimler sees as its own intellectual property, and charges a heavy fee for feature unlocking
on their cars, this will NOT be something that is going to be extracted from the CBF files,
and there will be no referencing to SCN regions in the extractor codebase either. The only
things that will be extracted from the CBF files will be diagnostic routines, ECU identification
data, and interpretation data.
CHAPTER 1. INTRODUCTION 4
1. Define a new standard for storing J2534 configuration data on Linux and OSX, since
the J2534 API officially only supports win32 (32bit Windows).
3. Create a Rust library with the necessary exported J2534 functions such that any appli-
cation can use the library and therefore adapter to talk to a vehicle
4. Create C++ firmware for the adapter for managing physical communication links be-
tween the OBD-II port and ECUs in the vehicle, and listen for command requests from
the PC and sending data back to the PC.
1. Create an application that can utilize the J2534 API to communicate with a vehicle.
2. Create an abstraction layer for future use, which allows for more hardware to be utilized
by OpenVehicleDiag other than J2534. (Examples: SocketCAN, D-PDU).
3. Create a useful GUI for data logging of ECUs based on the data found in the JSON
schema
4. Allow for basic commands to be sent to any ECU in a vehicle using KWP2000 or UDS.
This should allow for reading and clearing error codes from the vast majority of vehicle
ECUs, even if the vehicle does not have any JSON created for it.
5. Allow for a user to see raw data on their vehicles CAN Network (Targeted at individuals
who wish to reverse-engineer their vehicles’ CAN network)
6. Based on the work done by [Nils Weiss, Sebastian Renner, Jürgen Mottok, Václav
Matoušek (n.d.)], create a intuative user interface which can exploit the ISO-TP protocol
to scan for all UDS or KWP2000 ECU’s in any unknown vehicle.
which costs a fraction of the commercial adapters. This report will even show that the
adapter works with Commercial software from Daimler. Also, this report will show that the
SAE J2534 API can indeed be ported to other operating systems, which in turn makes it easier
for other operating system users to utilize the API with custom Diagnostic software such as
OpenVehicleDiag.
Literature Review
In this chapter, existing vehicle communication protocols, diagnostic protocols, hardware APIs
and diagnostic software will be looked at, with a comparison at the end comparing current
diagnostic software and hardware to what is proposed for this project.
2.1.1 CAN
CAN / CANBUS is a high speed transport network used for ECU communication. It consists
of 2 separate ISO specifications:
Both CAN Specifications work on similar principles, except with different electrical properties.
CAN Networks transmit CAN Packets. These are data packets containing up to 8 bytes of
data, as well as a 11 or 29bit Identifier ID (Depending on if the CAN Network uses Standard
of Extended addressing). There is also extra data in each CAN Frame (Bit stuffing and CRC
Checks), however these extra bits are never exposed to the ECUs CPU as the CAN Controller
deals with validating the CRC checks of the CAN Frame.
Figure 2.1: Bit layout of both Standard and Extended CAN Frames
Most commonly, the following data fields are exposed the ECU’s CPU:
6
CHAPTER 2. LITERATURE REVIEW 7
• CAN ID - This is the Unique identifier of the ECU subsystem which transmitted the
frame (Some ECUs transmit multiple CAN Frames with different IDs)
• CAN DLC - The amount of bytes in the data portion of the CAN Frame
• CAN Data - 0-8 bytes of data contained within the CAN Frame
• RTR - Remote frame. This is sometimes used by an ECU to request data from another
ECU. If this value is 1, then the ECU is requesting data from another ECU, if the value
is 0, then there is data within the frame from the requested ECU.
• High-Speed CAN ISO11898-2 - Both CAN wires are terminated with a 120Ω resistor
at each node on the bus. The recessive voltage of the CAN Network is 2.5V , with
the dominant voltage being approximately 3.5V for CAN-H, whilst being approximately
1.5V for CAN-L.
• Low-Speed CAN ISO11898-3 - CAN wires are not terminated with a resistor, however
the overall resistance between both wires over the entire bus should not exceed 100Ω.
The recessive voltage of the CAN Network is approximately 0V for CAN-H and 5V
for CAN-L, whilst the dominant voltages are approximately 5V for CAN-H and 0V for
CAN-L.
Both CAN Network types require that a logical ’1’ is the recessive voltage, and a logical ’0’
is the dominant voltage. The logical state of the CAN Network is calculated by applying
a logical AND to both the CAN-H and CAN-L wires. Components on the CAN Networks
(CAN Transceiver chips) are designed to handle anywhere between −27V to +40V without
sustaining any damage. If both CAN Wires are not in the same logical state at the same time
EG: CAN-H being in a dominant state whilst CAN-L is in a recessive state, all transmission
on the network will stop immediately as all the CAN Transceiver chips on the network as
detected that either a wire may be shorted to ground, or that a wire may be shorted to an
ECU’s power supply, and therefore the network is in an unstable state.
This is not always the case however, as some CAN Networks based on ISO11898-3 can
actually run in single wire mode, where the transceivers discard the erroneous wire voltage,
and only use the voltage provided by the ’good’ CAN wire.
Table 2.1 shows how an ECU with a lower CAN ID (0x0005) can stop another ECU with a
larger CAN ID of (0x000A) from transmitting. As soon as ECU transmitting the higher CAN
ID detects the CAN networks logical state does not match that of the bit it just sent, it stops
sending, and the data ECU 1 sent is not impacted. This is all handles by the CAN Transceiver
chip, so the ECU’s Processor is not occupied with checking the CAN network state.
2.1.2 ISO-TP
ISO-TP (ISO15765-2) is a transport layer protocol that runs over a CAN Network, allowing
for multiple ECUs to exchange up to 4096 bytes using multiple structured CAN Frames.
The ISO-TP Standard defines the following 4 frame types, which are denoted by the first
nibble (Half byte) in the CAN Frame, also known as the PCI byte:
In order to identify lost data, or out of order CAN frames during multi frame transmissions,
the PCI byte of the consecutive frame is used as a counter. The counter starts with a PCI of
0x21, then increases after every consecutive frame is sent until 0x2F, before wrapping round
to 0x20 and counting up again.
Below is an ISO-TP exchange traced from the interior CAN Network of my Mercedes
W203 C class. This exchange occurred between the Radio and Instrument cluster, where the
Radio is telling the instrument cluster what text to display on the Radio page of its LCD. Red
indicated bytes that are for the ISO-TP protocol
CAN ID CAN Data ASCII
0x01A4 10 12 03 26 01 00 01 0B ........
0x01D0 30 08 28 00 00 00 00 00 0.(.....
0x01A4 21 10 41 55 44 49 4F 20 !.AUDIO.
0x01A4 22 4F 46 46 00 C4 00 0B "OFF....
In this trace, we can see the Radio has a CAN ID of 0x01A4, and sent a 18 byte payload to
the instrument cluster. Upon receiving the first CAN Frame, the instrument cluster responded
on a CAN ID of 0x01D0, accepting the transmission of more data, and asking for a maximum
of 8 packets to be sent before the radio should wait for another flow control message, and
with each packet being sent at a minimum of 40ms apart. Reassembling the Radio’s payload
without any of the ISO-TP protocol bytes shows the raw payload:
03 26 01 00 01 0B 10 41 55 44 49 4F 20 4F 46 46 00 C4
2.1.3 LIN
LIN (Local interconnect Network) is a much cheaper and simpler interconnect network used
in vehicles. Its main focus is for allowing ECUs to drive more primitive components simply,
rather than using expensive network solutions such as CANBUS.
A LIN network consists of a single wire, which connects 1 master ECU with up to 16 slave
components or ECUs. This single wire acts as a half-duplex serial interface, running at 12V
DC. The data transfer rate of LIN Bus is relatively slow, ranging from 1-20kbps. However,
due to its simplicity, LIN bus is preferred when driving primitive components. For example,
the engine ECU in a vehicle can use a LIN network to drive items like the engine fan and AC
CHAPTER 2. LITERATURE REVIEW 10
compressor on the engine, allowing the engines ECU to also get feedback on the components
state of operation.
A LIN network operates by the master ECU requesting data from a slave component on
its network. The network is otherwise void of any traffic. When requesting data, the master
ECU will send a header frame which looks like the following:
Field name Length (Bits) Purpose
Sync break 14+ Indicates the start of a new frame
Sync byte 8 Allows for resynchronization of slave nodes
ID byte 6 Frame Identifier
Parity 2 parity for the frames ID
As seen above, the ECU starts by sending a sync break. This notifies all the nodes on the
LIN network to begin listening for incoming data and stop transmitting. Following this is the
sync byte, This has a predefined value of 0x55 (01010101 in binary) and allows all the nodes
on the network to calculate and determine the time between the high and low voltages on the
bus, allowing them to listen in sync with the master sending the rest of the frame. Lastly is
the ID followed by 2 bits for parity. This is the ID the ECU is requesting data from, and the
matching node on the network will respond with a data frame, which is structured like so:
Field name Length (Bits) Purpose
Data 1-64 Data to be transmitted
Checksum 8 Checksum calculation
The response sent back to the master ECU contains simply a data field (configurable between
1 and 64 bits) followed by an 8 byte checksum. Since LIN 2.0, the ID of the request determines
the data size of the response:
ID Range Data length
0-31 2 bytes
32-47 4 bytes
48-63 8 bytes
When calculating the checksum of the frame, the following code is used:
1 uint8_t c a l c u l a t e _ c h e c k s u m ( uint8_t id , uint8_t * data_ptr , uint8_t data_len ,
uint8_t cs_mode ) {
2 uint16_t tmp = 0; // 0 is default for classic checksum
3 if ( cs_mode == ENHANCED ) { tmp = id ; } // Enhanced checksum ( LIN 2.0)
4 for ( int i = 0; i < data_len ; i ++) {
5 tmp += * data_ptr ++
6 if ( tmp >= 256) {
7 tmp -= 255; // Wrap when overflow
8 }
9 }
10 return ~ tmp & 0 xff ; // Return the lower byte
11 }
runs at 10.4kbps and allows for the transmission of up to 255 bytes rather than the usual limit
of 8 bytes when being used under the ISO14230-2 protocol (KWP2000).
As the bus is usually powered off during normal operation of the vehicle, it has to be woken
up in one of 2 ways when a OBD-II adapter wants to send or receive data on the K-Line.
Fast initialization
The fast initialization method is another way to wake up a K-Line network, and is only used
for ISO14230-2. With fast initialization, the tester sends a 25ms pulse on the K-Line, followed
by the request ID at the networks usual bus speed (Typically 10.4kbps)
It should be noted that whilst the five baud initialization method and Fast initialization
methods work very differently from one another, both can be achieved using the same physical
hardware and transceivers.
ISO9141-2
ISO9141-2 ISO (1989) is the transport layer used for ISO9141-4 (OBD 2.3.1) when utilizing the
K-Line diagnostic line of a vehicle. This configuration only utilizes the Five baud initialization
sequence (Fast initialization is never used). This network configuration can run at multiple
baud rates, ranging from 9.6kbps to 15.625kbps, however most common rates found are
9.6kbps and 10.4kbps. This configuration supports a maximum data size of up to 12 bytes,
with an additional 1 byte used for checksum.
ISO14230-2
ISO14230-2 ISO (2000) is the transport layer used for ISO14230-4 (KWP2000 2.3.2) when
utilizing the K-Line diagnostic line of a vehicle. This configuration of K-Line only runs at
10.4kbps (Unless its 5bps during the Five baud initialization sequence), and supports both
Five baud initialization, or Fast initialization wake up methods. This configuration supports
data sizes of up to 255 bytes, with an additional 1 byte for checksum.
With both of these network configurations, the target ECU on the K-Line will go back
into a sleep state if no data is transferred in 5 seconds. Therefore, it is necessary for the tester
to send periodic ”Stay awake” messages on K-Line to keep the target ECU awake and in a
diagnostic session.
CHAPTER 2. LITERATURE REVIEW 12
SAE J2534
SAE J2534 (Passthru) Drew Technologies, Inc (2003) is a hardware API for diagnostic software
to communicate with a supported adapter via a Windows DLL. It was originally created for
Windows 2000 and Windows XP, however it still works on modern versions on Windows. A
manufacturer of an adapter which supports the Passthru API can support any number of the
following network layer protocols:
1. ISO 9141
2. ISO 14230-4
3. SAE J1850 41.6 KBPS PWM (Pulse width modulation)
4. SAE J1850 10.4 KBPS VPW (Variable pulse width)
5. CAN
6. ISO 15765-4 (ISO-TP)
7. SAE J2610 DaimelrChrysler SCI (Serial communication Interface)
Configuration data about each adapter and library which supports the Passthru API is stored
in the Windows Registry. A typical registry entry for an adapter will contain the following
information about the adapter:
• Hardware name
• Adapter vendor
• Supported protocols (From list above)
• Library path (Location to the Adapters DLL)
The J2534 API DLL must be compiled as a 32bit library DLL, meaning it is incompatible with
64bit software on modern systems. This is however not a problem as all diagnostic software
is also compiled as a 32bit executable, in order to keep backwards compatibility with older
systems.
CHAPTER 2. LITERATURE REVIEW 13
SDConnect C4
The SDConnect C4 adapter is a diagnostic adapter which Daimler will ship with their diag-
nostic tool set (2.4.3) when provided to authorized workshops and dealers.
Due to this, not much is known about the cost of the adapter, however it is known it
supports the D-PDU API, as well as Daimler’s own proprietary protocol for talking to their
in-house diagnostic adapters.
Whilst Daimler only provide the adapter to authorized workshops, there appears to be a
huge amount of listings on ebay for this adapter, ranging in price from £500-£1000. However,
it is apparent that some of these SDConnect listings might be cloned Chinese manufactured
adapters, rather than a genuine adapter which has come from Daimler.
Bosch VCI
The Bosch VCI adapter is a series of diagnostic adapters which Bosch sells. For this section,
the specifications of the Bosch MTS 6516 VCI adapter will be used.
This adapter can be used with both SAE J2534 and ISO 22900-1/2. It features 3 separate
CAN channels (for CAN and ISO15765-4), 2 UART channels (For ISO9141 and ISO14230-2),
1 J1850 channel for either J1850VPW or J1850PWM. In order to connect to a PC, either
WIFI or USB can be used with this adapter.
Cost of this adapter is unknown, however a similar used adapters by Bosch can be found
on auction sites like Ebay for around £1000-£2000. So it can be concluded that these VCI
adapters are very expensive, and out of the reach of the majority of consumers.
Figure 2.2: Data format of a service request with PID and data
0 8 16 24 32
0 8 16 24
In the above figures, it should be noted that some services (Such as in OBD-II), do not require
CHAPTER 2. LITERATURE REVIEW 15
a PID or Data, meaning only 1 byte of data (SID) is sent to the ECU. The entire length of the
request and positive response messages can be as long as the underlying transport protocol.
For example, ISO-TP can handle payloads of up to 4096 bytes, where as ISO14230-2 can only
support up to 255 bytes.
2.3.1 OBD-II
On-Board Diagnostics (OBD-II) is a relatively simple and read-only diagnostic protocol, and
is a legal requirement on all vehicles since 2001. It is a standard way to communicate with
the engines ECU in a diagnostic session in order to read standard error codes, read sensor
data from the ECU and carry out emissions tests. OBD-II can be used on any network layer
protocol, but is mainly found to use ISO9141-2 on older vehicles, and ISO-TP on all cars
found after 2008.
OBD have 9 pre-defined services, OEMs are free to choose which services their cars
support, however most commonly services 01,02,03,04,09 are always implemented. It should
be noted as well that OEMs are free to implement their own custom services beyond this
range.
• 0x06 - Request ob-board monitoring test results for specific monitored systems
• 0x07 - Request emissions-related diagnostic trouble codes detected during the current
or previous drive cycle
Every service (SID) has a child PID which will return which subfunctions are supported by the
ECU for the SID. For example, taking a look at the response for SID 0x01, PID 0x00, which
gets the supported PIDs for service 0x01 from 0x00 to 0x20:
1 REQUEST : 0 x01 0 x00
2 RESPONSE : 0 x41 0 x00 0 xBE 0 x1F 0 xA8 0 x13
Hexidecimal B E 1 F A 8 1 3
Binary 1 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 1
PID supported? Y N Y Y Y Y Y N N N N Y Y Y Y Y Y N Y N Y N N N N N N N N N Y Y
PID ID (Hex) 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20
From this, we can see that this engine ECU supports Service 01 PIDs 0x01, 0x03, 0x04, 0x05,
0x06, 0x07, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x1F and 0x20.
CHAPTER 2. LITERATURE REVIEW 16
Service 0x01
Service 01 is used to retrieve live data metrics from the engine, PIDs 0x00, 0x20, 0x40, 0x60,
0x80, 0xA0, 0xC0 are special pids that are used to retrieve the next 32 supported PIDs, as
shown in 2.2. A full list of PIDs are found at Wikipedia (2021).
Any response from Service 01 PIDs will result in 4 bytes or less being returned from the
ECU, which are typically converted using either as enumerated values, or using mathematical
formulas.
An example of how this works would be to look at decoding the Odometer reading over
Service 01 (PID 0xA6):
1 REQUEST : 0 x01 0 xA6
2 RESPONSE : 0 x41 0 xA6 0 x00 0 xA0 0 xC1 0 x0F
Service 0x02
Service 02 works the same way as 01, however in addition to the PID, the Freeze frame number
is also provided, and the ECU will return the sensor reading when the Freeze Frame occurred.
since reoccurred). Service 0A is used to retrieve DTCs that are permanent (malfunctions that
cannot be cleared with service 04 and will result in a failure in emissions test)
For all these services, no PID byte is given, and the ECU will return a list of error codes
which are applicable to each service (2 bytes per DTC).
Service 0x04
Service 04 is used to clear any DTCs that are stored on the ECU. No PID bytes are required
for this service, and the ECU does not respond back with any data. This service can only be
used to clear stored and pending DTCs. Permanent DTCs cannot be cleared with this service,
as they usually require the dealers specialist tool to clear.
Service 0x08
Service 08 is used to control components in the vehicle when performing tests. For example,
for the duration of some tests, the tester would tell the ECU with service 08 to not log any
DTCs for the duration of the test. Once the test is over, the tester will use service 08 to tell
the ECU to go back to its default state and monitor components and log DTCs.
Service 0x09
Service 09 is used to request vehicle information from the engine ECU. This can include data
such as VIN (Vehicle Identification Number), Calibration ID, Calibration verification numbers
(CVN), ECU Name and more. The majority of data returned in service 09 is returned as
ASCII encoded strings.
Example of requesting the VIN:
1 REQUEST : 0 x09 0 x02
2 RESPONSE : 0 x49 0 x02 0 x01 0 x57 0 x43 0 x43 0 x31 0 x30 0 x31 0 x30 0 x30 0 x38 0 x32 0 x42
0 x32 0 x37 0 x33 0 x39 0 x32 0 x32
be modified to enter an extended diagnostic session, flash session (For firmware updates), or
other protocol dependent modes. In these non-default modes, the ECU can behave unpre-
dictably, or with reduced functionality. KWP2000 and UDS both have the ability to clear
Permanent DTCs from an ECU, which OBD-II cannot do.
Both UDS and KWP2000 function over ISO-TP (2.1.2), however KWP2000 can also func-
tion on top of ISO14230-2 (2.1.3) and UDS can additionally function on DoIP (Diagnostic
over IP). However, DoIP is typically only utilized during firmware updates of large ECUs such
as infotainment systems in modern vehicles, which utilize a full Operating system. This dra-
matically reduces firmware update times from 10-12 hours (Over ISO-TP), to a few minutes
(Over DoIP). This is due to DoIP using half duplex ethernet with a maximum bandwidth of
200Mbps, whilst ISO-TP is a lot slower, due to the underlying CAN network having a maxi-
mum speed of 500kbps, and also the ISO-TP protocol itself having an additional performance
and bandwidth overhead.
knowledge, the diagnostic application can then scan all the ECUs within the vehicle using the
correct communication protocols and typically using KWP2000 or UDS diagnostic servers.
This can be viewed in A.1.
Once the application has established contact with all the ECUs within the vehicle, the
tester (The engineer who uses the software) is then free to probe each individual ECU in the
vehicle in order to locate a problem (A.2). Each ECU in the vehicle has a list of adaptations
and tests that can be carried out in order to test a potential problematic component. The
tester is guided throughout the tests by the diagnostic software as to what to do next, for
instance turning on/off the vehicle. Some tests can even be carried out by the ECU fully
autonomously, with the results being displayed to the user once the test has completed.
Each ECU also has its own view dedicated to displaying a list of error codes stored on the
ECU (DTCs), as well as displaying what the ECU was doing at the time of the error (Freeze
Frame data). The tester can view an error as well as a full description of the error.
There are very few free tools which can read and interpret ODX files. Instead, companies
such as Vector and Softing have commercial licensed tools that can generate ODX data files,
as well other tools that can be used to test an ECU’s implementation of the ODX data.
Diagnostic software typically utilizes data from all the above ODX categories, which the
OEM or diagnostic software creator will then use to compile custom binary files for their own
tools based on the data from ODX. This is done so that the raw ODX data is no longer in
a raw format, which can be easily understood by individuals, and also saves a lot of space
compared to using the raw XML based ODX containers. An example of this proprietary binary
format that will be utilized in this report is Daimler’s CBF file format.
As seen in the above table, the proposed adapter solution will be the only one which can
support the J2534 API on all desktop operating systems. One thing to note about the
ELM327 column. As discussed in (2.2.2), there can be many clone adapters, which won’t
support all the transport protocols such as ISO-TP, instead, only supporting ISO9141 so that
they can be used with mobile applications such as Torque.
CHAPTER 2. LITERATURE REVIEW 21
Table 2.4: Comparison between existing diagnostic software and proposed solution
As seen in this table, the proposed solution will be the only one which can support Linux and
OSX. Also, SocketCAN and D-PDU are proposed as future additions to the application, but
for the current scope, they will not be included due to time constraints.
Chapter 3
Methodology
This section will cover the design and implementation of all 3 parts of the project, refer-
encing content discussed in the literature review. Each part is broken down into its design,
implementation and justification.
OBD2 Port
Figure 3.1: Mock-up of how the ECUs are connected in the test setup
In this test setup, the ’Gateway’ is simply An arduino with 2 CAN interfaces on it. Its job
is to relay CAN Frames to and from the CAN C Network (Black) and the CAN B network
(Orange). This is because for diagnostics, the cluster only responds if the diagnostic packets
are sent over CAN B, and not CAN C. In a real vehicle, this functionality is usually handled by
the ignition switch ECU which acts as a CAN Gateway. However, this could not be purchased
for this setup due to its cost.
22
CHAPTER 3. METHODOLOGY 23
3.2 Rust
As part of this project, the Rust programming language was utilized rather than other lan-
guages such as C++ or Java for the majority of the development process, however C++
had to be used for the Passthru adapter firmware [3.4], due to limitations with the standard
Arduino libraries. Rust has some unique advantages compared to other languages, to name a
few:
However, at the time of writing this, Rust does have bindings for platform GUI library’s, such
as QT, GTK and WINAPI, but not have a mature cross-platform GUI library, which would
be required for the Diagnostic application, as will be mentioned in section 3.5. However,
an experimental cross-platform library called Iced [https://github.com/hecrj/iced] does
exist. This GUI library supports all three major operating systems (Linux, Windows and
MacOS), as well as the Web for WASM build targets. Everything in this library is drawn to
the screen using either Vulkan, OpenGL, Metal or DX11 depending on the platform.
Iced is inspired by the Elm Architecture, meaning that the GUI is built in pure code, rather
than having external XML files to describe the UI. Also due to it being inspired by Elm, the
GUI architecture is split into four main components. Application state, message events within
the UI, view logic and update logic.
OvdECU
Connection ECUVariantDefinition
enum
+ baud: u32 ServerType + name: String
+ send id: u32 server type + description: String
+ global send id: u32 1 KWP2000 + errors: Vec<ECUDTC>
+ connection type: ConType UDS + adjustments: Vec<Service>
+ recv id: u32 + actuations: Vec<Service>
+ functions: Vec<Service>
+ downloads: Vec<Service>
1 connection type
1..* patterns
ISO-TP
Vec
interface + blocksize: u32
ConType + st min: u32
+ ext can addr: u32 ECUVariantPattern
+ ext isotp addr: u32 + vendor: String
+ vendor id: u32
enum
LIN LinWakeUpMethod
wake up method
+ max segment size: u32 1 FiveBaudInit
FastInit
Service
+ name: String ECUDTC
+ description: String + error name: String
+ payload: Vec<u8> + summary: String
+ input params: Vec<Parameter> + description: String
+ output params: Vec<Parameter> + envs: Vec<Parameter>
+ service has input(): bool
+ service has output(): bool
Parameter
+ name: String
enum + unit: Option<String>
ParamDecodeError + start bit: usize
NotImplemented + length bits: usize
BitRangeError + data format: DataFormat
DecodeNotSupported + decode value to string(input: &[u8]): Result<String, ParamDecodeError>
StringDecodeFailure + decode value to number(input: &[u8]): Result<f32, ParamDecodeError>
+ can plot(): bool
+ get unit(): Option<String>
- get number
1 valid bounds
byte order 1
enum
Limit
ParamByteOrder
upper bound: f32
BigEndian
lower bound: f32
LittleEndian
Bool Linear
RatFunc TableInterpretation CompuCode
+ pos name: Option<String> + multipler: f32
+ neg name: Option<String> + offset: f32
interface
DataFormat
enum
TableData
StringEncoding
+ name: String
ASCII
+ start: f32
Utf8
+ end: f32
Utf16
Figure 3.2 shows the root object UML representation, as well as connection properties and
the main object for storing a ECU Variant object.
OvdECU is the root structure of the JSON. This contains the name of the ECU, a de-
scription of the ECU, connection properties of the ECU, and all ECU Variants. A ECU must
have at least 1 Connection method and at least VariantDefinition.
Connection is used to define what transport and diagnostic protocols are used when com-
municating with the ECU. This contains data such as the bus speed (baud) of the connection
method as well as various ID’s to use whilst in a diagnostic session. An ECU can have multiple
connection methods, such as KWP2000 over ISO-TP (CAN), or KWP2000 over K-Line (ISO
14230-2). In either configuration, the ECU’s addresses will differ. send id is used to iden-
tify which ID on either network type the tester should send diagnostic messages on. recv id
denotes which ID the ECU will respond on, and the tester will listen for data with this ID.
global send id is an optional entry, and is only used for some ECUs. This value is required
in situations where the TesterPresent message is to be broadcast on a separate address to
the diagnostic request message (Normally they are the same address). In the case of my test
vehicle, this is used for all interior CAN devices located on CAN-B.
ConType represents an interface for each physical network connection method to inherit,
with various attributes related to the network setup. For instance, ISO-TP configuration re-
quires attributes attaining to block size, default separation time, and extended addressing,
whilst the LIN connection (K-Line) requires a maximum packet size that the ECU supports,
as well as which wake up method to use when activating the K-Line network.
ECUVariantDefinition contains the definition for each ECU Variant. A ECU Variant is
an equivalent to a software version on a desktop PC. An ECU can receive updates over time
(via firmware flashes). With each firmware update, diagnostic routines can be altered, as
well as lists of errors, or even in some cases, the description of certain errors changes from
variant to variant. Each ECU Variant can be implemented by multiple hardware manufactures
CHAPTER 3. METHODOLOGY 26
such as Bosch or Delphi. Because of this, the ECUVariantPattern class is required here. This
small class contains the name of the manufacture (Vendor), as well as a defined vendor ID.
This vendor ID can be retrieved over both KWP2000 and UDS, and is unique to the software
version and ECU manufacturer.
Figure 3.3 shows how both Service and ECUDTC both use the Parameter object.
Service represents an IO function that can be executed on the ECU. Each function re-
quires a name, description of what the function does, a raw payload to send to the ECU, and
a list of input and/or output parameters. Input parameters modify the payload that is sent
to the ECU, whilst output parameters are used to convert the ECUs response message into
something that can be easily interpreted by an individual.
ECUDTC represents a Diagnostic trouble code (DTC) that can be stored on the ECU.
Each DTC has a unique name, such as ’P202A’, a summary of what the error means, and a
more detailed description string of what the error means. the envs array is a list of parameters
that are used when querying freeze frame data about the DTC, which is then interpreted using
the list of parameters to decode the freeze frame data into something that is human readable.
Freeze frame data can be acquired with both KWP2000 and UDS.
Parameter contains data which has been interpreted from the ODX-D data structure
[emotive de (2014a)]. Each parameter contains a name which denotes what the parameter
means, as well as an optional unit, which can be used to denote the unit of the parameter,
such as ◦ C or Engine RPM. start bit denotes where in the payload the parameter starts at,
and length bits denotes how big the parameter is in bits. This is because the ECU response
message can contain values that are tightly packed, and at odd bit offsets. data format de-
notes which Data format interpreter to use for the raw data within the given bit range. Lastly,
valid bounds is an optional class which can be specified. If it is declared in a Parameter
which is stored in a parent services’ input parameters section, then it is used to check the
user input is within range. If it is stored in a parameter which lies within a parent services’
output parameters section, then it used to set the upper and lower bounds for graphing. Note
that valid bounds is ignored if it is specified in a parameter which cannot be converted to a
number data type.
Figure 3.4 Shows the classes which inherit the DataFormat interface. Classes that appear in
blue exist in code as a placeholder, and do not do anything at this time. They are derived
from the ODX-D specification.
Bool data format is used when the parent parameter shall be interpreted as a boolean.
This class has two optional parameters, which can be used to override the default ’True’,
’False’ interpretation of booleans. This data type can be used for graphing.
Linear format is used when a raw value is to be manipulated using a simple y = mx + c
equation. The input value is x, whilst ’multiplier’ represents m, and ’offset’ represents c. This
data format can be used for graphing.
String format is used when the parent parameters byte range is to be interpreted as a raw
string. This data format has a parameter (encoding), which denotes what text encoding to
use when encoding and decoding the string. This can either be ASCII, Utf8 or Utf16. This
data type cannot be used for graphing.
Binary format is used when the parent parameter shall be interpreted as a binary string,
such as ’0b00110011’. It should be noted that this data type is not in the ODX-D specifica-
tion, but was added at a later date due to finding that Daimler seemed to use Enum tables
to represent binary strings in their CBF files, meaning that an 8 byte parameter would have
CHAPTER 3. METHODOLOGY 27
On line 1, the #[ derive ] attribute tells the compiler to implement traits for the Struct at
compile time. Serialize , Deserialize are traits that come from the Serde library, and will
enable this Struct to be serialized and deserialized to and from JSON without any additional
code needing to be written.
Looking at the Service Struct, the implementation in code is a bit more complicated:
1 #[ s e r d e _ a s ]
2 #[ derive ( Debug , Clone , Serialize , Deserialize , P a r t i a l E q ) ]
3 pub struct Service {
4 pub name : String ,
5 pub description : String ,
6 #[ s e r d e _ a s ( as = " s e r d e _ w i t h :: hex :: Hex < s e r d e _ w i t h :: formats :: Uppercase >") ]
7 pub payload : Vec < u8 > ,
8 #[ serde ( s k i p _ s e r i a l i z i n g _ i f = " Vec :: i s _ e m p t y ") ]
9 #[ serde ( default = " Vec :: new ") ]
10 pub input_params : Vec < Parameter > ,
11 #[ serde ( s k i p _ s e r i a l i z i n g _ i f = " Vec :: i s _ e m p t y ") ]
12 #[ serde ( default = " Vec :: new ") ]
13 pub output_params : Vec < Parameter >
14 }
In this structure, there are few more additional tags that are used by Serde. s k i p _ s e r i a l i z i n g _ i f
is used to tell Serde to skip Serialization if a condition is met for the value. In this case, if
the Vector is empty, Serialization is skipped in order to reduce the Serialized file size. default
tells Serde what to do if the value is not found in JSON when deserializing. In this case,
replace the value with an empty vector. Lastly, the serde_as online 6 tells Serde to serialize
the payload as an upper case Hex String. This because by default Serde will serialize the
payload as a list of numbers, which get re-interpreted as bytes during deserialization. This
was not optimal as both KWP2000 and UDS use Hex bytes in documentation rather than
numbers. Therefore, this tells Serde to strictly serialize the payload as a hex string, which is
more easily understood by people reading the file.
CHAPTER 3. METHODOLOGY 28
35 match r e s u l t {
36 Ok ( r ) => Ok ( r a s u32 ) ,
37 E r r ( ) => E r r ( P a r a m D e c o d e E r r o r : : B i t R a n g e E r r o r )
38 }
39 } else {
40 E r r ( ParamDecodeError : : B i t R a n g e E r r o r )
41 }
42 }
This function essentially extracts a 1-32bit long number from the input byte stream, using the
Parameters start bit and length bits values to work out where to extract the number in the
input array. As seen in this code, it also takes into account the Endianness of the expected
value.
When decoding a value to String, each DataFormat has its own parser function that runs
on the input byte stream. Below is a couple examples:
1 DataFormat :: Bool { pos_name , neg_name } = > {
2 return match self . get_number ( input ) ? {
3 0 = > Ok ( neg_name . clone () . unwrap_or ( " False " . into () ) ) ,
4 _ = > Ok ( pos_name . clone () . unwrap_or ( " True " . into () ) )
5 }
6 }
As seen here, both Bool and HexDump return early with their formatted Strings, whilst Linear
doesn’t return early, this is because it is a number and therefore might have a unit associated
with it. Further down the decoder there is a check for this, and if a unit exits, it is appended
to the ’result’ string, before being returned.
Container
1 1
1..*
CFFHeader CTFHeader ECU
1..*
1..*
CTFLanguage ECUVariant
1..* 1..*
1..* 1..*
0..1
1..*
ComParameter Preparation 0..*
Figure 3.5: Simplified UML representation of the data structure in a CBF File
As shown in figure 3.5, a CBF File has a completely different data structure when com-
pared to the JSON Schema. To solve this, a parser script was written to process the root
Container and convert this data structure to the JSON structures. This will be very briefly
touched upon here, since the code to convert to the JSON schema is far too long.
As an example, here is the code which converts the ComParameter’s found in Interface-
SubType to a Connection property for the JSON:
1 let mut c o n n e c t i o n s = Vec : : new ( ) ;
2 for x in e . interface sub types . i t e r () {
3 l e t c o n n e c t i o n = i f x . comm params . i t e r ( ) . any ( | x | x . param name == ”CP REQUEST CANIDENTIFIER” ) { // ISOTP
4 Connection {
5 baud : x . g e t c p b y n a m e ( ”CP BAUDRATE” ) . e x p e c t ( ”No CAN B a u d r a t e on i n t e r f a c e ! ? ” ) ,
6 send id : x . g e t c p b y n a m e ( ”CP REQUEST CANIDENTIFIER” ) . e x p e c t ( ”No CAN ReqID on i n t e r f a c e ! ? ” ) ,
7 recv id : x . g e t c p b y n a m e ( ”CP RESPONSE CANIDENTIFIER” ) . e x p e c t ( ”No CAN RespID on i n t e r f a c e ! ? ” ) ,
8 g l o b a l s e n d i d : x . g e t c p b y n a m e ( ”CP GLOBAL REQUEST CANIDENTIFIER” ) ,
9 c o n n e c t i o n t y p e : ConType : : ISOTP {
10 b l o c k s i z e : 8 , // Some r e a s o n MB a l w a y s u s e s 8
11 s t m i n : x . g e t c p b y n a m e ( ”CP STMIN SUG” ) . u n w r a p o r ( 2 0 ) , // Seems d e f a u l t f o r MB
12 e x t i s o t p a d d r : f a l s e , // MB n e v e r u s e e x t e n d e d ISO−TP a d d r e s i n g
13 // Check CAN ID t o s e e i f t h e CAN n e t w o r k i t s e l f i s e x t e n d e d
14 e x t c a n a d d r : x . g e t c p b y n a m e ( ”CP REQUEST CANIDENTIFIER” ) . unwrap ( ) > 0 x7FF
15 | | x . g e t c p b y n a m e ( ”CP RESPONSE CANIDENTIFIER” ) . unwrap ( ) > 0 x7FF
16 },
17 s e r v e r t y p e : i f x . q u a l i f i e r . c o n t a i n s ( ”UDS” ) { // I n t e r f a c e t y p e i s i n q u a l i f i e r name f o r ISO−TP
18 S e r v e r T y p e : : UDS
19 } else {
20 S e r v e r T y p e : : KWP2000
21 }
22 }
23 } e l s e { // Assume LIN ( ISO14230 −2)
24 Connection {
25 baud : 1 0 4 0 0 , // A l w a y s f o r ISO14230−2 w i t h MB
26 s e n d i d : x . g e t c p b y n a m e ( ”CP REQTARGETBYTE” ) . e x p e c t ( ”No LIN R e q u e s t ID on i n t e r f a c e ! ? ” ) ,
27 r e c v i d : x . g e t c p b y n a m e ( ”CP RESPONSEMASTER” ) . e x p e c t ( ”No LIN R e s p o n s e ID on i n t e r f a c e ! ? ” ) ,
28 g l o b a l s e n d i d : x . g e t c p b y n a m e ( ”CP TESTERPRESENTADDRESS” ) ,
29 c o n n e c t i o n t y p e : ConType : : LIN {
30 m a x s e g m e n t s i z e : x . g e t c p b y n a m e ( ”CP SEGMENTSIZE” ) . u n w r a p o r ( 2 5 4 ) , // D e f a u l t f o r ISO14230−2
31 wa ke u p me th od : LinWakeUpType : : F i v e B a u d I n i t , // MB a l w a y s u s e s t h i s w i t h ISO14230−2
32 },
33 s e r v e r t y p e : S e r v e r T y p e : : KWP2000 // A l w a y s w i t h LIN
34 }
35 };
36 c o n n e c t i o n s . push ( c o n n e c t i o n ) ;
37 }
What this code does is iterate over each of the CBF’s InterfaceSubTypes, which represents
CHAPTER 3. METHODOLOGY 31
a connection method for the ECU, and try to work out what kind of connection it is based
on the ComParameters found in the InterfaceSubType, then mapping the ComParameters to
data in the Connection object for the JSON Schema.
The name of the ComParameters (EG: CP_BAUDRATE ) are all found within the ODX specifi-
cation, so it is simple to know what they do. Also to note is that especially for a LIN connection
type, there is a lot of assumptions about what Mercedes utilizes. This was done by looking
at how Daimler’s own software tries to initialize contact with an ECU using ISO14230-2 for
over 50 different ECUs in various cars that supported ISO1423-4.
This is just one example. The CRD ECU in total has over 3500 services that look like this.
By grouping services’ output params together based on the request payload, it was possible
to significantly reduce the file size of the output JSON. Below is the same service’s grouped
together:
1 ” downloads ” : [
2 ...
3 {
4 ”name” : ” DT 30 C0 ” ,
5 ” d e s c r i p t i o n ” : ” Data do wnloa d 30 C0 ” ,
6 ” p a y l o a d ” : ”30 C001 ” ,
7 ” output params ” : [
8 ...
9 {
10 ”name” : ” c o u n t e r f o r t h e c o n d i t i o n s a t t h e end o f t h e r e g e n e r a t i o n [ 0 ] ” ,
11 ” u n i t ” : ”” ,
12 ” s t a r t b i t ” : 56 ,
13 ” l e n g t h b i t s ” : 16 ,
14 ” b y t e o r d e r ” : ” BigEndian ” ,
15 ” data format ” : {
CHAPTER 3. METHODOLOGY 32
16 ” Linear ” : {
17 ” m u l t i p l i e r ” : 1.0 ,
18 ” o f f s e t ” : 0.0
19 }
20 }
21 },
22 {
23 ”name” : ” c o u n t e r f o r t h e c o n d i t i o n s a t t h e end o f t h e r e g e n e r a t i o n [ 1 ] ” ,
24 ” u n i t ” : ”” ,
25 ” s t a r t b i t ” : 72 ,
26 ” l e n g t h b i t s ” : 16 ,
27 ” b y t e o r d e r ” : ” BigEndian ” ,
28 ” data format ” : {
29 ” Linear ” : {
30 ” m u l t i p l i e r ” : 1.0 ,
31 ” o f f s e t ” : 0.0
32 }
33 }
34 },
35 ...
36 }
37 ...
38 ]
The only down side to this approach is that the name and description of the parent service is
now a generic name, since the name is now no longer known. However, this is not a problem
as shown in section 3.5, the GUI for interfacing with this generated JSON will search both
input and output parameters for a user’s search terms, meaning that the parent service name
doesn’t have to be descriptive. Also, by doing this the file size of the output JSON shrunk by
approximately 15%, which in tern decreases parse times and load times in OpenVehicleDiag.
The SAE J2534 reference manual [Drew Technologies, Inc (2003)] was used as a reference
for the API implementation. Specifically, this will be an implementation of V04.04 of the
Passthru API 1 . Due to the short time constraints of this project, Only CAN and ISO-TP will
be implemented.
3.4.1 Architecture
In order to reliably allow the J2534 library and adapter to communicate, the architecture of
the communication between the adapter and J2534 library will revolve around Request and
Response messages. This can be executed without overly complex code since the J2534 API
states the the API is designed to be single threaded, meaning that any application that utilizes
the J2534 API will not be making asynchronous calls to the library.
Execute
Response bytes
Ok(Resp Msg)
The above figure shows how communication between the library and adapter itself will be
handled. The library will contain various functions for tasks like channel creation or trans-
mitting data, and they will construct Payloads to send to the adapter. These payloads are
forwarded to a communication server which is part of the library, and sends the payload as a
byte stream to the adapter. The adapter will execute what the payload specifies, and then
return a response message to indicate if the action completed successfully, or failed (Which
will contain the error message). This response is then forwarded back to the caller library
function.
Since the M2 has 5 LED’s and an additional RGB LED, it was decided to utilize these
LEDs so the user knows what is going on without the need to check log files:
1
List of Passthru API functions: C.1
CHAPTER 3. METHODOLOGY 34
and here is an example of how the Passthru function calls are converted from C++ to Rust:
1 # if defined __WIN32__
2 # define APICALL __stdcall
3 # else
4 # define APICALL
5 # endif
6 ...
7 typedef long APICALL (* J 2 5 3 4 _ P a s s T h r u W r i t e M s g s ) ( unsigned long ChannelID ,
PASSTHRU_MSG * pMsg , unsigned long * pNumMsgs , unsigned long Timeout ) ;
8 ...
1 #[ n o _ m a n g l e ]
2 #[ allow ( n o n _ s n a k e _ c a s e ) ]
3 pub extern " stdcall " fn P a s s T h r u W r i t e M s g s (
4 ChannelID : u32 ,
5 pMsg : * const PASSTHRU_MSG ,
6 pNumMsgs : * mut u32 ,
7 Timeout : u32 ,
8 ) -> i32 {
The #[ n o_ m a g l e ] macro tells Rusts compiler to not mangle or modify the function name, but
to keep it as is in order for the function to be exposed as part of a library and for it to be
callable by external applications.
Windows ordinals
During testing, it was found that some applications such as Daimler’s DAS software would call
the Passthru API functions by their ordinal numbers rather than function name. An ordinal is
a unique ID assigned to a function. Since Rust’s compiler assigns a random ordinal number
to each function by default, this would cause the custom Passthru library to not load, since
the ordinal numbers did not match to the correct Passthru function. Therefore, a fix was to
utilize a .def file with Windows’ MSVC compiler in order to force Rust to assign the correct
ordinal number to each function in the Passthru library:
1 LIBRARY
2 EXPORTS
3 PassThruOpen @1
4 PassThruClose @2
5 P as sT hr u Co nn ec t @3
6 P a s s T h r u D i s c o n n e c t @4
7 P a s s T h r u R e a d M s gs @5
8 P a s s T h r u W r i t e M s g s @6
9 P a s s T h r u S t a r t P e r i o d i c M s g @7
10 PassThruStopPeriodicMsg @8
11 P a s s T h r u S t a r t M s g F i l t e r @9
12 P a s s T h r u S t o p M s g F i l t e r @10
13 P a s s T h r u S e t P r o g r a m m i n g V o l t a g e @11
14 P a s s T h r u R e a d V e r s i o n @12
15 P a s s T h r u G e t L a s t E r r o r @13
16 PassThruIoctl @14
Then, using a custom build.rs file (Which gets executed by the compiler), its possible to tell
the compiler to use the .def file during the compile process, but only under Windows:
1 use std :: env ;
2 fn main () {
3 let target_os = env :: var ( " C A R G O _ C F G _ T A R G E T _ O S " ) ;
4 match target_os . as_ref () . map (| x | &** x ) {
5 Ok ( " macos " ) | Ok ( " linux " ) = > {}
6 Ok ( " windows " ) = > println! ( " cargo : rustc - cdylib - link - arg =/ DEF : driver . def " ) ,
7 tos = > panic! ( " unknown target os {:?} ! " , tos ) ,
8 }
9 }
the serial communication API under windows3 , the library was able to function as expected
and transfer data between the M2 and a PC, regardless of the operating system.
The following data structure was defined on both the firmware and Rust library, in order
for both ends to send and receive data correctly. Below is the C++ definition in the adapter
firmware:
1 # define COMM_MSG_SIZE 4096
2 # define C O M M _ M S G _ A R G _ S I Z E COMM_MSG_SIZE -4
3
4 struct __attribute__ (( packed ) ) COMM_MSG {
5 uint8_t msg_id ;
6 uint8_t msg_type ;
7 uint16_t arg_size ;
8 uint8_t args [ C O M M _ M S G _ A R G _ S I Z E ];
9 };
1. msg id - Unique identifier of the message. This ID will keep repeating in the range of
1-254, (0x01-0xFF), and is used by the PC and Firmware to identify a response message
to a requested command. Each request and response message will have the same ID.
Messages that do not require a response will have a message ID of 0x00.
2. msg type - A byte which identifies what kind of message is being sent by either the
adapter of PC driver. See section C.2 for a full list of the message types and what they
signify, as well as which end sends and receives them.
3. arg size - A 2 byte value indicating how long the parameters of the message are which
reside in args
Communication server
Response Msg
Poll
Figure 3.8 shows an expanded view of the communication server sequence diagram. The
’server’ endpoint is the interface other functions in the Passthru library can call to send
messages to the Adapter. A sent message is sent to the server’s sender thread, which then
attempts to send the payload to the adapter. A maximum of 3 attempts are used to try and
send the payload. If all 3 attempts fail, a Tx Fail status is sent back to the caller function,
which then handles the appropriate action, as it assumes that the adapter is not connected
to the PC. If payload sending was completed successfully, a Tx OK message is sent back to
the caller function. In this event, the caller will now poll for a maximum of 2 seconds for a
response message. Constantly in the background, there is the poller thread, which constantly
polls the serial port for new bytes, reading them in order to ensure the serial port’s buffer is
not full (Linux has a maximum serial buffer size of 4096 bytes where as Windows’ is 16384
bytes). When a full payload is received by the poller thread, the response message is sent back
to the sender via a thread channel. If the incoming message is a log message (See below),
the poller instead logs the message to the libraries log file. In order to ensure the sender
and receiver message are for the same operation, the server checks the message ID, which
is unique to each IO operation which requires a response. If a response is not received in 2
seconds, the server terminates and returns a Timeout error back to the caller function. This
Timeout is also sent back to the poller thread, which will then discard the received message if
it does eventually get read. A timeout of 2 seconds is considered adequate as during testing,
all commands usually get executed within 10ms.
15
M2 (Stock)
Multimeter
M2 (Corrected)
Reported voltage V
10
0
0 2 4 6 8 10 12 14
PSU Voltage V
Figure 3.9: Voltage reading comparison between M2 (Stock and corrected) and
Multimeter
As figure 3.9 shows, the reported voltage by the M2 12VIO library seems to level out at
6.1V , even if the actual supply voltage is lower than 6.1V . Therefore, the adapter firmware
4
https://github.com/TDoust/M2_12VIO
CHAPTER 3. METHODOLOGY 38
(Black line) will cut off the voltage, interpreting any value less or equal to 6.1V as 0.0V .
This way, when the adapter is unplugged from the vehicle, it will report 0V rather than 6.1V ,
which makes more sense.
PassthruConnect
Open ISO-TP Req
StartMsgFilter
Set filters
PassthruWriteMsgs
PassThruReadMsgs
Msg 20 bytes
ISO-TP FirstFrame
ISO-TP FirstFrame
PassThruReadMsgs
ISO-TP FlowControl
ISO-TP FlowControl
Consecutive frame
Consecutive frame
Tx complete
PassThruReadMsgs
When transmitting ISO-TP data to an ECU. PassthruConnect is first called. This tells the
adapter to open an ISO-TP Communication channel, with a specified baud rate, and also
indications as to weather the CAN Network uses extended addressing or if ISO-TP uses
extended addressing.
Then StartMsgFilter is called, this tells the adapter to apply 3 filters. Pattern and Mask
(Which are both applied to listen to a specific CAN ID), and the Flow control filter. This
filter is only used for ISO-TP and contains the CAN ID to transmit data on back to the ECU.
After this, PassthruWriteMsgs is called, where the application tries to transmit data to
the ECU. This payload can be up to 4096 bytes long. The adapter receives this payload and
sends the ISO-TP First frame to the ECU, initiating the data transfer.
The ECU Then responds back with a flow control frame. The adapter reads this, and
applies the necessary block size and separation time, before transmitting ISO-TP consecutive
frames to the ECU.
Once the transmission is complete, the adapter sends a message back to the Passthru
library to indicate the data transfer is now complete. During the entire data transfer process,
the Application has been polling the Passthru library for new data. When the data transfer
CHAPTER 3. METHODOLOGY 39
is complete, a blank message is returned to the Application, with the TX MSG TYPE bit set
to 1, indicating data transfer complete.
PassthruConnect
Open ISO-TP Req
StartMsgFilter
Set filters
ISO-TP FirstFrame
ISO-TP FirstFrame
FF indication
PassThruReadMsgs
ISO-TP FlowControl
ISO-TP FlowControl
Consecutive frame
Consecutive frame
Consecutive frame
Rx complete
PassThruReadMsgs
Figure 3.11: Sequence diagram for receiving ISO-TP Data from an ECU
When receiving data from an ECU, PassthruConnect and StartMsgFilter are called just
like when transmitting data. Once the filters have been applied, the adapter can listen to
incoming data from the ECU.
When adapter receives a first frame indication from an ECU, it sends a flow control frame
back to the ECU, initiating the rest of the data transfer. At the same time, the adapter sends
a message to the Passthru library which contains the CAN ID of the sending ECU with the
ISO15765 FIRSTF bit set. This message is then read by the user application and it will then
begin polling for the full payload.
Upon receiveing the flow control frame from the adapter, the ECU then sends all the
consecutive CAN Frames which make up the full payload. Once the adapter verifies it has all
the data, it sends the full payload back to the Passthru library, which is then read by the user
application.
To work around the issue mentioned in 1, it was decided that the J2434 library driver and info
data should reside in ~/.passthru/. This is because on both Linux and OSX, writing to the
users home directory (~/) does not require any elevated privileges.
To mitigate the section issue mentioned 2, it was decided the best approach would be to
create a JSON file containing the attribute data defined in the J2534 API:
1 {
2 " CAN " : true ,
3 " ISO15765 " : true ,
4 " ISO9141 " : true ,
5 " ISO14230 " : true ,
6 " SCI_A_TRANS " : true ,
7 " SCI_A_ENGINE " : true ,
8 " SCI_B_TRANS " : false ,
9 " SCI_B_ENGINE " : false ,
10 " J1850VPW " : false ,
11 " J1850PWM " : false ,
12 " FUNCTION_LIB " : "~/. passthru / macchina . so " ,
13 " NAME " : " Macchina M2 Under the dash " ,
14 " VENDOR " : " rnd - ash@github . com " ,
15 " COM - PORT " : "/ dev / ttyACM0 "
16 }
This means that every potential J2534 device would have its own JSON file stored in ~/
.passthru/. The above example being ~/.passthru/macchinam2.json for the created
Macchina M2 driver.
Every key name within this JSON is an exact copy of the key names found in the official
J2534 specification for the Windows Registry.
From within the passthru library code itself, fetching the COM-PORT attribute now re-
quires 2 code bases, one for Windows to locate the registry key within the Registry, and
another for UNIX OS’s to read the JSON file and find the COM-PORT attribute.
This was done with the conditional compilation flags on the library code to generate 2
versions of the get com port function, based on the compile target:
1 #[ cfg ( unix ) ]
2 fn get_comm_port () -> Option < String > {
3 if let Ok ( content ) = std :: fs :: read_ to_strin g ( shellexpand :: tilde ( " ~/. passthru /
macchina . json " ) . to_string () ) {
4 return match serde_json :: from_str :: < serde_json :: Value >( content . as_str () ) {
5 Ok ( v ) = > v [ " COM - PORT " ]. as_str () . map ( String :: from ) ,
6 Err ( _ ) = > None
7 }
8 }
9 None
10 }
11
12 #[ cfg ( windows ) ]
13 fn get_comm_port () -> Option < String > {
14 if let Ok ( reg ) = RegKey :: predef ( H K E Y _ L O C A L _ M A C H I N E ) . open_subkey ( " SOFTWARE \\
WOW6432Node \\ P as sT hr u Su pp o rt .04.04\\ Macchina - Passthru " ) {
15 return match reg . get_value ( " COM - PORT " ) {
16 Ok ( s ) = > Some ( s ) ,
17 Err ( _ ) = > None
18 }
19 }
20 None
21 }
Both functions work in the same way. They try to locate the COM-PORT key and read it.
If the value was successfully read, then the COM-PORT value is returned, if it wasn’t found,
None is returned.
CHAPTER 3. METHODOLOGY 41
Tag Description
[DEBUG] Messages for debugging only. These messages do not
appear in release builds of the driver.
[ERROR] Error messages, something has gone terribly wrong and
the Passthru adapter cannot continue functioning.
[WARN ] An error has occured somewhere, but the adapter can
continue to function.
[INFO ] Info messages that can track what the adapter is doing
[M2LOG] Messages that originate from the adapter itself
The generated log file has a dual purpose. It aids debugging of the library when used by
an external application, but also shows in detail what the adapter is doing with regards to
setting up CAN and ISO-TP channels, which can aid reverse engineering by snooping what a
diagnostic application is requesting an adapter to listen for. An example of some log messages:
1 [ INFO ] − P a s s t h r u O p e n c a l l e d
2 [ DEBUG ] − M2 s e r i a l w r i t e r t h r e a d s t a r t i n g !
3 [ DEBUG ] − M2 c h a n n e l s e n d e r t h r e a d s t a r t i n g !
4 [ DEBUG ] − M2 s e r i a l r e a d e r t h r e a d s t a r t i n g !
5 [ DEBUG ] − R e q u e s t i n g c h a n n e l open . ID : 0 , P r o t o c o l : ISO15765 , baud : 8 3 3 3 3 , f l a g s : 0 x0000
6 [ M2LOG ] − S t a n d a r d CAN d e t e c t e d !
7 [ M2LOG ] − Normal ISO−TP A d d r e s s i n g d e t e c t e d !
8 [ DEBUG ] − Command t o o k 15544 u s t o e x e c u t e
9 [ DEBUG ] − M2 op en ed c h a n n e l !
10 [ DEBUG ] − F i l t e r s p e c i f i e d . Type : Mask f i l t e r , Data : [ 0 , 0 , 2 5 5 , 2 5 5 ]
11 [ DEBUG ] − F i l t e r s p e c i f i e d . Type : P a t t e r n f i l t e r , Data : [ 0 , 0 , 4 , 2 4 4 ]
12 [ DEBUG ] − F i l t e r s p e c i f i e d . Type : Flow c o n t r o l f i l t e r , Data : [ 0 , 0 , 5 , 1 8 0 ]
13 [ DEBUG ] − S e t t i n g ISO−TP f l o w c o n t r o l f i l t e r ( ID : 0 ) on c h a n n e l 0 . Mask : [ 0 0 , 0 0 , FF , FF ] , P a t t e r n : [ 0 0 , 0 0 ,
0 4 , F4 ] , F l o w C o n t r o l : [ 0 0 , 0 0 , 0 5 , B4 ]
7 };
1 v o i d CustomCan : : e n a b l e C a n F i l t e r ( i n t i d , u i n t 3 2 t p a t t e r n , u i n t 3 2 t mask , b o o l i s E x t e n d e d ) {
2 i f ( i d < 0 | | i d >= 7 ) r e t u r n ; // I n v a l i d m a i l b o x ID
3
4 // S e t p a t t e r n and mask on t h e s p e c i f i e d m a i l b o x
5 Can0 . s e t R X F i l t e r ( i d , p a t t e r n , mask , i s E x t e n d e d ) ;
6 // D e l e t e any o l d b u f f e r i f i t f o r some r e a s o n e x i s t s
7 delete check rx ring ( id ) ;
8 // C r e a t e o u r new r i n g
9 create check rx ring ( id ) ;
10 // Now r e g i s t e r t h e c a l l b a c k s o t h a t f r a m e s g e t p u s h e d t o o u r m a i l b o x
11 }
Listing 3.2: Function to enable CAN Filter. This sets up the RxQueue for the specified mailbox
1 v o i d CustomCan : : d i s a b l e C a n F i l t e r ( i n t i d ) {
2 i f ( i d < 0 | | i d >= 7 ) r e t u r n ; // I n v a l i d m a i l b o x ID
3 Can0 . s e t R X F i l t e r ( i d , 0xFFFF , 0 x0000 , f a l s e ) ;
4 delete check rx ring ( id ) ;
5 }
Listing 3.3: Function that removes the CAN Filter from the mailbox and deletes the RxQueue
Listing 3.4: Function that pushes a CAN Frame to a RxQueue during an interrupt
1 // T h i s f u n c t i o n g e t s c a l l e d by t h e c h a n n e l s
2 b o o l CustomCan : : r e c e i v e F r a m e ( i n t m a i l b o x i d , CAN FRAME ∗ f ) {
3 i f ( m a i l b o x i d < 0 | | m a i l b o x i d >= 7 ) r e t u r n f a l s e ; // I n v a l i d malbox ID
4 return r x q u e u e p o p f r a m e ( rxQueues [ m a i l b o x i d ] , ∗ f ) ;
5 }
6
7 b o o l CustomCan : : r x q u e u e p o p f r a m e ( rxQueue &r , CAN FRAME &f ) {
8 // No f r a m e s i n r i n g b u f f e r
9 i f ( r . head == r . t a i l ) return false ;
10 memcpy ( ( v o i d ∗)&f , ( v o i d ∗)&r . b u f f e r [ r . t a i l ] , s i z e o f (CAN FRAME) ) ;
11 r . t a i l = ( r . t a i l + 1 ) % MAX RX QUEUE ;
12 return true ;
13 }
Listing 3.5: Funtions which handles pulling CAN Frames from a specific mailboxes’ RxQueue
when requested.
1. Allow the user to scan for UDS/KWP2000 compatible ECUs in their vehicle that rely
on the ISO-TP transport protocol as described by [Nils Weiss, Sebastian Renner, Jürgen
Mottok, Václav Matoušek (n.d.)]
2. Allow the user to select an ECU from their vehicle, and for the application to establish
a diagnostic session with the ECU
CHAPTER 3. METHODOLOGY 43
3. Allow a user to run basic KWP2000/UDS commands on an ECU whilst the ECU is in
its diagnostic session
4. have a simple CAN analyzer page for showing CAN Traffic on the OBD-II port
5. Use the OVD JSON (3.3) to run advanced functions on an ECU that are usually only
able to be executed by commercial diagnostic software, and to interpret DTC Error
codes and freeze frame data.
Due to the complexity of the user interface and underlying architecture, only the major points
will be discussed in this report. Refer to D for the full code of the user interface.
interface
ProtocolServer
DTC
enum
DTCState
KWP2000Server UDSServer OBDServer None
Stored
Pending
Permanent
As discussed in (2.3), KWP2000, UDS and OBD all inherit the same request and response
message format, with only the actual SID and PID’s differing. Therefore, figure 3.12 shows how
all three diagnostic servers inherit the same common interface. This will allow all diagnostic
servers to work under a common UI interface, using dynamic dispatch to call the appropriate
diagnostic server. Also to note. The storage format will be the same regardless of the protocol
used. The DTC object can accommodate the DTC format of all 3 diagnostic protocols.
ISO15765Data
CanFrame enum
id: u32 FilterType
id: u32
data: Vec<u8>
dlc: u8 Pass
pad frame: bool
data: [u8; 8] Block
ext addressing: bool
ISO15765Config
interface
baud: u32 ComServer
send id: u32
recv id: u32
bs: u32 open device()
st min: u32 close device()
use ext can: bool is connected()
use ext addr: bool send can packets(&[CanFrame], timeout ms: u32): usize
read can packets(timeout ms: u32, max msgs: usize): Vec<CanFrame>
send iso15765 data(&[ISO15765Data], timeout ms: u32): usize
read iso15765 data(timeout ms: u32, max msgs: usize): Vec<ISO15765Data>
DeviceCapabilities open can interface(baud: u32, ext can: bool)
name: String close can interface()
vendor: String open iso15765 interface(baud: u32, ext can: bool, ext addr: bool)
lib path: String close iso15765 interface()
device fw version: String add can filter(filter: FilterType, id: u32, mask: u32): u32
lib version: String rem can filter(id: u32)
sci: Capability add iso15765 filter(id: u32, mask: u32, fc id: u32): u32
j1850: Capability configure iso15765(cfg: &ISO15765Config)
can: Capability rem iso15765 filter(id: u32): u32
iso15765: Capability clear can rx buffer()
iso9141: Capability clear can tx buffer()
iso14230: Capability clear iso15765 rx buffer()
doip: Capability clear iso15765 tx buffer()
batt voltage: Capability read battery voltage(): u32
get capabilities(): DeviceCapabilities
get api name(): String
As seen in figure 3.13, there are multiple data classes to represent data such as CAN and
ISO-TP payloads. This is done because each communication API has its own way of trans-
mitting data to and from hardware with various communication protocols. Therefore, data
types such as ISO15765Data exist as a portable data type that the rest of the application can
work with, and the implementors of ComServer (Such as passthru api) will internally convert
to and from data types.
CHAPTER 3. METHODOLOGY 45
The first line in this extract creates a type alias for the library. This way, if a function returns
an error code (Rather than 0 - OK), it gets wrapped in Rust’s return Err type. However if
the function succeeded, then the Error’s OK variant is returned, along with any data.
The second line shows an example of defining the Passthru API’s functions as a type alias
(Example shown is for PassThruClose).
The API also contains a struct which encapsulates all the library functions and reference
to the loaded passthru library:
1 #[ d e r i v e ( C l o n e ) ]
2 pub s t r u c t P a s s t h r u D r v {
3 // / Loaded l i b r a r y t o i n t e r f a c e w i t h t h e d e v i c e
4 l i b : Arc<l i b l o a d i n g : : L i b r a r y >,
5 // / Open d e v i c e c o n n e c t i o n
6 o p e n f n : PassThruOpenFn ,
7 // / C l o s e d e v i c e c o n n e c t i o n
8 c l o s e f n : PassThruCloseFn ,
9 // / C o n n e c t a c o m m u n i c a t i o n c h a n n e l
10 c o n n e c t f n : PassThruConnectFn ,
11 // / D i s c o n n e c t a c o m m u n i c a t i o n c h a n n e l
12 d i s c o n n e c t f n : PassThruDisconnectFn ,
13 // / Read m e s s a g e s from a c o m m u n i c a t i o n c h a n n e l
14 r e a d m s g f n : PassThruReadMsgsFn ,
15 // / W r i t e m e s s a g e s t o a c o m m u n i c a t i o n c h a n n e l
16 w r i t e m s g f n : PassThruWriteMsgsFn ,
17 // / S t a r t a p e r i o d i c m e s s a g e
18 s t a r t p e r i o d i c f n : PassThruStartPeriodicMsgFn ,
19 // / Stop a p e r i o d i c m e s s a g e
20 s t o p p e r i o d i c f n : PassThruStopPeriodicMsgFn ,
21 // / S t a r t a f i l t e r on a c h a n n e l
22 s t a r t f i l t e r f n : PassThruStartMsgFilterFn ,
23 // / Stop a f i l t e r on a c h a n n e l
24 s t o p f i l t e r f n : PassThruStopMsgFilterFn ,
25 // / S e t programming v o l t a g e
26 s e t p r o g v f n : PassThruSetProgrammingVoltageFn ,
27 // / Get t h e l a s t d r i v e r e r r o r d e s c r i p t i o n i f ERR FAILED
28 g e t l a s t e r r f n : PassThruGetLastErrorFn ,
29 // / IOCTL
30 i o c t l f n : PassThruIoctlFn ,
31 // / Get d r i v e r d e t a i l s
32 r e a d v e r s i o n f n : PassThruReadVersionFn ,
33 }
In order to call the Passthru API functions, the Passthru library contains wrapper functions
such as the one shown below:
5
https://github.com/nagisa/rust_libloading
CHAPTER 3. METHODOLOGY 46
1 // type Pa ss Th r uC lo s eF n = unsafe extern " stdcall " fn ( device_id : u32 ) -> i32 ;
2 pub fn close (& mut self , dev_id : u32 ) -> Result <() > {
3 let res = unsafe { (& self . close_fn ) ( dev_id ) };
4 if res == 0 x00 {
5 self . is_connected = false ;
6 }
7 ret_res ( res , () )
8 }
The function above shows the abstraction libraries implementation of PassthruClose. This
function takes a mutable reference to the PassthruDrv struct (Similar to ’this’ in other lan-
guages), as well as the device ID to close. The function returns a Result with OK variant type
of unit. Then, the PassthruDrv’s close function is called in an unsafe block. The unsafe block
here is necessary as it is calling external code, which might not guarantee the same memory
safety as Rust. If the function succeeded, the the OK Variant is returned. If the function call
failed, then the Result’s Err variant is returned, along with the error code.
Another example of the abstraction this library does is with PassthruReadMsgs function.
This function is supposed to take a mutable reference to an array of PASSTHRU MSGS, and
attempts to fill the array up based on what the adapter can read. Instead, the abstraction
library’s implementation looks like the following:
1 // type P a s s T h r u R e a d M s g s F n = unsafe extern " stdcall " fn ( channel_id : u32 , msgs : * mut
PASSTHRU_MSG , num_msgs : * mut u32 , timeout : u32 ) -> i32 ;
2 pub fn read_messages (& self , channel_id : u32 , max_msgs : u32 , timeout : u32 ) -> Result
< Vec < PASSTHRU_MSG > > {
3 let mut msg_count : u32 = max_msgs ;
4 // Create a blank array of empty passthru messages according to the max we
should read
5 let mut write_array : Vec < PASSTHRU_MSG > = vec! [
6 PASSTHRU_MSG :: default () ;
7 max_msgs as usize
8 ];
9 // Call Passthru library P a s s t hr u R e a d M s g s
10 let res = unsafe {
11 (& self . read_msg_fn ) (
12 channel_id ,
13 write_array . as_mut_ptr () as * mut PASSTHRU_MSG ,
14 & mut msg_count as * mut u32 ,
15 timeout ,
16 )
17 };
18 // Error , but due to timeout waiting for more!
19 // Just return what we have and say OK!
20 if res == PassthruError :: E R R _ B U F F E R _ E M P T Y as i32 && msg_count ! = 0 {
21 write_array . truncate ( msg_count as usize ) ;
22 return ret_res (0 x00 , write_array ) ;
23 }
24 if msg_count ! = max_msgs {
25 // Trim the output vector to size
26 write_array . truncate ( msg_count as usize ) ;
27 }
28 ret_res ( res , write_array )
29 }
This function simplifies the PassthruReadMsgs call significantly for the application. It inter-
nally constructs an array to pass a mutable pointer to PassthruReadMsgs itself, then return
a Vector of PassthruMsgs that have been read by the adapter. If no messages are read, by
default the Passthru library will return an error (ERR BUFFER EMPTY). This function can
catch that error and simply return an empty array of messages, which is easier to handle in
higher levels of the communication server.
CHAPTER 3. METHODOLOGY 47
• button
• Progress bar
• Drop-down
• Image view
• Radio button
• Checkbox
• Text view
• Scroll view
• Padding widget
Each element of the user interface can have its own custom ’style’ struct applied to it, and
also have its own alignment (Relative to its parent in the user interface)
Interface style
Iced has the ability to apply a ’style’ struct on each element of the user interface. It was
decided that as part of the user interface, there should be a dark theme. This will hopefully
allow for less eye-strain when working with the application, especially in darker environments
such as garages.
To theme the entire user interface, there is a global variable which indicates which theme
is currently in use:
1 s t a t i c mut CURR THEME : S t y l e = S t y l e : : Dark ;
2 #[ d e r i v e ( Debug , Copy , Cl on e , Ord , P a r t i a l O r d , Eq , P a r t i a l E q ) ]
3 pub enum S t y l e {
4 Light ,
5 Dark ,
6 }
7
8 pub f n s e t d a r k t h e m e ( ) {
9 u n s a f e { CURR THEME = S t y l e : : Dark }
10 }
11
12 pub f n s e t l i g h t t h e m e ( ) {
13 u n s a f e { CURR THEME = S t y l e : : L i g h t }
14 }
15
16 pub f n t o g g l e t h e m e ( ) {
17 i f ∗ g e t t h e m e ( ) == S t y l e : : L i g h t {
18 set dark theme ()
19 } else {
20 set light theme ()
21 }
22 }
23
CHAPTER 3. METHODOLOGY 48
For the main user interface components, builder functions were created which can apply, then
return a newly created user interface element. Only helper for button creation will be shown,
but a similar process occurs for other UI elements. The user interface is somewhat inspired
by the Material design guidelines.
Buttons in the user interface comes in two flavours. Outlined, and Solid. Each button
flavour has eight different themes. Primary colour, Secondary colour, Success, Danger, Warn-
ing, Info, Light and Dark.
To create the buttons, the following code is used:
1 pub enum ButtonType {
2 Primary ,
3 Secondary ,
4 Success ,
5 Danger ,
6 Warning ,
7 Info ,
8 Light ,
9 Dark ,
10 }
11
12 i m p l ButtonType {
13 pub ( c r a t e ) f n g e t c o l o u r (& s e l f ) −> C o l o r {
14 match & s e l f {
15 ButtonType : : P r i m a r y => C o l o r : : f r o m r g b 8 ( 0 x0d , 0 x6e , 0 x f d ) ,
16 ButtonType : : S e c o n d a r y => C o l o r : : f r o m r g b 8 ( 0 x66 , 0 x10 , 0 x f 2 ) ,
17 ButtonType : : S u c c e s s => C o l o r : : f r o m r g b 8 ( 0 x00 , 0 xb7 , 0 x4a ) ,
18 ButtonType : : Danger => C o l o r : : f r o m r g b 8 ( 0 x f 9 , 0 x31 , 0 x54 ) ,
19 ButtonType : : Warning => C o l o r : : f r o m r g b 8 ( 0 x f f , 0 xa9 , 0 x00 ) ,
20 ButtonType : : I n f o => C o l o r : : f r o m r g b 8 ( 0 x39 , 0 xc0 , 0 x ed ) ,
21 ButtonType : : L i g h t => C o l o r : : f r o m r g b 8 ( 0 x f b , 0 x f b , 0 x f b ) ,
22 ButtonType : : Dark => C o l o r : : f r o m r g b 8 ( 0 x26 , 0 x26 , 0 x26 ) ,
23 }
24 }
25 }
26
27 pub f n b u t t o n c o l o u r e d <’a , T : Clo ne >(
28 s t a t e : &’ a mut b u t t o n : : S t a t e ,
29 t e x t : &s t r ,
30 b t n t y p e : ButtonType ,
31 ) −> Button <’a , T> {
32 l e t color = btn type . get colour () ;
33 B u t t o n : : new ( s t a t e , Text : : new ( t e x t ) )
34 . s t y l e ( B u t t o n S t y l e : : new ( c o l o r , f a l s e ) )
35 . padding (8)
36 }
The ButtonType enum has an implementation for each element to retrieve the desired colour
of the button. These colours come from the Material design guidelines. When creating a
button (button coloured), a standard Iced button is created, then a style is applied to it,
along with a desired padding.
The process of creating the style for the button looks like this:
1 pub s t r u c t B u t t o n S t y l e {
2 c o l o r : Color ,
3 i s o u t l i n e d : bool ,
4 }
5
6 impl ButtonStyle {
7 pub f n new ( c o l o r : C o l o r , i s o u t l i n e d : b o o l ) −> S e l f {
8 Self { color , i s o u t l i n e d }
9 }
10 }
11
12 impl button : : StyleSheet f o r ButtonStyle {
13 f n a c t i v e (& s e l f ) −> S t y l e {
14 match s u p e r : : g e t t h e m e ( ) {
15 s u p e r : : S t y l e : : L i g h t => b u t t o n : : S t y l e {
16 shadow offset : Default : : default () ,
17 background : i f s e l f . i s o u t l i n e d {
18 WHITE . i n t o ( )
19 } else {
20 s e l f . color . into ()
21 },
22 b o r d e r r a d i u s : BUTTON RADIUS ,
23 border width : i f s e l f . i s o u t l i n e d {
24 BUTTON BORDER WIDTH
25 } else {
26 0.0
27 },
CHAPTER 3. METHODOLOGY 49
In this code block, the theme style for the button changes based on the global theme of the
rest of the interface (Dark/Light). Then the returned button style is also modified based on
if the button is supposed to be coloured (solid), or outlined. Not shown here is the styling for
a hovered button, pressed button, or disabled button. All 3 follow the exact same method of
creating the return style as the active button style, except with different colours.
Launcher
The launcher is responsible for enumerating located diagnostic adapters on the users computer,
and to then display them to the user, allowing them to select which adapter to use with
OpenVehicleDiag.
When the user selects Launch after selecting an adapter. A ComServer is created with
the specified adapter API. If an error occurs during this process, then the launcher bails out
and displays the error to the end user.
As seen in figure 3.15, the Passthru library for the M2 was loaded, but returned an error
as the M2 was not connected to the PC. This error is then displayed in the launcher.
The code to achieve this looks like the following in the Launcher’s update function:
1 pub f n u p d a t e (&mut s e l f , msg : &L a u n c h e r M e s s a g e ) −> Option<WindowMessage> {
2 match msg {
3 L a u n c h e r M e s s a g e : : S w i t c h A P I ( a p i ) => s e l f . a p i s e l e c t i o n = ∗ a p i ,
4 L a u n c h e r M e s s a g e : : D e v i c e S e l e c t e d ( d ) => {
5 i f s e l f . a p i s e l e c t i o n == API : : P a s s t h r u {
6 s e l f . selected device passthru = d . clone ()
7 } e l s e i f s e l f . a p i s e l e c t i o n == API : : DPdu {
8 s e l f . selected device dpdu = d . clone ()
9 }
10 }
11 L a u n c h e r M e s s a g e : : L a u n c h R e q u e s t e d => {
12 i f s e l f . a p i s e l e c t i o n == API : : P a s s t h r u {
13 match s e l f . g e t d e v i c e p a s s t h r u ( ) {
14 Ok ( ( d e t a i l s , d r i v e r ) ) => {
15 l e t mut s e r v e r = P a s s t h r u A p i : : new ( d e t a i l s , d r i v e r ) ;
16 i f l e t Err ( e ) = s e r v e r . open device () {
17 s e l f . status text = e . t o s t r i n g ()
18 } else {
19 // Ready t o l a u n c h OVD!
20 r e t u r n Some ( WindowMessage : : S t a r t A p p ( s e r v e r . c l o n e b o x ( ) ) ) ;
21 }
22 }
23 E r r ( x ) => s e l f . s t a t u s t e x t = x . t o s t r i n g ( ) ,
24 }
25 } e l s e i f s e l f . a p i s e l e c t i o n == API : : DPdu {
26 // TODO D−PDU L a u n c h i n g
27 }
28 }
29 }
30 None
31 }
This code block receives a signal ’msg’ when a UI element is interacted with. In the case
of ”LaunchRequested”, which occurs when the Launch button is pressed. When this occurs,
if the API selection is Passthru, and the listed device was located, the PassthruAPI library
attempts to load the device. If it was OK, then a message is emitted to the Main Window,
telling it to launch the Home page. As part of this message, the dynamic ComServer (from
the PassthruAPI) is passed to the main window.
If the loading of the Passthru device was unsuccessful, then the status text is set to the
error message returned by the Passthru API.
Home page
The home page is a gateway to all of OpenVehicleDiag’s various functions and pages. It also
displays details about the application, and adapter specifications being utilized.
CHAPTER 3. METHODOLOGY 51
Status bar
The status bar is a small part of the overall user interface. Taking its inspiration from Daimler’s
DAS software, it provides status on the connection to the vehicle (If an interface is currently
open and communicating with a vehicle), and also the battery voltage of the vehicle. The
status bar also provides a ’Go Home’ button, and also a ’Back’ button which allows users
to quickly go back, or go to the home page. Both the ’Back’ and ’Go Home’ button can
be disabled by any page in the user interface. This is used so that certain operations can
be cancelled safely within the main user interface, rather than by clicking Go back or home,
which forcibly destroys the current page.
The battery voltage is queried every two seconds. This is done by submitting ’events’ to
the main window every two seconds:
1 f n s u b s c r i p t i o n (& s e l f ) −> S u b s c r i p t i o n <S e l f : : Message> {
2 l e t mut b a t c h : Vec<S u b s c r i p t i o n <WindowMessage>> = v e c ! [ ] ;
3 if self . poll voltage {
4 b a t c h . push (
5 t i m e : : e v e r y ( s t d : : t i m e : : D u r a t i o n : : f r o m s e c s ( 2 ) ) . map ( WindowMessage : : S t a t u s U p d a t e ) ,
6 );
7 S u b s c r i p t i o n : : batch ( batch )
8 }
This allows for the battery voltage to be polled every two seconds, but to also be done on
the same thread as the user interface, rather than a background thread. This is done because
certain API’s such as Passthru do not support multi-threading.
the variable ’poll voltage’ is set when the main window loads, by querying the ComServer’s
DeviceCapabilities. If battery voltage reading is supported, ’poll voltage’ is true, else it is false.
This is then utilized by the Status bar draw code:
1 let v = if self . poll voltage {
2 i f s e l f . v o l t a g e < 1 2 . 0 && s e l f . v o l t a g e > 11.5 {
3 t e x t ( f o r m a t ! ( ”{}V” , s e l f . v o l t a g e ) . a s s t r ( ) , TextType : : Warning ) // Amber
4 } e l s e i f s e l f . voltage < 11.5 {
5 t e x t ( f o r m a t ! ( ”{}V” , s e l f . v o l t a g e ) . a s s t r ( ) , TextType : : Danger ) // Red
6 } else {
7 t e x t ( f o r m a t ! ( ”{}V” , s e l f . v o l t a g e ) . a s s t r ( ) , TextType : : S u c c e s s ) // G r e e n
8 }
9 } else {
CHAPTER 3. METHODOLOGY 52
10 t e x t ( ” Not s u p p o r t e d ” , TextType : : D i s a b l e d ) // G r e y
11 } ;
As seen above, there are 4 styles for battery voltage display. If the voltage is between 11.5V
and 12.0V , then the battery voltage colour will be Amber. If the voltage is less that 11.5V ,
the text is Red. Any voltage higher than 12.0V is displayed in Green. If battery voltage
reading is not supported by the adapter, Grey text saying ”Not Supported” will be displayed.
CAN Analyser
The CAN Analyser is a simple page which is designed to show CAN traffic on the OBD-II
port. The user interface initially has a simple ’Connect’ button. When pressed, the CAN
Analyser will begin by setting up an open CAN interface on the ComServer, and wake up the
OBD-II port by sending a OBD-II over CAN request message. This message is used since the
vast majority of cars will respond to it, thus wake up the OBD-II port allowing for traffic to
be monitored. This button is also togglable to disable the CAN Interface. When pressed, the
following code is executed:
1 if s e l f . i s c o n n e c t e d { // A l r e a d y c o n n e c t e d − We a r e d i s c o n n e c t i n g CAN
2 i f l e t Err ( e ) = s e l f . s e r v e r . as mut ( ) . c l o s e c a n i n t e r f a c e ( ) {
3 s e l f . s t a t u s t e x t = f o r m a t ! ( ” E r r o r c l o s i n g CAN I n t e r f a c e {}” , e )
4 } else {
5 s e l f . is connected = false ;
6 s e l f . can queue . c l e a r () ;
7 }
8
9 }
10 // Try t o open CAN I n t e r f a c e ( D e t e c t e r r o r i f any )
11 e l s e i f l e t E r r ( e ) = s e l f . s e r v e r . a s m u t ( ) . o p e n c a n i n t e r f a c e ( 5 0 0 000 , f a l s e ) {
12 s e l f . s t a t u s t e x t = f o r m a t ! ( ” E r r o r o p e n i n g CAN I n t e r f a c e {}” , e )
13 }
14 // Opening CAN i n t e r f a c e was OK!
15 else {
16 s e l f . is connected = true ;
17 i f l e t Err ( e ) =
18 self . server
19 . as mut ( )
20 . a d d c a n f i l t e r ( F i l t e r T y p e : : Pass , 0 x0000 , 0 x0000 )
21 {
22 s e l f . s t a t u s t e x t = f o r m a t ! ( ” E r r o r s e t t i n g CAN F i l t e r {}” , e )
23 // Send OBD−I I wakeup p a c k e t t o wake up c a r ’ s OBD−I I p o r t
24 } e l s e i f l e t Err ( e ) = s e l f . s e r v e r . send can packets (
25 &[ CanFrame : : new (
26 0x07DF ,
27 &[0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 ] ,
28 )],
29 0,
30 ) {
31 s e l f . s t a t u s t e x t = f o r m a t ! ( ” E r r o r s e n d i n g wake−up p a c k e t {}” , e )
32 }
33 }
Once connected, the CAN interface is polled every 10 milliseconds for new data:
1 pub f n s u b s c r i p t i o n (& s e l f ) −> S u b s c r i p t i o n <T r a c e r M e s s a g e> {
2 i f s e l f . is connected {
3 r e t u r n t i m e : : e v e r y ( s t d : : t i m e : : D u r a t i o n : : f r o m m i l l i s ( 1 0 ) ) . map ( T r a c e r M e s s a g e : : NewData ) ;
4 }
5 S u b s c r i p t i o n : : none ( )
6 }
When the message is received, the following code is executed to push each CAN Frame to its
own location within a HashMap
CHAPTER 3. METHODOLOGY 53
1 if l e t Ok (m) = s e l f . s e r v e r . a s r e f ( ) . r e a d c a n p a c k e t s ( 0 , 1 0 0 ) {
2 for f in m {
3 s e l f . can queue . i n s e r t ( f . id , f ) ;
4 }
5 }
Each hashMap entry uses the CAN ID as a unique key (Which will always be unique), with
the CAN Data array being the value stored in the hashmap.
In order to display CAN Frames to the user, there are two available formats, which can be
switched by using a checkbox:
25 r e t u r n E r r ( P r o t o c o l E r r o r : : Timeout ) ;
26 }
27 l e t mut t m p r e s = r e s [ 0 ] . d a t a . c l o n e ( ) ;
28 i f t m p r e s [ 0 ] == 0 x7F && t m p r e s [ 2 ] == 0 x78 {
29 // R e s p o n s e P e n d i n g
30 p r i n t l n ! ( ”DIAG − ECU i s p r o c e s s i n g r e q u e s t − W a i t i n g ! ” ) ;
31 l e t s t a r t = I n s t a n t : : now ( ) ;
32 w h i l e s t a r t . e l a p s e d ( ) . a s m i l l i s ( ) <= 2000 {
33 // ECU a c c e p t e d t h e r e q u e s t , b u t c a n n o t r e s p o n d a t t h e moment!
34 i f l e t Some ( msg ) = s e r v e r . r e a d i s o 1 5 7 6 5 p a c k e t s ( 0 , 1 ) ? . g e t ( 0 ) {
35 t m p r e s = msg . d a t a . c l o n e ( ) ;
36 }
37 }
38 }
39 i f t m p r e s [ 0 ] == 0 x7F {
40 // S t i l l e r r o r : (
41 E r r ( P r o t o c o l E r r o r : : P r o t o c o l E r r o r ( Box : : new (
42 S e l f : : Error : : from byte ( tmp res [ 2 ] ) ,
43 )))
44 } e l s e i f t m p r e s [ 0 ] == ( cmd + 0 x40 ) {
45 Ok ( t m p r e s )
46 } else {
47 eprintln! (
48 ”DIAG − Command r e s p o n s e d i d n o t match r e q u e s t ? Send : { : 0 2X} − Recv : { : 0 2X}” ,
49 cmd , t m p r e s [ 0 ]
50 );
51 E r r ( P r o t o c o l E r r o r : : Timeout )
52 }
53 }
54 }
55 }
This function starts by building an ISO15765Data payload to transmit to the ECU. The ’cmd’
byte is the SID of the protocol, and args contain any additional data. If ’receive response’ is
set, the server does not require a response from the ECU, so it immediately bails upon sending
the data. If response from the ECU is required, it begins waiting for up to 2 seconds for a
response from the ECU.
If the ECU responds with an error, it is returned as a ProtocolError. However, if the
error code is 0x78, then the server begins a holding sequence, blocking the thread for up to
another 2 seconds until the ECU responds. This is because the error 0x78 implies the ECU has
accepted the request, but is unable to respond to the request immediately. Both KWP2000
and UDS state that in this event, the diagnostic server is to stop sending any data to the
ECU (Including TesterPresent commands), until the ECU responds, or a timeout occurs. This
second timeout is 2 seconds. If the ECU responds with a positive response, the response is
returned as a byte array back to whichever diagnostic server sent the message.
Utilizing both the KWP2000 specification [DaimlerChrysler (2002)] and UDS specification
[ISO (2006)], basic diagnostic functions were implemented such as clear / Read DTCs, as well
as some additional functions for KWP2000 which allow for querying detailed ECU software
and hardware version data.
For reading DTCs from ECUs, each protocol returns a different data format, so the Diag-
nostic servers consolidate the data into a generic DTC structure. Below is the code to read
DTCs with KWP2000:
1 f n r e a d e r r o r s (& s e l f ) −> P r o t o c o l R e s u l t <Vec<DTC>> {
2 // 0 x02 − R e q u e s t Hex DTCs a s 2 b y t e s
3 // 0 xFF00 − R e q u e s t a l l DTCs ( Mandatory p e r KWP2000 )
4 l e t mut b y t e s = s e l f . run command ( S e r v i c e : : ReadDTCByStatus . i n t o ( ) , &[0 x02 , 0xFF , 0 x00 ] ) ? ;
5 bytes . drain ( . . 1 ) ;
6 l e t count = bytes [ 0 ] as u s i z e ;
7 bytes . drain ( 0 . . 1 ) ;
8
9 l e t mut r e s : Vec<DTC> = Vec : : new ( ) ;
10 for i n 0 . . count {
11 l e t name = f o r m a t ! ( ” { : 0 2X}{:02X}” , b y t e s [ 0 ] , b y t e s [ 1 ] ) ;
12 l e t status = bytes [ 2 ] ;
13 // l e t f l a g = ( s t a t u s >> 4 & 0 b00000001 ) > 0 ;
14 // 0 b011
15 l e t s t o r a g e s t a t e = ( s t a t u s >> 5 ) & 0 b0000011 ;
16
17 l e t s t a t e = match s t o r a g e s t a t e {
18 1 => DTCState : : S t o r e d ,
19 2 => DTCState : : Pending ,
20 3 => DTCState : : Permanent ,
21 => DTCState : : None
22 };
CHAPTER 3. METHODOLOGY 55
23
24 // I s c h e c k e n g i n e l i g h t on ?
25 l e t m i l = ( s t a t u s >> 7 & 0 b00000001 ) > 0 ;
26
27 r e s . push (DTC {
28 e r r o r : name ,
29 state ,
30 c h e c k e n g i n e o n : mil ,
31 i d : ( ( b y t e s [ 0 ] a s u32 ) << 8 ) | b y t e s [ 1 ] a s u32
32 }) ;
33 b y t e s . d r a i n ( 0 . . 3 ) ; // DTC i s 3 b y t e s ( 1 f o r s t a t u s , 2 f o r t h e ID )
34 }
35 Ok ( r e s )
36 }
Figure 3.17: Warning message presented to the user prior to the ECU scan
To begin the scan, the scanner begins by setting up an open CAN Interface on the appli-
cations ComServer, and configures the filter to be open to all incoming traffic:
1 s e l f . s e r v e r . o p e n c a n i n t e r f a c e ( 5 0 0 000 , f a l s e ) ;
2 s e l f . s e r v e r . a d d c a n f i l t e r ( commapi : : comm api : : F i l t e r T y p e : : Pass , 0 x00000000 , 0 x00000000 ) ;
This allows for OpenVehicleDiag to receive ALL incoming CAN Packets from the OBD-II port
as it is an open filter.
Next, an OBD-II request is sent to the OBD-II ports CAN interface. This is done to
wake-up the CAN Interface on the vehicles side:
1 s e l f . s e r v e r . s e n d c a n p a c k e t s (&[ CanFrame : : new ( 0 x07DF , &[0 x09 , 0 x02 ] ) ] , 0 )
The next step is important. In order to ensure that OpenVehicleDiag does not accidentally
send a CAN Frame to a vehicle which already exists on the bus, it listens to traffic on the
OBD-II port for 15 seconds. During this listening period, any incoming CAN Traffic is noted,
and the CAN ID’s of the incoming traffic are added to a blacklist of CAN ID’s to not send to
the vehicle for probing ISO-TP endpoints.
CHAPTER 3. METHODOLOGY 56
After the initial poll period, the scan begins iterating over every CAN ID between 0x000 and
0x7FF (CAN’s Maximum CAN ID with Standard 11 bit addressing). During every iteration,
it will send a fake ISO-TP Start frame, and poll for 100ms for incomming data that is not in
the existing blacklist. If the incomming data looks like an ISO-TP Flow control message, it is
added to an array, along with the Send CAN ID used.
1 s e l f . get next canid () ;
2 s e l f . server . c l e a r c a n r x b u f f e r () ;
3 // Send a f a k e ISO−TP f i r s t f r a m e . T e l l t h e p o t e n t i a l ECU we a r e s e n d i n g 16 b y t e s t o i t . I f i t u s e s ISO−TP , i t
’ l l s e n d back a
4 // f l o w c o n t r o l m e s s a g e back t o OVD
5 s e l f . server . send can packets (
6 &[ CanFrame : : new (
7 self . curr scan id ,
8 // Fake p a y l o a d t o t a r g e t a d d r e s s . S e n d i n g 0 x10 b y t e s o f d a t a ( 1 6 b y t e s )
9 &[0 x10 , 0 x10 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 ] ,
10 )],
11 0,
12 ) ;
13 s e l f . c l o c k = I n s t a n t : : now ( ) ;
14 w h i l e s e l f . c l o c k . e l a p s e d ( ) . a s m i l l i s ( ) < 100 {
15 // Keep p o l l i n g CAN!
16 f o r frame i n &s e l f . s e r v e r . r e a d c a n p a c k e t s (0 , 10000) . u n w r a p o r d e f a u l t ( ) {
17 i f s e l f . c a n t r a f f i c i d l i s t . g e t (& f r a m e . i d ) . i s n o n e ( ) {
18 // I t s a new f r a m e we haven ’ t s e e n b e f o r e !
19 l e t payload = frame . g e t d a t a ( ) ;
20 i f p a y l o a d [ 0 ] == 0 x30 && p a y l o a d . l e n ( ) == 8 {
21 // P o s s i b l e r e c v ID ? − I t m i g h t p i c k up m u l t i p l e I D s d u r i n g t h e s c a n , we f i l t e r i t l a t e r on
22 i f l e t Some ( r ) = s e l f . s t a g e 2 r e s u l t s . g e t m u t (& s e l f . c u r r s c a n i d ) {
23 r . push ( f r a m e . i d )
24 } else {
25 s e l f . s t a g e 2 r e s u l t s . i n s e r t ( s e l f . c u r r s c a n i d , v e c ! [ frame . i d ] ) ;
26 }
27 }
28 }
29 }
30 }
Once this initial list of potential ISO-TP endpoints has been created, it is then iterated over
again, but this time using a specified CAN Filter in order to remove any false positives. If
the incoming frame is seen again with the CAN filter applied, then it is defiantly an ISO-TP
endpoint.
1 l e t k e y s : Vec<u32> = s e l f . s t a g e 2 r e s u l t s . k e y s ( ) . c o p i e d ( ) . c o l l e c t ( ) ;
2 let filter id = self
3 . stage2 results
4 . g e t (& k e y s [ s e l f . c u r r s c a n i d a s u s i z e ] )
5 . unwrap ( ) ;
6 self . filter idx = self
7 . server
CHAPTER 3. METHODOLOGY 57
8 // S p e c i f i e d f i l t e r w i t h o u r s p e c i f i c CAN ID we a r e s e a r c h i n g f o r .
9 . a d d c a n f i l t e r ( commapi : : comm api : : F i l t e r T y p e : : Pass , f i l t e r i d [ 0 ] , 0xFFFF )
10 . unwrap ( ) ;
11 s e l f . s e r v e r . s e n d c a n p a c k e t s (
12 &[ CanFrame : : new (
13 keys [ s e l f . c u r r s c a n i d as u s i z e ] ,
14 &[0 x10 , 0 x10 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 ] ,
15 ) ] , 0) ;
16 f o r f r a m e i n & s e l f . s e r v e r . r e a d c a n p a c k e t s ( 0 , 1 0 0 0 0 ) . u n w r a p o r d e f a u l t ( ) {
17 l e t payload = frame . g e t d a t a ( ) ;
18 i f p a y l o a d [ 0 ] == 0 x30 && p a y l o a d . l e n ( ) == 8 {
19 // True p o s i t i v e ! We can add t h e C o n f i g t o l i s t !
20 s e l f . s t a g e 3 r e s u l t s . push ( I S O 1 5 7 6 5 C o n f i g {
21 baud : 5 0 0 0 0 0 ,
22 // −1 i s c u r r e n t s c a n ID w h i l s t i n t h i s l o o p
23 s e n d i d : ∗ k e y s . g e t ( ( s e l f . c u r r s c a n i d − 1 ) a s u s i z e ) . unwrap ( ) ,
24 r e c v i d : frame . id ,
25 b l o c k s i z e : p a y l o a d [ 1 ] a s u32 ,
26 s e p t i m e : p a y l o a d [ 2 ] a s u32 ,
27 use ext isotp : false ,
28 use ext can : false
29 })
30 }
31 }
At this point, a full list of positive ISO-TP endpoints on the vehicle have been recorded. Next,
the scanner needs to determine which diagnostic protocol each endpoint supports. For each
ISO15765 Config generated in the ISO-TP scan, it is iterated over, and both a KWP2000
and UDS diagnostic server are created for the ISO-TP endpoint. Due to the architecture of
both servers, if the ECU rejects the start diagnostic session command ([0x10, 0x03] for UDS,
[0x10, 0x92] for KWP2000), then the diagnostic server will return an Error upon initialization,
and therefore the scanner knows the ECU does not support the protocol.
1 l e t ecu = s e l f . s t a g e 3 r e s u l t s [ s e l f . c u r r s c a n i d as u s i z e ] ;
2
3 l e t mut e c u r e s = E C U D i a g S e t t i n g s {
4 name : ”Unknown ECU name” . i n t o ( ) ,
5 s e n d i d : ecu . s e n d i d ,
6 f l o w c o n t r o l i d : ecu . r e c v i d ,
7 b l o c k s i z e : ecu . b l o c k s i z e ,
8 sep time ms : ecu . sep time ,
9 uds support : false ,
10 kwp support : f a l s e ,
11 } ;
12
13 // I n t e r r o g a t e t h e ECU w i t h KWP2000 e x t e n d e d d i a g n o s t i c s e s s i o n
14 match KWP2000ECU : : s t a r t d i a g s e s s i o n ( s e l f . s e r v e r . c l o n e ( ) , &ecu , None ) {
15 Ok ( mut s ) => {
16 i f l e t Ok ( i d ) = r e a d d c x m m c i d (& s ) {
17 e c u r e s . name = f o r m a t ! ( ”ECU P a r t number : {}” , i d . p a r t n u m b e r ) ;
18 p r i n t l n ! ( ”ECU 0 x { : 0 4X} s u p p o r t s KWP2000!” , e c u . s e n d i d ) ;
19 e c u r e s . kwp support = true ;
20 }
21 s . e x i t d i a g s e s s i o n () ;
22 }
CHAPTER 3. METHODOLOGY 58
23 E r r ( e ) => {
24 p r i n t l n ! ( ”KWP2000 s e r v e r f a i l e d ! {:?} ” , e ) ;
25 }
26 }
27 // I n t e r r o g a t e t h e ECU w i t h UDS e x t e n d e d d i a g n o s t i c s e s s i o n
28 match UDSECU : : s t a r t d i a g s e s s i o n ( s e l f . s e r v e r . c l o n e ( ) , &ecu , None ) {
29 Ok ( mut s ) => {
30 // TODO f i n d a UDS o n l y CMD t o t e s t w i t h
31 p r i n t l n ! ( ”ECU 0 x { : 0 4X} s u p p o r t s UDS!” , e c u . s e n d i d ) ;
32 s e l f . s t a g e 4 r e s u l t s [ s e l f . c u r r s c a n i d as u s i z e ] . uds support = t r u e ;
33 s . e x i t d i a g s e s s i o n () ;
34 }
35 E r r ( e ) => {
36 p r i n t l n ! ( ”UDS s e r v e r f a i l e d ! {:?} ” , e ) ;
37 }
38 }
39 s e l f . c u r r s c a n i d += 1 ;
Since KWP2000 has a standard way of reading the ECU part number, Line 16 shows that
the KWP2000 scan attempt also tries to grab the part number from the ECU. If successful,
this part number replaces the default ”Unknown ECU Name” text. This is because this part
number can be easily searched online to find out what ECU it is.
As mentioned above, the reason for showing a large warning to the end user prior to
starting the scan is because putting ECUs into diagnostic mode will cause them to partially
deactivate. On modern cars, this can result in hundreds of warning lights illuminating on the
users instrument cluster. These warning messages disappear after a couple seconds (As the
ECU returns into its default session state).
Figure 3.22: Instrument cluster warning lights being displayed during the final
stages of ECU detection
The last stage is to present the results of the scan to the user, and save the results to a
JSON File.
CHAPTER 3. METHODOLOGY 59
As the above image shows, this is the hom page of the JSON diagnostic session. At the
top is the name of the ECU along with the vendor name and software version of the ECU.
This is all found within the JSON.
1 l e t d i a g s e r v e r t y p e = match c o n n e c t i o n s e t t i n g s . s e r v e r t y p e {
2 common : : schema : : S e r v e r T y p e : : UDS => D i a g P r o t o c o l : : UDS ,
3 common : : schema : : S e r v e r T y p e : : KWP2000 => D i a g P r o t o c o l : : KWP2000
4 };
5 p r i n t l n ! ( ” D e t e c t . ECU u s e s {:?} ” , d i a g s e r v e r t y p e ) ;
6
7 // F o r now , Diag s e r v e r ONLY s u p p o r t s ISO−TP , n o t L I N !
8 l e t c r e a t e s e r v e r = match c o n n e c t i o n s e t t i n g s . c o n n e c t i o n t y p e {
9 ConType : : ISOTP { b l o c k s i z e , s t m i n , e x t i s o t p a d d r , e x t c a n a d d r } => {
10 l e t cfg = ISO15765Config {
11 baud : c o n n e c t i o n s e t t i n g s . baud ,
12 send id : connection settings . send id ,
13 recv id : connection settings . recv id ,
14 block size : blocksize ,
15 sep time : st min ,
16 use ext can : ext can addr ,
17 use ext isotp : ext isotp addr
18 };
19 // Dynamic d i a g n o s t i c s e r v e r c r e a t i o n
20 D i a g S e r v e r : : new ( c o m m s e r v e r , &c f g , c o n n e c t i o n s e t t i n g s . g l o b a l s e n d i d d i a g s e r v e r t y p e )
21 },
22 ConType : : LIN { . . } => r e t u r n E r r ( S e s s i o n E r r o r : : O t h e r ( ”K−L i n e i s n o t i m p l e m e n t e d a t t h i s t i m e ” . i n t o ( ) ) )
23 } ;
The above code reads the JSON data about connection info before building an ISOTP con-
figuration setting, and applying it to a special ’DiagServer’ class. This class is a dynamic
wrapper around both the KWP2000 and UDS diagnostic servers.
DTC Errors
When the ’read errors’ button is pressed, the JSON Session requests the underlying diagnostic
server to read all the DTCs stored on the ECU. Then, for every DTC which is read, the session
will then query freeze frame data about it. This returns both a DTC struct, and a sequence of
bytes which represents the DTC Freeze frame request response message. Using the decoder
function within the JSON, as mentioned earlier in section 3.3, the freeze frame bytes are fed
through a decoder for the DTC’s freeze frame array, in order to extract data about the freeze
frame. Also, each read DTC’s ID is cross-referenced with the DTC list in JSON. If a matching
name is found, the error description is also presented to the end user. This is all shown in the
DTC error view:
CHAPTER 3. METHODOLOGY 61
The user can click on each DTC, and the freeze frame table will display the interpreted
freeze-frame data from the ECU. There is also a button to clear DTCs stored on the ECU.
Since this utilizes KWP2000 or UDS, it is indeed possible to clear Permanent DTCs, unlike
with generic OBD-II applications.
When selected and executed, the log view displays the outputted presentation data using
the decoder functions found in section 3.3. If the ECU responds negatively to the request, a
human readable error message is displayed in red:
CHAPTER 3. METHODOLOGY 62
These error messages come from either the ComServer (Indicating something wrong with
the adapter), or DiagServer (Indicating the ECU Rejected the request message).
3.6 Summary
As shown in this chapter, it has been shown that it is possible to implement all 3 major parts
of this project, and all whilst keeping the code modular enough to be easily expanded on at
a later date.
Especially with OpenVehicleDiag’s UI, there are a lot of details not covered by this report,
mainly due to the length of the implementation, however this report has covered the major
points which would be useful to 99% of end users when it comes to car diagnostics.
Chapter 4
In this chapter, results and conclusions of utilizing OpenVehicleDiag with be discussed, as well
as validating that the Open source J2534 (Passthru) driver works with other software that
utilizes the Passthru protocol
However, DAS only transmit small ISO-TP payloads to and from the ECU. Nothing comes
close to the maximum 4096 byte limit of the ISO-TP protocol. Therefore, in order to verify
that the driver and adapter can indeed handle the limits of the ISO-TP protocol, my own cars
ECU was purposly bricked (Put into a bootloader state), and using Veidmao (Daimler’s ECU
Flashing tool that supports the J2534 API), was successfully restored. As shown in the below
63
CHAPTER 4. RESULTS, DISCUSSION AND ANALYSIS 64
image, Vediamo sends thousands of 4096 byte ISO-TP payloads to the adapter to send to the
ECU over ISO-TP, which contain the data to be flashed onto the ECU. Also transmitted are
some smaller payloads, which keep the ECU in a flash diagnostic session and to control other
ECUs in the vehicle.
Vediamo tells the other ECUs in the vehicle to stop sending normal CAN messages, and
go silent. This allows the full 500kbps of the CAN Network to be utilized for the firmware
update, which in turn reduces the chances of lost CAN Packets and makes the flashing process
faster.
Figure 4.2: Vediamo utilizing the custom J2534 adapter to flash a ECU
From running these 2 applications with the custom Passthru driver, it is possible to conclude
that for the 2 implemented protocols (CAN and ISO-TP), the adapter is fully function and
therefore is a success.
As seen in table 4.1, the automated scanner only located diagnosable ECUs. the ECU
name is found by cross referencing the part number in the JSON with google, which gives the
name of the ECU. This unfortunately is not possible with UDS, so table 4.2 does not show
ECU identification data.
One interesting thing about these two results is that in the W203, it appears the OBD-II
port is properly firewalled, only allowing diagnosable ISO-TP addresses to be located, without
also locating other ISO-TP endpoints such as Radio to Instrument cluster communication.
This however is not true for the W246, as it located multiple ISO-TP endpoints in the vehicle
which don’t support either UDS or KWP2000, meaning these are likely ECU to ECU ISO-TP
endpoints.
When pressing the ’clear errors’ button, the ECU successfully clears all these errors, the
JSON session re-reads the DTCs on the ECU immediately after clearing to verify that the
errors have actually been cleared.
CHAPTER 4. RESULTS, DISCUSSION AND ANALYSIS 67
To prove that this works with multiple ECUs that are not necessarily engine ECUs, here is
OpenVehicleDiag communicating with EGS52. This is a transmission controller for the 722.6
series gearboxes by Mercedes. In this picture, OpenVehicleDiag is connected to the ECU and
reading data from the ECU about solenoid pressures. This data can only normally be retrieved
using Daimler’s own diagnostic software.
Lastly, here is the JSON sesison working with the Mercedes W246. In the below image,
the JSON session is being used to talk to the vehicles instrument cluster to read various data:
This proves that both KWP2000 and UDS have been correctly implemented within Open-
VehicleDiag, and work reliably to do ECU diagnostics on both older and modern vehicles
without any proprietary software.
allows for basic diagnostics, but still being able to read and clear DTCs from ECUs that
cannot normally be done under OBD-II. This works by opening up a generic KWP2000 or
UDS session based on the output of the ECU scan results:
In this interface, the user has to manually enter commands to the ECU in hex form
(However there is a button to read and clear DTCs). When reading DTCs in this mode,
only the name of the error and state of the DTC can be read, the description of the error is
unknown:
4.3 Summary
As shown in this chapter, everything mentioned in section 1 has been achieved, and is working
reliably enough for it to be used in vehicles. Since a lot of OpenVehicleDiag’s UI features
are dynamic, a lot of the user interface has not been demonstrated in this chapter, just the
main points. However, there is a video1 that shows the entire interface and feature-set in use.
There are also additional images of other parts of OpenVehicleDiag’s user interface located
at A.0.2.
The JSON diagnostic session results show that the decoder functions in JSON Schema
code work perfectly as well, being able to decode Enums, Booleans, Integers and Strings from
ECU response message byte streams, and nicely present them to the end user in a way which
they can understand. The only thing that could not be tested here is verifying the decoder
works with different endiannes. This is because the vast majority of ECUs utilize Big-Endian.
1
https://youtube.com/_k-dWdNRVr0
Chapter 5
5.1 Conclusions
With the initial goal of this project being to attempt to create an open source alternative
to commercial / proprietary diagnostic software for vehicle ECUs, as well as an open storage
format for ECU diagnostic data, the initial results as outlined in this report have shown that
it is indeed possible.
The Passthru library has shown that it is indeed possible to write firmware for a custom
Passthru compliant adapter, and utilize it with applications that utilize the API. Although
K-Line and J1850 based protocols were not implemented as part of this project, the testing
with just CAN and ISO-TP protocols proves that the M2 is fully compliant with the API
protocol, and more importantly, is stable.
With OpenVehicleDiag, the application has proven that a cross-platform ECU diagnostics
platform can be achieved, and more importantly, be done in a way which can theoretically
work with any vehicle, without any prior knowledge of the vehicle (For basic diagnostics
with OBD-II, KWP2000 and UDS). Combining OpenVehicleDiag with the JSON Schema can
improve the diagnostic capability of the application significantly, allowing for some functions
only found in OEM tools.
The JSON Schema has shown that there can be an easy accessible replacement for the
ODX specification. However, there are things that are missing, which would need to be added
later on in order for it to be a true competitor to the ODX specification. In diagnostic tools,
there are tests which are defined as a list of operations to perform on the ECU
69
CHAPTER 5. CONCLUSIONS AND FUTURE WORK 70
are also plans to create a D-PDU (2.2.1) driver for both the M2 and A0, and add D-PDU
functionality to OpenVehicleDiag itself, which would widen the list of supported protocols and
existing adapter that can be utilized.
OpenVehicleDiag at the time of writing this now has initial support for SocketCAN [ker-
nel.org (n.d.)] on Linux based OS’s, meaning that in theory it is possible to use OpenVehi-
cleDiag on nothing more than a raspberry Pi and a $5 CAN Shield based on the MCP2515
CAN Transceiver (Assuming the target vehicle supports CAN). This will hopefully encourage
more users to utilize OpenVehicleDiag due just how low cost this solution will be compared
to purchasing other adapter.
Stated in 1, it was mentioned that ECU Flashing will not be a part of OpenVehicleDiag,
however, eventually it might be a good idea to add ECU firmware dumping to OpenVehicle-
Diag, which would allow more advanced users who want to reverse engineer their own ECUs
to dump the firmware of the ECU and load it up in a disassembler such as Ghidra to under-
stand how it operates. This would aid in the development of open source ECU firmware, such
as projects like Speeduino2 . This is because every engine operates slightly differently, and
also modern engine ECUs broadcast tons of CAN Data to other ECUs in a vehicle. Reverse
engineering ECU firmware will allow such projects to cater for more engines and vehicles.
Lastly, one area of future work would be to create a Graphical user interface for users to
create the JSON format used for ECU diagnostics, using simple ’drag and drop’ blocks. This
would vastly simplify the process of creating ECU diagnostic data in JSON. This would be a
direct parallel to Vector’s ODXStudio, which allows for OEMs to create ODX diagnostic data
using a graphical tool, and then for the data to be saved in an ODX XML file.
2
https://speeduino.com/home
Chapter 6
Reflection
This project has given me loads of insights and understanding of how ECU diagnostic protocols
function and how easy they actually are to implement once obtaining the correct knowledge on
the specification. One interesting skill from this project is creating a user interface dynamically
in pure code without any form of UI designer. When looking back at the original objectives
of the project, the outcome was more than was originally planned, and with the community
support received during the projects development, the project will live on and gain more
features over time.
With regards to the JSON Schema for OpenVehicleDiag, this was probably the hardest
component out of the the three components developed for this project, since the ODX file
specification is not well known. Therefore, creating my own parallel for ODX-D but in JSON
opens the possibility for others to create diagnostic containers for ECUs without the need of
advanced commercial tools.
The Passthru library creation using Macchina’s M2 has shown that it is indeed possible
to create an open source alternative to professional diagnostic adapters. Although difficult
to implement, it was interesting to see how its possible to create a cross-platform of an API
which was originally designed for older versions of Windows, utilizing certain cross-platform
libraries in Rust.
71
References
Drew Technologies, Inc (2003), ‘Sae j2534 api reference’. J2534 API reference.
URL: http: // read. pudn. com/ downloads209/ ebook/ 984970/ PassThru_ API-1.
pdf
ISO (1989), ‘Road vehicles — diagnostic systems — requirements for interchange of digital
information’.
URL: https: // www. iso. org/ standard/ 16737. html
ISO (2000), ‘Road vehicles – diagnostic systems – keyword protocol 2000 – part 4: Require-
ments for emission-related systems’.
URL: https: // www. iso. org/ standard/ 28826. html
ISO (2006), ‘Road vehicles - unified diagnostic services (uds) - specification and require-
ments’.
URL: http: // read. pudn. com/ downloads191/ doc/ 899044/ ISO+ 14229+ ( 2006)
.pdf
kernel.org (n.d.), ‘Readme file for the controller area network protocol family (aka socketcan)’.
URL: https: // www. kernel. org/ doc/ Documentation/ networking/ can. txt
Nils Weiss, Sebastian Renner, Jürgen Mottok, Václav Matoušek (n.d.), ‘Transport layer scan-
ning for attack surface detection in vehicular networks’.
Softing (2013), ‘D-pdu api software for communication interfaces’. D-PDU API reference.
URL: https: // www. slideshare. net/ linhdoanbro/ d-pduapi-usermanual
72
REFERENCES 73
Figure A.1: Xentry Diagnostics establishing communication with all the ECUs
within a vehicle. During this stage of diagnostics, Xentry is trying to locate all the
ECUs on the vehicle, and checking what variation each ECU is in order to parse
their diagnostic data correctly
74
APPENDIX A. SCREENSHOTS AND DIAGRAMS 75
Figure A.2: Xentry diagnostics with a list of all possible ECUs in the vehicle to
talk to, each in their own category
Figure A.3: Obtaining advanced data from the ECU in Xentry - Querying various
attributes about the ESP ECU. The serial number of this part has been hidden.
Figure A.4: Xentry showing a live ’actuation’ value of certain items the ESP ECU
controls
APPENDIX A. SCREENSHOTS AND DIAGRAMS 76
Figure A.5: Show Xentry obtaining real-time data from the CDI Engine ECU. The
values in Green are within tolerance, and values in red are outside tolerance. Black
values indicate no tolerances are specified for the value
Figure A.6: Xentry showing advanced real-time data from the CDI Engine ECU.
This allows for advanced analytics of how the engine is performing.
APPENDIX A. SCREENSHOTS AND DIAGRAMS 77
Figure A.7: More advanced real-time diagnostics with the CDI Engine ECU. This
shows the injector calibration values for the number 1 cylinder
A.0.2 OpenVehicleDiag
Figure A.17: OVD KWP2000 generic session - Sending valid manual payload
Figure A.18: OVD KWP2000 generic session - Sending invalid manual payload
Figure A.22: OVD Json session - DTC page with Freeze frame interpretation
Figure A.23: OVD Json session - Selecting function to read data from
APPENDIX A. SCREENSHOTS AND DIAGRAMS 83
Figure A.25: OVD Json session - Reading data from EGS52 transmission ECU
Appendix B
84
Appendix C
85
APPENDIX C. CODE SNIPPETS AND TABLES 86
56 }
57 }
58 ]
59 }
60 ],
61 ” downloads ” : [
62 {
63 ”name” : ” DT 21 33 ” ,
64 ” d e s c r i p t i o n ” : ” Data do wnlo ad 21 3 3 ” ,
65 ” payload ” : ”2133” ,
66 ” output params ” : [
67 {
68 ”name” : ” V a l v e s F l a g ” ,
69 ” u n i t ” : ”” ,
70 ” s t a r t b i t ” : 16 ,
71 ”length bits”: 1,
72 ” b y t e o r d e r ” : ” BigEndian ” ,
73 ” data format ” : {
74 ” Bool ” : {
75 ” pos name ” : ” A c t i v e ” ,
76 ” neg name ” : ” I n a c t i v e ”
77 }
78 }
79 },
80 {
81 ”name” : ” S h o u l d a c t i v a t e s o l e n o i d 1−2/4−5”,
82 ” u n i t ” : ”” ,
83 ” s t a r t b i t ” : 18 ,
84 ”length bits”: 1,
85 ” b y t e o r d e r ” : ” BigEndian ” ,
86 ” data format ” : {
87 ” Bool ” : {
88 ” pos name ” : ” A c t i v e ” ,
89 ” neg name ” : ” I n a c t i v e ”
90 }
91 }
92 },
93 {
94 ”name” : ” S h i f t s o l e n o i d t a r g e t s t a t u s ” ,
95 ” u n i t ” : ”” ,
96 ” s t a r t b i t ” : 24 ,
97 ”length bits”: 8,
98 ” b y t e o r d e r ” : ” BigEndian ” ,
99 ” data format ” : {
100 ” Table ” : [
101 {
102 ”name” : ” None ” ,
103 ” start ” : 0.0 ,
104 ” end ” : 0 . 0
105 },
106 {
107 ”name” : ”1−2/4−5”,
108 ” start ” : 1.0 ,
109 ” end ” : 1 . 0
110 },
111 {
112 ”name” : ”2 −3” ,
113 ” start ” : 2.0 ,
114 ” end ” : 2 . 0
115 },
116 {
117 ”name” : ”1−2/4−5 and 2−3”,
118 ” start ” : 3.0 ,
119 ” end ” : 3 . 0
120 },
121 {
122 ”name” : ”3 −4” ,
123 ” start ” : 4.0 ,
124 ” end ” : 4 . 0
125 },
126 {
127 ”name” : ”1−2/4−5 and 3−4”,
128 ” start ” : 5.0 ,
129 ” end ” : 5 . 0
130 },
131 {
132 ”name” : ”2−3 and 3−4”,
133 ” start ” : 6.0 ,
134 ” end ” : 6 . 0
135 },
136 {
137 ”name” : ”1−2/4−5 and 2−3 and 3−4”,
138 ” start ” : 7.0 ,
139 ” end ” : 7 . 0
140 }
141 ]
142 }
143 },
144 {
145 ”name” : ” S h o u l d a c t i v a t e s o l e n o i d 2−3”,
146 ” u n i t ” : ”” ,
147 ” s t a r t b i t ” : 25 ,
148 ”length bits”: 1,
149 ” b y t e o r d e r ” : ” BigEndian ” ,
150 ” data format ” : {
APPENDIX C. CODE SNIPPETS AND TABLES 91
151 ” Bool ” : {
152 ” pos name ” : ” A c t i v e ” ,
153 ” neg name ” : ” I n a c t i v e ”
154 }
155 }
156 },
157 {
158 ”name” : ” S h o u l d a c t i v e s o l e n o i d 3−4”,
159 ” u n i t ” : ”” ,
160 ” s t a r t b i t ” : 26 ,
161 ”length bits”: 1,
162 ” b y t e o r d e r ” : ” BigEndian ” ,
163 ” data format ” : {
164 ” Bool ” : {
165 ” pos name ” : ” A c t i v e ” ,
166 ” neg name ” : ” I n a c t i v e ”
167 }
168 }
169 },
170 {
171 ”name” : ” S o l e n o i d 1−2/4−5”,
172 ” u n i t ” : ”” ,
173 ” s t a r t b i t ” : 27 ,
174 ”length bits”: 1,
175 ” b y t e o r d e r ” : ” BigEndian ” ,
176 ” data format ” : {
177 ” Bool ” : {
178 ” pos name ” : ” A c t i v e ” ,
179 ” neg name ” : ” I n a c t i v e ”
180 }
181 }
182 },
183 {
184 ”name” : ” S o l e n o i d 2−3”,
185 ” u n i t ” : ”” ,
186 ” s t a r t b i t ” : 28 ,
187 ”length bits”: 1,
188 ” b y t e o r d e r ” : ” BigEndian ” ,
189 ” data format ” : {
190 ” Bool ” : {
191 ” pos name ” : ” A c t i v e ” ,
192 ” neg name ” : ” I n a c t i v e ”
193 }
194 }
195 },
196 {
197 ”name” : ” S o l e n o i d 3−4”,
198 ” u n i t ” : ”” ,
199 ” s t a r t b i t ” : 29 ,
200 ”length bits”: 1,
201 ” b y t e o r d e r ” : ” BigEndian ” ,
202 ” data format ” : {
203 ” Bool ” : {
204 ” pos name ” : ” A c t i v e ” ,
205 ” neg name ” : ” I n a c t i v e ”
206 }
207 }
208 },
209 {
210 ”name” : ” S h i f t p r e s s u r e ” ,
211 ” u n i t ” : ” mbar ” ,
212 ” s t a r t b i t ” : 32 ,
213 ” l e n g t h b i t s ” : 16 ,
214 ” b y t e o r d e r ” : ” BigEndian ” ,
215 ” data format ” : ” I d e n t i c a l ”
216 },
217 {
218 ”name” : ” M o d u l a t i n g p r e s s u r e ” ,
219 ” u n i t ” : ” mbar ” ,
220 ” s t a r t b i t ” : 48 ,
221 ” l e n g t h b i t s ” : 16 ,
222 ” b y t e o r d e r ” : ” BigEndian ” ,
223 ” data format ” : ” I d e n t i c a l ”
224 },
225 {
226 ”name” : ” S w i t c h i n g p r e s s u r e : T a r g e t c u r r e n t ” ,
227 ” u n i t ” : ”mA” ,
228 ” s t a r t b i t ” : 64 ,
229 ” l e n g t h b i t s ” : 16 ,
230 ” b y t e o r d e r ” : ” BigEndian ” ,
231 ” data format ” : ” I d e n t i c a l ”
232 },
233 {
234 ”name” : ” S w i t c h i n g p r e s s u r e : A c t u a l c u r r e n t ” ,
235 ” u n i t ” : ”mA” ,
236 ” s t a r t b i t ” : 80 ,
237 ” l e n g t h b i t s ” : 16 ,
238 ” b y t e o r d e r ” : ” BigEndian ” ,
239 ” data format ” : ” I d e n t i c a l ”
240 },
241 {
242 ”name” : ” M o d u l a t i n g p r e s s u r e : T a r g e t c u r r e n t ” ,
243 ” u n i t ” : ”mA” ,
244 ” s t a r t b i t ” : 96 ,
245 ” l e n g t h b i t s ” : 16 ,
APPENDIX C. CODE SNIPPETS AND TABLES 92
246 ” b y t e o r d e r ” : ” BigEndian ” ,
247 ” data format ” : ” I d e n t i c a l ”
248 },
249 {
250 ”name” : ” M o d u l a t i n g p r e s s u r e : A c t u a l c u r r e n t ” ,
251 ” u n i t ” : ”mA” ,
252 ” s t a r t b i t ” : 112 ,
253 ” l e n g t h b i t s ” : 16 ,
254 ” b y t e o r d e r ” : ” BigEndian ” ,
255 ” data format ” : ” I d e n t i c a l ”
256 },
257 {
258 ”name” : ” Torque c o n v e r t e r t a r g e t l o c k u p ” ,
259 ” u n i t ” : ”1/255” ,
260 ” s t a r t b i t ” : 128 ,
261 ”length bits”: 8,
262 ” b y t e o r d e r ” : ” BigEndian ” ,
263 ” data format ” : ” I d e n t i c a l ”
264 }]
265 }
266 ]
267 }
268 ]
269 }
Appendix D
The below repositories are where the code for this thesis is located at. Each repository has
its own purpose.
93
Appendix E
The following has been adapted from the Json Schema document from the OpenVe-
hicleDiag repository at release 1.01
This document outlines the JSON Specification which OpenVehicleDiag uses for ECU di-
agnostics. It is designed to be a simple, easy to understand replacement for ODX, and
proprietary data formats such as Daimler’ CBF and SMR-D data format.
JSON Root
Example
1 {
2 " name " : " Awesome ECU " ,
3 " description " : " My awesome engine ECU !" ,
4 " variants " : [] ,
5 " connections " : []
6 }
Properties
ECU Variant
An ECU Variant is used to identify a particular software version of an ECU. Since an ECU can
get software updates over time, this is necessary as with certain software updates, an ECU
can change or modify error code descriptions and also add or remove diagnostic routines.
Example
1
https://github.com/rnd-ash/OpenVehicleDiag/blob/v1.0.0/SCHEMA.md
94
APPENDIX E. OPENVEHICLEDIAG JSON SCHEMA 95
1 {
2 " name " : " SW_V_01 " ,
3 " description " : " My Awesome ECU software version 0.1" ,
4 " patterns " : [] ,
5 " errors " : [] ,
6 " adjustments " : [] ,
7 " actuations " : [] ,
8 " functions " : [] ,
9 " downloads " : []
10 }
Properties
Pattern
An ECU Pattern is used to identify which hardware vendor is responsible for implementing
the parent variant software version, since its possible for 1 ECU software to be installed on
multiple vendors’ ECUs, such as Bosch, Simens and Delphi.
Example
1 {
2 " vendor " : " rnd - ash@github . com " ,
3 " vendor_id " : 12345
4 }
Properties
Error
An error block represents a DTC (Diagnostic trouble code) which the ECU could potentially
throw under certain circumstances to signify something is wrong.
Example
1 {
2 " error_name " : " P2082 -002" ,
3 " summary " : " MAF implausible " ,
4 " description " : " Mass airflow sensor is producing inconsistent readings " ,
5 " envs " : []
6 }
Properties
Service
A service is used to describe an operation that can be executed on the ECU during a diagnostic
session
Example
1 {
2 " name " : " Read injector status " ,
3 " description " : " Retrieves the injector quantity per stroke for all cylinders " ,
4 " payload " : "22 FB " ,
5 " input_params " : [] ,
6 " output_params " : [] ,
7 }
Properties
Parameter
A parameter is used to define the data format used for either input or output data that the
ECU uses, as well as defining where in the ECU Request or response message the value is
APPENDIX E. OPENVEHICLEDIAG JSON SCHEMA 97
located at.
Example
1 {
2 " name " : " Supply voltage " ,
3 " description " : " Supply voltage being measured by the ECU " ,
4 " unit " : " V " ,
5 " start_bit " : 32 ,
6 " length_bits " : 8 ,
7 " byte_order " : " BigEndian " ,
8 " data_format " : " Identical " ,
9 " valid_bounds " : {
10 " upper " : 100.0 ,
11 " lower " : 0.0
12 }
13 }
Properties
Binary
Description: The output value is formatted as a binary string.
Example JSON
1 " data_format " : " Binary ,
Example parsing
1 INPUT : [0 x20 ]
2 OUTPUT : "0 b00100000 "
3
4 INPUT : [0 x20 , 0 xFF ]
5 OUTPUT : "[0 b00100000 , 0 b11111111 ]"
APPENDIX E. OPENVEHICLEDIAG JSON SCHEMA 98
HexDump
Description: The output value is formatted as a hex string like array.
Example JSON
1 " data_format " : " HexDump ,
Example parsing
1 INPUT : [0 x20 , 0 xFF , 0 x00 ]
2 OUTPUT : "[0 x20 , 0 xFF , 0 x00 ]"
String
Description: The output value is formatted as a String using a specified encoding option.
This uses lossless decoding, so if bytes don’t map to specific characters, ? will be displayed
in its place.
Example JSON
1 " data_format " : {
2 " String " : " Utf8 "
3 },
Example parsing
1 INPUT : [0 x54 , 0 x65 , 0 x73 , 0 x74 , 0 x20 , 0 x6D , 0 x65 , 0 x73 , 0 x73 , 0 x61 , 0 x67 , 0 x65 ]
2 OUTPUT : " Test message "
Notes For the string encoding option, 3 possible values are allowed:
Bool
Description: The output value is formatted as a Boolean, where 0 is interpreted as False,
and any other value is interpreted as True. If the fields pos name and neg name are present,
then the strings in those fields will replace the default ”True”, ”False” Strings.
Example JSON
1 " data_format " : " Bool " ,
Example parsing
1 # First JSON example
2 INPUT : 0 x01
3 OUTPUT : " True "
4
5 INPUT : 0 x00
6 OUTPUT : " False "
7
APPENDIX E. OPENVEHICLEDIAG JSON SCHEMA 99
8
9 # Second JSON example
10 INPUT : 0 x01
11 OUTPUT : " This is positive "
12
13 INPUT : 0 xFF
14 OUTPUT : " This is positive "
15
16 INPUT : 0 x00
17 OUTPUT : " This is negative "
Table
Description: The output value is formatted as a String based on a table which represents
an Enum. Each enum entry in the table has a defined start and end (inclusive) value. Any
number between the start and end value are accepted into the enums variant. If no Enum
could be found for a given input value, then ”UNDEFINED” is returned (See example parsing
below).
Example JSON
1 " data_format " : {
2 " Table " : [
3 {
4 " name " : " This value is between 0 and 10" ,
5 " start " : 0.0 ,
6 " end " : 10.0
7 },
8 {
9 " name " : " This value is only 11" ,
10 " start " : 11.0 ,
11 " end " : 11.0
12 },
13 {
14 " name " : " This value is only 100" ,
15 " start " : 100.0 ,
16 " end " : 100.0
17 }
18 ]
19 },
Example parsing
1 INPUT : [0 x00 ]
2 OUTPUT : " This value is between 0 and 10"
3
4 INPUT : [0 x05 ]
5 OUTPUT : " This value is between 0 and 10"
6
7 INPUT : [0 x64 ]
8 OUTPUT : " This value is only 100"
9
10 INPUT : [0 xFF ]
11 OUTPUT : " UNDEFINED (0 xFF ) "
Identical
Description: The output value is formatted as a number based on the raw value extracted
from the parent parameter’s bit range.
Example JSON
1 " data_format " : " Identical "
APPENDIX E. OPENVEHICLEDIAG JSON SCHEMA 100
Example parsing
1 INPUT : [0 x00 ]
2 OUTPUT : "0"
3
4 INPUT : [0 x05 ]
5 OUTPUT : "5"
6
7 INPUT : [0 x64 ]
8 OUTPUT : "100"
9
10 INPUT : [0 xFF , 0 xFF ]
11 OUTPUT : "65565"
Linear
Description: The output value is calculated using a simple y = mx + c equation, where
the ”multiplier” field represents the m component of the equation, and the ”offset” field
represents the c component.
Example JSON
1 " data_format " : {
2 " Linear " : {
3 " multiplier " : 0.125 ,
4 " offset " : -40.0
5 }
6 },
Example parsing
1 INPUT : [0 x00 ]
2 OUTPUT : " -40.0"
3
4 INPUT : [0 x10 ]
5 OUTPUT : " -38.0"
6
7 INPUT : [0 xFF ]
8 OUTPUT : " -8.125"
ScaleLinear
This does not function in OpenVehicleDiag 1.0!
RatFunc
This does not function in OpenVehicleDiag 1.0!
ScaleRatFunc
This does not function in OpenVehicleDiag 1.0!
TableInterpretation
This does not function in OpenVehicleDiag 1.0!
Compucode
This does not function in OpenVehicleDiag 1.0!
Description: The output value is calculated using a Java virtual machine that runs bytecode
of a class that implements the I CompuCode() interface.
Connection
Example
1 {
2 " baud " : 500000 ,
3 " send_id " : 2016 ,
4 " gl obal_sen d_id " : 2016 ,
5 " co nn e ct io n_ t yp e " : {
6 " ISOTP " : {
7 " blocksize " : 8 ,
8 " st_min " : 20
9 }
10 },
11 " server_type " : " KWP2000 " ,
12 " recv_id " : 2024
13 }
properties
Connection type
Example (LIN)
1 " co nn e ct io n_ t yp e " : {
2 " LIN " : {
3 " m a x _ s e g m e n t _ s i z e " : 254 ,
4 " wa ke_up_me thod " : " FiveBaudInit "
5 }
6 }
Properties
APPENDIX E. OPENVEHICLEDIAG JSON SCHEMA 102
Example (ISO-TP)
1 " co nn e ct io n_ t yp e " : {
2 " ISOTP " : {
3 " blocksize " : 8 ,
4 " st_min " : 20 ,
5 " ext_can_addr " : false ,
6 " ex t_isotp_ addr " : false ,
7 }
8 }
Properties