The protocol stack
BMW diagnostic communication is layered. Beemuu uses the same model for both KWP2000 and UDS paths:
| Layer | KWP2000 path (E-series) | UDS path (F/G-series) |
|---|---|---|
| Transport | USB → serial → FTDI → K+DCAN cable → OBD-II socket | USB / Ethernet → RJ45 → ENET cable → OBD-II socket |
| Physical | CAN bus (D-CAN, 500 kbit/s) or K-line (10.4 kbit/s) | Automotive Ethernet (100 Mbit/s) |
| Discovery | Beemuu probes known module addresses | UDP broadcast on port 13400 (still not implemented in project) |
| Segmentation | Not needed (single-frame requests) | ISO-TP (CAN path) or DoIP framing (Ethernet path) |
| Service layer | KWP2000 (startDiagnosticSession, readDataByLocalIdentifier, etc.) | UDS (DiagnosticSessionControl, ReadDataByIdentifier, etc.) |
| Application | Beemuu’s protocol helpers (read_vin, read_faults, run_service_function) | Same Beemuu helpers, different framing |
KWP2000 (E-series)
KWP2000 is the older BMW diagnostic protocol. It runs on a single-wire K-line bus or, on later E-series, on the dual K+DCAN cable’s D-CAN pair. Single-frame requests — each request fits in a single CAN frame (8 bytes). No segmentation, no security access gate, smaller data blocks.
The project’s KWP2000 path is in src-tauri/src/protocol/kwp2000.rs. Service identifiers are 1-byte locals (for example, 0x90 for VIN read on E-series DME). The framing is straightforward request-response; the deadline (typically 1 s default, 3 s for slow modules) is the main tuning parameter.
What the operator sees
- Reads are fast (typically under 100 ms for fault codes).
- Per-target KWP response deadline is hardware-aware: it scales with the FTDI VCP latency timer. A 1 ms latency timer doesn’t pay a 3 s penalty on a 1 s target.
- Service functions (CBS reset, etc.) use
startDiagnosticSession+startRoutineByLocalIdentifier.
UDS (F/G-series)
UDS is the modern diagnostic protocol standardised as ISO 14229. It runs over CAN with ISO-TP segmentation, or over Ethernet with DoIP framing. UDS adds several features KWP2000 doesn’t have:
- Larger data blocks via ISO-TP or DoIP segmentation.
- SecurityAccess (0x27) gates write operations behind a seed/key challenge. CBS reset, battery registration, and any coding operation must pass SecurityAccess first.
- RoutineControl (0x31) is the standard service for invoking service functions. The DID format is consistent across targets (16-bit), unlike KWP2000’s 1-byte local identifiers.
- ReadDataByIdentifier (0x22) reads a single DID. The same service reads VIN, fault memory, live-data DIDs.
What the operator sees
- Reads over DoIP are very fast (typically under 30 ms for fault codes).
- SecurityAccess can be a barrier: a misconfigured seed/key routine will hang the service function indefinitely.
- Service functions on F/G-series are larger operations; a CBS reset reads the current state, computes the new state, writes, verifies.
DoIP (transport)
DoIP is the transport that puts UDS over automotive Ethernet. It’s standardised as ISO 13400 and uses UDP for discovery (port 13400) and TCP for the actual diagnostic session.
Beemuu’s DoIP UDP broadcast discovery is still honestly not implemented per the v0.14.4 CLAUDE.md refresh. The project preserves this as an open item rather than overstating. The work is well-scoped and ready to claim — see the roadmap.
What does work in Beemuu today: the UDS-over-DoIP framing once a connection is established. You can plug an ENET cable into a router, set the laptop IP in the 169.254.x.x range, and Beemuu talks UDS directly.
ISO-TP (segmentation)
ISO-TP is the segmentation layer that lets UDS messages longer than 8 bytes be split across multiple CAN frames. The protocol is a simple frame-reassembly scheme:
- First frame (FF): carries the length and the first 6 bytes.
- Consecutive frames (CF): carry 7 bytes each, in order.
- Flow control (FC): the receiver tells the sender how many frames to send before pausing.
Beemuu’s src-tauri/src/transport/isotp.rs handles ISO-TP for UDS-over-CAN. The DoIP path uses a different segmentation scheme on top of UDP, defined as part of ISO 13400.
Timing & deadlines
Both KWP2000 and UDS paths have timing constraints. The v0.13.0 cycle added per-target KWP response deadlines (1 s default, 3 s “slow”) that scale with the FTDI VCP latency timer. The UDS path uses the standard P2 server timer (typically 50 ms) and P2* (typically 5 s for enhanced operations).
The CLAUDE.md invariants (preserved by the v0.14.4 refresh) cover timing-critical rules:
- Async commands. Any
#[tauri::command]that touches serial or network transport must beasync fn. Blocking I/O on the main thread freezes the webview. - Tester Present keep-alive. Active diagnostic sessions need a
3E 00/3E 80frame every 2000–4000 ms on an async/isolated worker. Beemuu ships this insrc-tauri/src/keepalive.rswith a 3000 ms interval. - Per-target deadlines. A 1 ms latency timer shouldn’t pay a 3 s penalty on a 1 s target. The deadline is hardware-aware.
In Beemuu
The project’s src-tauri/src/protocol/ module is the protocol dispatch. The protocol::read_vin helper handles both E-series (KWP2000 1A 90) and F/G-series (UDS 22 F1 90) from a single call site. Same pattern for read_faults, read_live_data, and the service functions.
For the operator, the protocol choice is transparent: pick the right cable, Beemuu handles the framing. What you do see is that F/G-series reads are generally faster (DoIP is faster than D-CAN for fault reads), and F/G-series service functions sometimes need SecurityAccess first while E-series doesn’t.
Protocol questions
Why does the E-series use KWP2000 and the F/G-series use UDS?
E-series BMWs were designed before UDS over CAN was standardised. KWP2000 was the BMW-specific protocol at the time. F-series moved to UDS running over DoIP, which supports larger data blocks, ISO-TP segmentation, security access and routine control in one standard.
Does Beemuu support both?
Yes. KWP2000 over K+DCAN for E-series, UDS over DoIP for F/G-series. The protocol module dispatches based on the cable and chassis.
What is ISO-TP?
ISO Transport Layer (ISO 15765-2). The segmentation layer that splits UDS messages longer than 8 bytes across multiple CAN frames.