Unified Diagnostic Services (UDS)
UDS is the most common diagnostic protocol used in vehicles today and is standardized as ISO 14229-1. It is a request-response protocol that can be used over different transport layers and networks, including CAN, FlexRay, and Ethernet.
Protocol
The first byte of a UDS request is the SID (Service ID), a unique identifier for the requested service. A successful response starts with the SID ORed with 0x40 (e.g. SID 0x10 becomes a response of 0x50). In case of an error, the reply starts with 0x7F, followed by the SID and a negative response code.
Some SIDs use a sub-function ID as the first payload byte. A sub-function ID is a 7-bit value in which the highest bit is used to suppress the positive response from the ECU in case that is not needed. The sub-function ID is only used for some SIDs, and is followed by the parameters.
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | SID | SID | 1 byte. Identifies the requested service (e.g. 0x10, 0x22, 0x27). |
| sub-function | SF | sub-function | Optional, 1 byte. Present for some SIDs. The top bit (0x80) suppresses the positive response. |
| parameters | DATA | service-specific bytes | Optional. Format depends on the service. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | SIDPR | SID + 0x40 | Request SID ORed with 0x40 (e.g. 0x10 → 0x50, 0x22 → 0x62). |
| sub-function | SF | sub-function | Echoed when the request had a sub-function (top bit cleared). |
| parameters | DATA | service-specific bytes | Optional. Format depends on the service. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Negative Response indicator | SIDNR | 7F | Fixed prefix indicating a negative response. |
| Service ID | SIDRQ | SID | The service ID the negative response is for. |
| negativeResponseCode | NRC | NRC | 1 byte explaining why the request was rejected. See the Error Codes section. |
Service IDs (SIDs)
Below is an overview of some interesting UDS services. For a full list of services, see ISO 14229-1.
0x10 — Diagnostic Session Control
UDS has a concept of diagnostic sessions. Some SIDs are only available in certain sessions. For example, firmware updates usually require a programming session, and actuation tests are usually only available in an extended diagnostic session. Entering some sessions might require authentication first. SID 0x10 is used to change the current session type. The sub-function ID is used to specify the requested session type.
Optionally, the ECU may send an extra 4 bytes of "Session Parameter Record" following the response, containing information on the timeout values the tester can use.
The most common sessions are listed below, but manufacturers can define their own session types as well. These can be found by iterating over all possible session types and observing the ECU's response.
0x1— Default Session: The session type the ECU will be in after a power cycle.0x2— Programming Session: Used to update the firmware running on an ECU. Often requesting this session type will reboot the ECU into the bootloader.0x3— Extended Diagnostic Session: Usually unlocks some extra SIDs that are not available in the default session, for example actuation tests and changing data that persists across reboots.0x4— Safety System Diagnostic Session: Used to test safety-critical systems, such as the airbags.0x40–0x5F— Vehicle Manufacturer-specific.0x60–0x7E— System Supplier Specific.
Enter extendedDiagnosticSession
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | DSC | 10 | DiagnosticSessionControl request SID. |
| diagnosticSessionType | DS | 03 | Sub-function. 0x03 = extendedDiagnosticSession. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | DSCPR | 50 | Request SID ORed with 0x40. |
| diagnosticSessionType | DS | 03 | Echoed sub-function. |
0x11 — ECU Reset
ECU Reset can be used to reset the ECU. For example, this could be used after modifying flash memory. The sub-function ID specifies the reset type. Common reset types include 0x01 hard reset, 0x02 key off/on reset, and 0x03 soft reset.
Hard reset
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | ER | 11 | ECUReset request SID. |
| resetType | RT | 01 | Sub-function. 0x01 = hardReset. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | ERPR | 51 | Request SID ORed with 0x40. |
| resetType | RT | 01 | Echoed sub-function. |
0x22 / 0x2E — Read / Write Data By Identifier
Read Data By Identifier (0x22) is used to read values, and Write Data By Identifier (0x2E) is used to write values.
The identifiers are called DIDs (Data Identifiers). The UDS standard has a few predefined DIDs, such as 0xF180 "Boot Software Identification Data Identifier" or 0xF190 which contains the vehicle's VIN. Manufacturers can define their own DIDs as well, which can be found by iterating over all possible DIDs and observing the ECU's response.
On some implementations it may also be possible to request multiple DIDs with a single request, by concatenating the DIDs after the SID. Whether this is supported depends on the UDS implementation.
Example requesting the VIN:
Reading the VIN
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RDBI | 22 | ReadDataByIdentifier request SID. |
| dataIdentifier | DID | F1 90 | 0xF190 = VIN data identifier (big-endian). |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RDBIPR | 62 | Request SID ORed with 0x40. |
| dataIdentifier | DID | F1 90 | Echoed identifier. |
| dataRecord | DREC | 17 ASCII bytes (the VIN) | Value of the identifier. Format is manufacturer-specific. |
Using Write Data By Identifier (0x2E) data can be written back to the ECU. Use the same format, but append the data to write after the DID. The response will contain the SID + 0x40 and the DID.
Writing the VIN
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | WDBI | 2E | WriteDataByIdentifier request SID. |
| dataIdentifier | DID | F1 90 | Identifier to write. |
| dataRecord | DREC | 17 ASCII bytes (the new VIN) | Data to write into the identifier. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | WDBIPR | 6E | Request SID ORed with 0x40. |
| dataIdentifier | DID | F1 90 | Echoed identifier. |
0x23 / 0x3D — Read / Write Memory By Address
Where 0x22 can be used to read DIDs, 0x23 can be used to read memory by address. The payload starts with an addressAndLengthFormatIdentifier (ALFID), followed by the address and the number of bytes to read. The high nibble of the ALFID specifies the number of size bytes, and the low nibble specifies the number of address bytes. The address and length are usually big endian.
The response will contain the SID + 0x40, immediately followed by the data.
The example below reads 16 bytes from address 0x12345678. There are 4 address bytes and 1 size byte, so the ALFID is 0x14. Hover any byte or row to highlight the corresponding field.
ReadMemoryByAddress — 4-byte address, 1-byte size
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RMBA | 23 | ReadMemoryByAddress request SID. |
| addressAndLengthFormatIdentifier | ALFID | 14 |
|
| memoryAddress | MA | 12 34 56 78 | Start address, big-endian. MSB first. |
| memorySize | MS | 10 | Number of bytes to read (0x10 = 16). |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RMBAPR | 63 | Request SID ORed with 0x40. |
| dataRecord | DREC | 16 bytes of memory contents | The requested memory bytes, in the order they appear in memory. |
Using Write Memory By Address (0x3D), data can be written back to the ECU. Use the same format, but append the data to write after the address and length. The response will contain the SID + 0x40, the ALFID, and the address and size.
WriteMemoryByAddress — 4-byte address, 1-byte size
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | WMBA | 3D | WriteMemoryByAddress request SID. |
| addressAndLengthFormatIdentifier | ALFID | 14 |
|
| memoryAddress | MA | 12 34 56 78 | Destination address, big-endian. |
| memorySize | MS | 10 | Number of bytes to write (0x10 = 16). |
| dataRecord | DREC | 16 bytes to write | Payload of bytes to write at the destination. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | WMBAPR | 7D | Request SID ORed with 0x40. |
| addressAndLengthFormatIdentifier | ALFID | 14 | Echoed. |
| memoryAddress | MA | 12 34 56 78 | Echoed. |
| memorySize | MS | 10 | Echoed. |
0x27 — Security Access
Some types of diagnostic sessions or SIDs require authentication first. This is done using SID 0x27. The authentication works by requesting a seed from the ECU, putting the seed into a special algorithm to compute a key, and sending the key back to the ECU. If the key is correct, the ECU will grant access to the requested session or SID.
There are usually two countermeasures to prevent brute-forcing the key. The first is that there are usually only a limited number of tries to provide the correct key before the ECU locks further attempts. This either requires a reboot of the ECU or waiting a certain amount of time.
The second countermeasure is that a certain amount of time has to pass between powering on the ECU and requesting a seed. This is to prevent an attacker from making a few attempts until the ECU blocks further tries and then quickly power-cycling the ECU to try again. Another benefit is that if the RNG does not have any good source of randomness, the seed will be the same every time the ECU is powered on. By forcing the user to wait before making an attempt, it's harder to get the same seed twice.
Usually sub-function 0x01 is used to request a seed, and sub-function 0x02 is used for sending the key as a response. Sometimes more than one type of seed/key can be chosen for different levels of security using higher sub-function IDs. All odd sub-function IDs request a certain type of seed, while the even sub-function IDs are used to send back the computed key.
The algorithms to compute the key are usually not public — they range from simple XOR operations to more complex algorithms.
Example of a tester requesting a seed (0x1234), and then sending back the key (0x9876):
Step 1 — request seed
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | SA | 27 | SecurityAccess request SID. |
| securityAccessType | SAT | 01 | Sub-function. Odd value = requestSeed at level 0x01. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | SAPR | 67 | Request SID ORed with 0x40. |
| securityAccessType | SAT | 01 | Echoed sub-function. |
| securitySeed | SECSEED | 12 34 | Random seed produced by the ECU. |
Step 2 — send computed key
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | SA | 27 | SecurityAccess request SID. |
| securityAccessType | SAT | 02 | Sub-function. Even value = sendKey at level 0x01. |
| securityKey | SECKEY | 98 76 | Key derived from the seed. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | SAPR | 67 | Request SID ORed with 0x40. Indicates the key was accepted. |
| securityAccessType | SAT | 02 | Echoed sub-function. |
0x28 — Communication Control
This SID is used to enable or disable certain types of communication. The sub-function ID is used to specify the requested action. The payload contains the type of communication to control, optionally followed by a two-byte node ID.
Sub-function ID values:
0x0— Enable Rx and Tx (default).0x1— Enable Rx and disable Tx for the specified communication type.0x2— Disable Rx and enable Tx for the specified communication type.0x3— Disable Rx and Tx for the specified communication type.0x4— Enable Rx and disable Tx on the sub-network with the specified node ID.0x5— Enable Rx and Tx on the sub-network with the specified node ID.
The communication type is a bit field such that multiple types can be controlled at the same time. The lower nibble is used for the type of messages: bit 0 indicates normal communication messages, bit 1 indicates network management messages. The upper nibble is used to indicate the number of the sub-network to control.
Example of a tester disabling all CAN communication. Note the MSB of the sub-function ID is set, so the ECU will not send a positive response:
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | CC | 28 | CommunicationControl request SID. |
| controlType | CTRLTP | 83 | Sub-function. 0x03 = disableRxAndTx. Top bit (0x80) = suppressPosRspMsgIndicationBit. |
| communicationType | CTP | 03 | Bit 0: normal communication messages. Bit 1: network management messages. Upper nibble: sub-network number. |
The ECU does not send a response because the suppress-positive-response bit is set.
0x29 — Authentication
This SID was added in a 2020 update to the UDS specification. Where service 0x27 (Security Access) works using symmetric cryptography (where the tester and ECU use the same algorithm and key), service 0x29 (Authentication) also supports using asymmetric cryptography and PKI certificates.
0x31 — Routine Control
This SID is used to start and stop functions on the ECU. Some functions produce a result that can be retrieved using this SID. For example, functions can be implemented to test or calibrate actuators. During reprogramming of an ECU, a routine control is used to initiate the erasure process before new firmware can be written.
The sub-function ID specifies the requested action: 0x1 to start a routine, 0x2 to stop a routine, 0x3 to request the result. The sub-function ID is followed by a two-byte routine identifier.
Both requests and responses can contain extra payload data, e.g. to pass extra parameters when starting a routine, or to return the result of a routine when stopping it.
Example of a tester starting a routine with routine identifier 0xAABB, stopping it, and requesting the result:
Start routine
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RC | 31 | RoutineControl request SID. |
| routineControlType | RCTP | 01 | Sub-function. 0x01 = startRoutine. |
| routineIdentifier | RID | AA BB | 16-bit identifier of the routine to start. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RCPR | 71 | Request SID ORed with 0x40. |
| routineControlType | RCTP | 01 | Echoed sub-function. |
| routineIdentifier | RID | AA BB | Echoed identifier. |
Stop routine
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RC | 31 | RoutineControl request SID. |
| routineControlType | RCTP | 02 | Sub-function. 0x02 = stopRoutine. |
| routineIdentifier | RID | AA BB | Identifier of the routine to stop. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RCPR | 71 | Request SID ORed with 0x40. |
| routineControlType | RCTP | 02 | Echoed sub-function. |
| routineIdentifier | RID | AA BB | Echoed identifier. |
Request result
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RC | 31 | RoutineControl request SID. |
| routineControlType | RCTP | 03 | Sub-function. 0x03 = requestRoutineResults. |
| routineIdentifier | RID | AA BB | Identifier of the routine to query. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RCPR | 71 | Request SID ORed with 0x40. |
| routineControlType | RCTP | 03 | Echoed sub-function. |
| routineIdentifier | RID | AA BB | Echoed identifier. |
| routineStatusRecord | RSR | routine result bytes | Result data, format defined by the manufacturer. |
0x34 – 0x37 — Request Download / Upload
Request Download / Upload SIDs are used to update firmware or calibration maps on the ECU. The terms for download and upload are defined from the perspective of the ECU, so download means sending data from the tester to the ECU, and upload means sending data from the ECU to the tester.
The transfer is initiated by sending the request. The payload is similar to SID 0x23 (Read Memory By Address), where the first byte is a format byte. This format byte is used to indicate if the data is encrypted or compressed.
The format byte is followed by the address-and-length format byte that specifies the number of address bytes and the number of data bytes. This is followed by the address and length of the transfer.
After requesting the download or upload, the ECU will respond with the maximum block size of the actual data transfer. The response payload starts with a format byte indicating the number of length bytes that will follow, followed by the actual maximum length of the block.
The actual data is then transferred using SID 0x36 (Transfer Data). The payload starts with a 1-byte block counter that starts at 1 and increments for each block. The block counter is used to detect missing or duplicate blocks. If a download was requested, the block counter is followed by the actual data, which can be up to the maximum block size specified by the ECU.
After each call to SID 0x36, the ECU will respond with the block counter and a payload. For an upload, this payload will contain the requested data. For a download, the payload is optional, but could for example be used to send a checksum of the transferred data to be verified by the tester.
After all data has been transferred, SID 0x37 (Request Transfer Exit) is used to exit the transfer mode. The payload is optional and can be used to send a checksum of the transferred data to be verified by the ECU. The ECU will use this function to verify the transferred data and optionally mark the new firmware as valid or bootable.
The example below downloads 0x100 bytes (256) starting at address 0x60200000. The ECU then accepts blocks up to 0x0081 bytes (129).
0x34 — Request download
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RD | 34 | RequestDownload request SID. |
| dataFormatIdentifier | DFI | 00 |
|
| addressAndLengthFormatIdentifier | ALFID | 44 |
|
| memoryAddress | MA | 60 20 00 00 | Destination address, big-endian. |
| memorySize | MS | 00 00 01 00 | Number of bytes to download (0x100 = 256). |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RDPR | 74 | Request SID ORed with 0x40. |
| lengthFormatIdentifier | LFID | 20 |
|
| maxNumberOfBlockLength | MNROB | 00 81 | Maximum number of bytes per TransferData request, including SID and BSC. 0x0081 = 129. |
The 0x35 (RequestUpload) request and response have the same structure but the SID is 0x35 / 0x75.
0x36 — Transfer data block
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | TD | 36 | TransferData request SID. |
| blockSequenceCounter | BSC | 01 | Starts at 1, increments per block, wraps at 0xFF → 0x00. |
| transferRequestParameterRecord | TRPR | up to maxNumberOfBlockLength − 2 bytes | For a download, the data being written. For an upload, this is empty. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | TDPR | 76 | Request SID ORed with 0x40. |
| blockSequenceCounter | BSC | 01 | Echoed counter. |
| transferResponseParameterRecord | TRPR | optional response data | For an upload, this contains the data being read. For a download, optional (e.g. a checksum). |
0x37 — Request transfer exit
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | RTE | 37 | RequestTransferExit request SID. |
| transferRequestParameterRecord | TRPR | optional checksum | Optional. May contain a checksum of the transferred data for verification. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | RTEPR | 77 | Request SID ORed with 0x40. The transfer is complete and the ECU has accepted the data. |
| transferResponseParameterRecord | TRPR | optional verification data | Optional response data, e.g. a confirmation checksum. |
0x3E — Tester Present
After a certain time of no communication, the ECU will exit the current diagnostic session. Tester Present is used to keep the current diagnostic session active, or is sometimes needed before initiating a session.
Tester Present has a sub-function that controls whether the ECU should send a positive response. This is useful to keep the ECU awake without filling up the CAN bus with responses. The sub-function ID is 0x00 to send a response, or 0x80 to suppress the positive response.
0x80 is also useful after using 0x28 (Communication Control) to disable CAN communication. Some ECUs need periodic Tester Present messages or the CAN messages will resume. Sending Tester Present with sub-function 0x00 will immediately enable CAN communication again because the ECU is forced to send a response.
Tester Present is also a very useful SID to scan for the presence of UDS on a certain CAN Arbitration ID. You can quickly send the Tester Present payload to a set of Arbitration IDs and see which ones respond.
Tester Present keep-alive
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Service ID | TP | 3E | TesterPresent request SID. |
| zeroSubFunction | ZSUBF | 00 | Sub-function. 0x00 = expect response. 0x80 = suppress positive response. |
| Field | Mnemonic | Value | Description |
|---|---|---|---|
| Response SID | TPPR | 7E | Request SID ORed with 0x40. |
| zeroSubFunction | ZSUBF | 00 | Echoed sub-function. |
Error Codes
When an ECU rejects a request, the negative response (0x7F SID NRC) ends with a Negative Response Code (NRC). The full set is defined in Annex A of ISO 14229-1:2013. Codes 0x01–0x7F cover communication-related errors (request format, sequencing, security, transfer state). Codes 0x80–0xFF are reserved for preconditions on the vehicle state (RPM, temperature, voltage, gear, …) and serve as more specific alternatives to 0x22 conditionsNotCorrect.
| Code | Mnemonic | Name |
|---|---|---|
0x10 | GR | generalReject |
0x11 | SNS | serviceNotSupported |
0x12 | SFNS | sub-functionNotSupported |
0x13 | IMLOIF | incorrectMessageLengthOrInvalidFormat |
0x14 | RTL | responseTooLong |
0x21 | BRR | busyRepeatRequest |
0x22 | CNC | conditionsNotCorrect |
0x24 | RSE | requestSequenceError |
0x25 | NRFSC | noResponseFromSubnetComponent |
0x26 | FPEORA | failurePreventsExecutionOfRequestedAction |
0x31 | ROOR | requestOutOfRange |
0x33 | SAD | securityAccessDenied |
0x35 | IK | invalidKey |
0x36 | ENOA | exceedNumberOfAttempts |
0x37 | RTDNE | requiredTimeDelayNotExpired |
0x70 | UDNA | uploadDownloadNotAccepted |
0x71 | TDS | transferDataSuspended |
0x72 | GPF | generalProgrammingFailure |
0x73 | WBSC | wrongBlockSequenceCounter |
0x78 | RCRRP | requestCorrectlyReceived-ResponsePending |
0x7E | SFNSIAS | sub-functionNotSupportedInActiveSession |
0x7F | SNSIAS | serviceNotSupportedInActiveSession |
CAN Arbitration IDs
Functional Addressing
UDS supports two methods of addressing. The first is functional addressing, which is a type of broadcast. In functional addressing the Arbitration ID is usually 0x7DF. All ECUs can respond to the request simultaneously. The response will be sent on the ECU's normal response address for Physical Addressing.
In functional addressing, only payloads that fit in a single CAN message are supported; otherwise, it's unclear which ECU should handle flow control.
Physical Addressing
With physical addressing, the request is sent to a specific ECU. For physical addressing, the Arbitration ID is usually between 0x700 and 0x800. The response is sent on the Arbitration ID of the request + 8 (or sometimes + 1, or + 0x6A for Volkswagen).
If 29-bit extended addresses are used, UDS can usually be found on Arbitration IDs of the form 0x18DAxxF1. In this case the response is not sent on the Arbitration ID of the request + 8, but on 0x18DAF1xx.