ECU protocols & PID prefixes¶
You'll notice that some PIDs in a profile are written like 2101 and others like
22B002 — a two-hex-digit prefix vs a four-hex-digit one. That prefix isn't
cosmetic: it's the diagnostic service byte sent as the first byte on the wire,
and it tells you which of two diagnostic protocols the ECU speaks.
Two protocols, two read services¶
A single vehicle bus commonly runs a mix of two diagnostic protocols. They do the same job — "read a data identifier from an ECU" — but number their read service differently:
| Protocol | Read service (SID) | Identifier width | PID looks like |
|---|---|---|---|
| KWP2000 (ISO 14230, older) | 0x21 — ReadDataByLocalIdentifier |
1 byte | 21 + LID → 2101, 21F2 |
| UDS (ISO 14229, newer) | 0x22 — ReadDataByIdentifier |
2 bytes | 22 + DID → 22B002, 22BC07 |
What canair calls a PID
Strictly, in OBD-II a PID is a Parameter ID — a 1-byte code read under
service 0x01. canair uses the word more broadly: "PID" is an umbrella for
any read identifier — a standard OBD-II PID, a KWP2000 LID, or a UDS
DID. So don't be surprised that 22B002 (a DID) is called a "PID". This
isn't canair being sloppy — it's WiCAN's convention: profiles ultimately
compile to the WiCAN AutoPID format (see canair wican),
whose profile schema keys every parameter group under pid regardless of
the underlying diagnostic service.
So the prefix is the service ID (SID) — the literal first byte of the request:
BMS:2101puts21 01on the wire: service0x21(KWP2000 read), 1-byte local identifier01.CLU:22B002puts22 B0 02on the wire: service0x22(UDS read), 2-byte data identifierB002.
That's also why the identifier widths differ. KWP2000's 0x21 carries a 1-byte
LID (only 01–FF, 255 pages) → PIDs like 2101, 2102, 21F2. UDS's 0x22
carries a 2-byte DID (65 536 possible) → PIDs like 22B002, 22BC07.
id_protocol records which one each ECU speaks¶
Every ECU in a profile carries an id_protocol field in its ecus/<name>.yaml:
id_protocol: UDS— answers22/F1xx identity DIDs (e.g. CLU, IGPM, BCM, ESC).id_protocol: KWP2000— answers21/1Axx identity LIDs (e.g. the powertrain ECUs BMS, MCU, VCU, OBC on the bundled Ioniq).id_protocol: none— NRCs both (identity-only silent modules).
canair reads this field and auto-selects the right service for you across
read, scan, identity, routines, and iocontrol — so it never blind-sends
a 0x22 UDS request to a KWP2000 ECU (which would NRC), and vice versa. This is
why a bare identifier often just works: the tool supplies the correct SID from the
ECU's protocol.
The prefix is optional but recommended
Because canair knows each ECU's protocol, a UDS ECU accepts a bare DID
(CLU:B002) as well as the full CLU:22B002. Prefer the full form
(22B002) — it's self-documenting and matches the convention in the bundled
Ioniq profile. A KWP2000 PID is conventionally written with its 21 prefix
too (2101).
The -1 / -2 in byte tooling¶
The service byte also shifts where the data starts in the response, which is why
canair bix has -1 (1-byte subfunction, service 21) and -2 (2-byte
subfunction, service 22) modes. A 21 01 response starts 61 01 <data…>
(1 echo byte); a 22 B0 02 response starts 62 B0 02 <data…> (2 echo bytes).
canair bix --annotate doesn't need the flag at all — it reads the service from
the response SID and labels the identifier for what it is (LID for KWP2000
0x21, DID for UDS 0x22, PID for an OBD-II mode), so -1/-2 are only an
override. See Byte indexing for how that feeds into Bnn
offsets.
See it in a profile¶
canair ecu # every ECU with its protocol
canair ecu BMS # one ECU: protocol, addresses, PID/param counts
canair identity BMS # reads identity, auto-picking 21 vs 22 by protocol
Further reading¶
Marque-specific DID conventions — range semantics (22Bxxx cluster, 22Cxxx
body, 22Fxxx flash), paging vs indexing, and the Hyundai/Kia identity-DID -1
offset — are worked through in the reverse-engineer-signal skill's UDS-conventions
reference. Those patterns vary by manufacturer; expect to re-derive them for a new
car.