Using UDS
Previous: Working with CAN
Unified Diagnostic Services (UDS) is a widely used protocol for automotive diagnostics that is independent of the transport layer.
Take a look at the “Systems” set up from levels 5 to 7 in as described in Managing the lifecycle of components.
Run Level |
Component |
Contexts |
---|---|---|
5 |
TransportSystem |
TASK_UDS |
6 |
DoCanSystem |
TASK_CAN |
7 |
UdsSystem |
TASK_UDS |
In this example, CAN provides the transport layer, but note that the separation of UDS from the transport path allows multiple transports to be supported. Take time to study these “System” classes and how they interact.
UDS over CAN
If you have successfully tested the CAN setup for a POSIX platform as described in Working with CAN, try some more tests using the same setup in which…
vcan0
is successfully set upapp.referenceApp.elf
is running in shell terminal 1candump vcan0
is running in shell terminal 2
In a third shell terminal run…
cansend vcan0 02A#0322CF0100000000
With this, cansend
sends a CAN Frame with CAN ID set to 0x02A
.
If you look in the code for DoCanSystem
you will see it is set up with 0x02A
as the CAN ID to listen for.
The payload contains the following parts…
The 1st byte of the payload (with value
0x03
) is the Protocol Control Information (PCI) field for UDS over CAN. This breaks down into 2 parts..The first 4 bits indicate the frame type (
0
for Single Frame)The last 4 bits indicate the length of the request (
3
bytes)
The 2nd byte is the UDS Service Identifier (SID). Its value
0x22
is the ID for the “Read Data By Identifier” service.The 3rd and 4th bytes make up the UDS Data Identifier (DID) with value
0xCF01
identifies which data should be returned.
The CAN frame sent was actually a UDS over CAN request to “Read” the data identified by 0xCF01
and return that data.
So what response should be seen?
app.referenceApp.elf
should print…
32057178: RefApp: CAN: DEBUG: [SocketCanTransceiver] received CAN frame, id=0x2A, length=8
32057178: RefApp: TPROUTER: DEBUG: TransportRouterSimple::getTransportMessage : sourceId 0xf0, targetId 0x2a
32057178: RefApp: UDS: DEBUG: Opening incoming connection 0xf0 --> 0x2a, service 0x22
32057178: RefApp: Accepted 0x22, current session: 0x1
32057178: RefApp: UDS: DEBUG: Process diag job 0x22
32057178: RefApp: Accepted 0x22cf01, current session: 0x1
32057178: RefApp: UDS: DEBUG: Process diag job 0x22CF01
32057180: RefApp: CAN: INFO: [CanSystem] CAN frame sent, id=0xF0, length=8
32058181: RefApp: DOCAN: WARN: DoCanTransmitter(CAN_0)::cyclicTask(0x2a -> 0xf0): Flow control timeout
32058181: RefApp: UDS: DEBUG: IncomingDiagConnection::terminate(): 0xf0 --> 0x2a, service 0x22
32058182: RefApp: UDS: ERROR: IncomingDiagConnection::transportMessageSent(): failed to send message from 0x2a to 0xf0
In the code for UdsSystem
you can see that it instantiates ReadIdentifierFromMemory _read22Cf01
to return responseData22Cf01
for the DID 0xCF01
as follows…
uint8_t const responseData22Cf01[]
= {0x01, 0x02, 0x00, 0x02, 0x22, 0x02, 0x16, 0x0F, 0x01, 0x00, 0x00, 0x6D,
0x2F, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x8F, 0xE0, 0x00, 0x00, 0x01};
...
, _read22Cf01(0xCF01, responseData22Cf01)
The log line containing Process diag job 0x22CF01
indicates that the “Read” request was interpreted correctly,
it found the job _read22Cf01
that matched the request,
then the log line containing CAN frame sent, id=0xF0, length=8
indicates it sent a CAN frame response with CAN ID 0x0F0
which is the ID DoCanSystem
is set up to send responses with.
During the above, candump vcan0
should have printed…
vcan0 02A [8] 03 22 CF 01 00 00 00 00
vcan0 0F0 [8] 10 1B 62 CF 01 01 02 00
The first CAN frame is the request being send and the 2nd CAN frame is the response which breaks down as follow…
The PCI is 2 bytes this time, with value
0x101B
The first 4 bits indicate the frame type (0x1 indicates it is the 1st frame of a series)
The next 12 bits indicate the number of bytes to follow (
0x01B
= 27 bytes). Note this is larger than 1 CAN Frame (max 8 bytes)
The 3rd byte is the UDS Response SID. Its value
0x62
is the ID for the response for the “Read Data By Identifier” service.The 4th and 5th bytes hold the same UDS Data Identifier (DID)
0xCF01
as in the request.The remaining bytes
0x010200
are the 1st three bytesresponseData22Cf01
.
After sending the CAN frame response app.referenceApp.elf
printed…
32058181: RefApp: DOCAN: WARN: DoCanTransmitter(CAN_0)::cyclicTask(0x2a -> 0xf0): Flow control timeout
This indicates it is waiting for the client to request the next CAN Frame of the response.
cansend
is a simple tool to send CAN frames, it has no knowledge of UDS and so it did not send the next expected follow-on UDS request.
What we need is a tool that implements UDS over CAN that is capable of sending the next request expected.
UdsTool
, which can be found in the subdirectory referenceApp/tools/UdsTool
implements exactly what we need.
Assuming you have python >= 3.7
installed, you can install UdsTool
as instructed in UdsTool.
Using the same setup, with app.referenceApp.elf
and candump vcan0
running in separate shell terminals,
in a third shell terminal run…
udstool read --can --channel vcan0 --txid 2A --rxid F0 --did cf01 --config tools/UdsTool/app/canConfig.json
If it works it should show output like this…
uptime library not available, timestamps are relative to boot time and not to Epoch UTC
<class 'udsoncan.client.Client'>
62cf01010200022202160f0100006d2f0000010600008fe0000001
<PositiveResponse: [ReadDataByIdentifier] - 26 data bytes at 0x7d9f586c2530>
In that you can see the response contains the data from responseData22Cf01
.
Looking at the log output from app.referenceApp.elf
you can see that this time it received a 2nd CAN Frame with ID 0x2A
sent by UdsTool
after which that remaining data was sent in response in CAN frames with ID 0xF0
.
37907832: RefApp: CAN: DEBUG: [SocketCanTransceiver] received CAN frame, id=0x2A, length=8
37907832: RefApp: TPROUTER: DEBUG: TransportRouterSimple::getTransportMessage : sourceId 0xf0, targetId 0x2a
37907833: RefApp: UDS: DEBUG: Opening incoming connection 0xf0 --> 0x2a, service 0x22
37907833: RefApp: Accepted 0x22, current session: 0x1
37907833: RefApp: UDS: DEBUG: Process diag job 0x22
37907833: RefApp: Accepted 0x22cf01, current session: 0x1
37907833: RefApp: UDS: DEBUG: Process diag job 0x22CF01
37907834: RefApp: CAN: INFO: [CanSystem] CAN frame sent, id=0xF0, length=8
37907883: RefApp: CAN: DEBUG: [SocketCanTransceiver] received CAN frame, id=0x2A, length=8
37907883: RefApp: CAN: INFO: [CanSystem] CAN frame sent, id=0xF0, length=8
37907916: RefApp: CAN: INFO: [CanSystem] CAN frame sent, id=0xF0, length=8
37907950: RefApp: CAN: INFO: [CanSystem] CAN frame sent, id=0xF0, length=8
37907951: RefApp: UDS: DEBUG: IncomingDiagConnection::terminate(): 0xf0 --> 0x2a, service 0x22
The output from candump vcan0
shows all details of these CAN frames…
vcan0 02A [8] 03 22 CF 01 00 00 00 00
vcan0 0F0 [8] 10 1B 62 CF 01 01 02 00
vcan0 02A [8] 30 08 20 00 00 00 00 00
vcan0 0F0 [8] 21 02 22 02 16 0F 01 00
vcan0 0F0 [8] 22 00 6D 2F 00 00 01 06
vcan0 0F0 [8] 23 00 00 8F E0 00 00 01
To learn more you can experiment with adding you own services to UdsSystem
similar to ReadIdentifierFromMemory _read22Cf01
.
Next: Using hardware I/O