KWP2000 & ISO 14230
Keyword Protocol 2000 is UDS's direct ancestor, the diagnostic language that ran on older ECUs (roughly mid-1990s to late-2000s) before ISO 14229 (UDS) standardised everything. On modern CAN vehicles you meet it as KWP2000-on-CAN (ISO 15765-3): the same ISO-TP transport sloppyCAN already speaks, with a different set of service IDs and error codes on top.
Same ISO-TP Carrier
Originally KWP2000 ran over a K-line (ISO 14230 on ISO 9141 wiring). When the industry moved to CAN, the application layer was re-hosted on ISO-TP (ISO 15765-2), the same Single-Frame / First-Frame / Flow-Control / Consecutive-Frame transport UDS uses. That is why sloppyCAN implements KWP2000 as a mode of the ISO-TP / UDS tab, not a new transport: only the decode tables change.
For the transport mechanics, how a long response splits into First Frame + Flow Control + Consecutive Frames, see the ISO-TP Explainer.
Same Conventions as UDS
The request/response grammar is identical to UDS, so if you know one you know the other:
- A request starts with a Service ID (SID) byte, e.g.
21 F0. - A positive response echoes the SID + 0x40, e.g.
61 F0 …. - A negative response is always
7F <sid> <nrc>. - NRC
0x78"responsePending" means "still working, wait for the real answer," handled automatically.
0xF00x21 + 0x40), RLI echoed, then dataWhere It Differs From UDS
Several KWP service IDs share a number with UDS but mean something different.
sloppyCAN keeps separate KWP tables instead of patching the UDS ones, since
decoding 21 as a UDS service would be wrong.
| SID | In KWP2000 | In UDS |
|---|---|---|
| 21 | ReadDataByLocalIdentifier | Does not exist |
| 1A | ReadECUIdentification | Does not exist |
| 81 | StartCommunication | KWP-only |
| 82 | StopCommunication | KWP-only |
| 83 | AccessTimingParameters | AccessTimingParameter |
| 3B | WriteDataByLocalIdentifier | Uses 2E by DID |
The negative-response-code list also differs in spots. The routing codes
0xA0 ecuNotResponding and 0xA1 ecuAddressUnknown are a KWP thing,
left over from the K-line world where a gateway sat between the tester and the ECU, and UDS
doesn't define them. (The block-transfer codes around 0x71-0x73 look
like a difference but aren't: UDS kept them.) sloppyCAN's KWP mode names these from the
ISO 14230 table.
A Typical Session
Unlike OBD-II, where you can fire a request at a broadcast address, a KWP session is explicitly opened and closed:
| Step | Request | Meaning |
|---|---|---|
| 1 | 81 | StartCommunication, wakes the ECU, gets key bytes back (C1 …) |
| 2 | 10 81 | StartDiagnosticSession, mode 0x81 default (others: 85 programming, 89 standby, 92 EOL) |
| 3 | 1A 9A | ReadECUIdentification, option 0x9A, response is mostly ASCII |
| 4 | 21 F0 | ReadDataByLocalIdentifier, RLI 0xF0 |
| 5 | 3E | TesterPresent, keeps the session alive, sent periodically |
| 6 | 82 | StopCommunication, closes the session cleanly |
TesterPresent matters here. If the tester goes quiet for the session's
timeout (typically a few seconds, negotiable via 0x83), the ECU drops back to
its default session, so diagnostic tools send 3E on a heartbeat.
Try it
Switch the ISO-TP / UDS tab to KWP2000 and turn on Demo.
The palette buttons (StartComms, StartSession, TesterPresent, ReadECUIdent, ReadByLocalId,
StopComms) each transmit a real request, and a simulated ECU answers: positive responses
decoded with KWP service names, unknown services answered with
7F <sid> 11 (serviceNotSupported). You can also type any hex into the
input, e.g. 21 F0 or 1A 9A.