UDS & ISO 14229
Unified Diagnostic Services is the standard diagnostic language of modern vehicles. It's what a dealer scan tool, a flashing tool, or an end-of-line tester speaks to read fault codes, log data, run actuator tests, unlock security, and reprogram ECUs. On CAN it rides the same ISO-TP (ISO 15765-2) transport sloppyCAN already speaks. UDS just defines the application layer on top.
Request / response grammar
Every exchange follows the same convention. Learn it once and every service reads the same way:
- A request starts with a Service ID (SID) byte, e.g.
22 F1 90. - A positive response echoes the SID + 0x40, e.g.
62 F1 90 …. - A negative response is always
7F <sid> <nrc>. - NRC
0x78"responsePending" means "still working, wait for the real answer." Handled automatically. - Many services take a sub-function byte; its top bit (
0x80) is "suppress positive response".
0xF190 (VIN)0x22 + 0x40), DID echoed, then dataFor the transport mechanics, how a long response splits into First Frame + Flow Control + Consecutive Frames, see the ISO-TP Explainer.
Services you'll actually use
There are dozens of services in ISO 14229, but a handful cover most day-to-day work. These are exactly the buttons in sloppyCAN's UDS palette:
| SID | Service | What it does |
|---|---|---|
| 10 | DiagnosticSessionControl | Switch session: default / programming / extended. Unlocks the rest. |
| 11 | ECUReset | hardReset / keyOffOn / softReset. |
| 19 | ReadDTCInformation | Read fault codes by a sub-function + status mask. |
| 22 | ReadDataByIdentifier | Read a value by 16-bit DID (e.g. F190 VIN). |
| 14 | ClearDiagnosticInformation | Clear DTCs for a group (FFFFFF = all). |
| 27 | SecurityAccess | Seed/key challenge to unlock protected services. |
| 28 | CommunicationControl | Enable/disable normal Rx/Tx messaging. |
| 2E | WriteDataByIdentifier | Write a value by DID. |
| 31 | RoutineControl | Start / stop / get results of an on-ECU routine. |
| 3E | TesterPresent | Heartbeat that keeps a non-default session alive. |
| 85 | ControlDTCSetting | Turn DTC logging on/off during a test. |
Sessions & security
At power-up an ECU is in the default session, where it answers only safe,
read-only requests. Anything intrusive, writing data, running actuators, flashing, first
needs an extended or programming session
(10 03 / 10 02), and usually a SecurityAccess
unlock (27). You request a seed, compute a key with the manufacturer's secret
algorithm, and send it back.
| Step | Request | Meaning |
|---|---|---|
| 1 | 10 03 | Enter extended diagnostic session |
| 2 | 27 01 | SecurityAccess, request seed (response: 67 01 <seed>) |
| 3 | 27 02 <key> | Send computed key (response: 67 02 = unlocked) |
| 4 | 2E F1 90 … | Now a protected WriteDataByIdentifier is accepted |
| 5 | 3E 00 | TesterPresent, keep the session alive while you work |
TesterPresent matters. If the tester goes quiet for the session timeout
(typically ~5 s), the ECU drops back to the default session and you lose your unlock, so
tools send 3E 00 on a heartbeat.
Negative response codes (NRCs)
When an ECU rejects a request it replies 7F <sid> <nrc>. The most
common NRCs you'll meet:
| NRC | Name | Usually means |
|---|---|---|
| 11 | serviceNotSupported | This ECU doesn't implement that service |
| 12 | subFunctionNotSupported | Service exists, that sub-function doesn't |
| 22 | conditionsNotCorrect | Wrong session/state for this request |
| 31 | requestOutOfRange | Bad DID / parameter value |
| 33 | securityAccessDenied | Need a SecurityAccess unlock first |
| 7E / 7F | …InActiveSession | Service/sub-function not allowed in the current session |
Common diagnostic IDs
Most ECUs use a fixed pair of CAN IDs for diagnostics: one the tester transmits requests on, one the ECU replies on. The exact numbers vary by OEM, but these are the conventions you'll see most often:
| Request ID | Response ID | Convention |
|---|---|---|
| 7E0 | 7E8 | OBD-II / UDS, ECU #1 (engine), classic "7E0-7E7 / 7E8-7EF" physical addressing range |
| 7E1 | 7E9 | ECU #2 (often transmission) |
| 7DF | - (multiple) | OBD-II functional request, every ECU that supports the PID may answer |
| 18DA xx F1 | 18DA F1 xx | Extended (29-bit) UDS addressing, xx is the ECU's source address |
These pairs are just convention, not part of the UDS standard itself. The actual IDs are configured per
ECU and per OEM. sloppyCAN's UDS palette defaults to 7E0/7E8, but you can change
them to match the bus you're on.
Broadcast IDs and their limits
A few IDs are deliberately heard by every node on the bus instead of one. They're useful, but they come with a catch: there's no single device to talk back to you.
| ID | Standard | What it does | Limitation |
|---|---|---|---|
| 7DF | OBD-II | Functional request, "whoever supports this PID, answer" | Multiple ECUs may all reply, staggered by the stack. Fine for polling, unusable for anything needing a single deterministic response |
| 18DB33F1 | UDS (extended) | Functional addressing equivalent of 7DF for 29-bit UDS | Same multi-responder problem; also can't carry a SecurityAccess-gated unlock meant for one ECU |
PDU1 to DA 0xFF | J1939 | Global destination address 0xFF in a PDU1 message (and every PDU2 broadcast), "all nodes," used for things like time/date broadcasts | Nothing tells you who received it: the Acknowledgment PGN (59392) is only sent by a node that was addressed specifically, so a global request may get many answers, or none |
Broadcast isn't acknowledged by anyone in particular. CAN does have a wired-AND ACK slot, and it is filled on a broadcast frame just like any other, but it only means "at least one node on the bus received this frame intact". It names nobody, and a node that heard the frame but doesn't implement the service still ACKs it. So both UDS functional requests and J1939 global PGNs are fire-and-forget at the application layer. If you need confirmation that a specific ECU received something, address it physically (UDS request ID, or a J1939 destination-specific PGN) and wait for its actual reply, not the CAN ACK bit.
UDS vs OBD-II vs KWP2000
All three ride ISO-TP on CAN. OBD-II (SAE J1979) is the legally-mandated, emissions-only subset: fixed PIDs, no sessions, no security. KWP2000 (ISO 14230) is UDS's older cousin; same grammar, a few service IDs collide and mean different things. UDS is the full modern toolbox. sloppyCAN switches between all three from the protocol toggle on the ISO-TP tab.
Try it
On the ISO-TP tab select UDS and turn on Demo. The palette
buttons each transmit a real request. The ones with a ▾ caret open a small
panel where you pick a sub-function (e.g. session type) or type a hex argument (e.g. a DID),
then Send. A simulated ECU answers, and unknown services come back as
7F <sid> 11 (serviceNotSupported). You can also type any hex into the
input directly, e.g. 22 F1 90 or 10 03.