Open camercu opened 2 years ago
Here is a capture (using candump
) of what "good" OBD2 traffic should look like when requesting a VIN (Service 09, PID 02):
vcan0 7DF [8] 02 09 02 00 00 00 00 00 <== OBD2 request sent to functional (broadcast) address 0x7df
vcan0 7E8 [8] 10 14 49 02 01 57 41 55 <== ECU responds with first frame, here using ArbID 0x7e8
vcan0 7E0 [8] 30 00 00 00 00 00 00 00 <== FlowControl frame sent to ECU's ArbID minus 8 (here 0x7e0)
vcan0 7E8 [8] 21 5A 5A 5A 38 56 39 46 <== ECU responds with rest of data
vcan0 7E8 [8] 22 41 31 34 39 38 35 30
vcan0 7E8 [2] 23 FE
Here is what the current implementation actually sends, when using 0x7df as the tx_id:
can0 7DF [8] 02 09 02 00 00 00 00 00
can0 7E8 [8] 10 14 49 02 01 34 54 31
can0 7DF [8] 30 00 00 00 00 00 00 00 <== FlowControl frame incorrectly sent to original ArbID, 0x7df
=== ECU hangs, awaiting FC response on 7E0 ====
And here is what happens when using 0x7e0 as the tx_id on a 2020 Toyota Camry or 2022 Carolla:
can0 7E0 [8] 02 09 02 00 00 00 00 00
=== ECU does not respond ===
I talked to the author of the can-isotp
Linux kernel module, and he confirmed that the OBD2 request message should be sent as a broadcast message in a single CAN frame, with 8 ISO-TP sockets established to listen for responses.
OK thanks for this!
I will work on a new OBD2 diagnostic server when I get time to fix this issue :)
Adding a note about extended addressing for whenever we can get around to fixing this issue.
If using 29-bit (extended) addresses instead of the typical 11-bit addressing, the broadcast ID is 0x18DB33F1 instead of 0x7DF.
If the vehicle responds to the requests, you'll typically see responses with CAN IDs 18DAF100 to 18DAF1FF (in practice, typically 18DAF110 and 18DAF11E). The response identifier is also sometimes shown in the 'J1939 PGN' form, specifically the PGN 0xDA00 (55808), which in the J1939-71 standard is marked as 'Reserved for ISO 15765-2'.
ref: https://www.csselectronics.com/pages/obd2-explained-simple-intro
Background
According to Wikipedia (sorry I don't have access to the actual OBD-II specification):
This library's current code has you specify a sid/did (
tx_id
,rx_id
) for OBD messages, and uses a single socket (socketCAN ISO-TP kernel interface) to handle sending/receiving messages on just those addresses. However, this causes problems when running against vehicles that follow the spec.Observed Behavior
On a 2020 Toyota Camry, I tested
Service_09::get_vin()
, but it causes a timeout error. The reason is that if I set thetx_id
to0x7df
, I get the first frame back from the ECU (with CAN ID0x7e8
), but the ECU pauses and waits for a FlowControl frame to be sent with CAN ID0x7e0
(8 less than the ECU's transmit ID). The ISO-TP kernel sends flow control using the original broadcast address,0x7df
, which the ECU ignores. If I set thetx_id
to 0x7e0 and request the VIN, the ECU completely ignores the request.Other Implementations
I have a scan tool that sends the OBD messages properly, and I looked at the example code for Comma.ai's Panda, and they also send the request to 0x7df and listen on 0x7e8 (which sends the FC frame using ID 0x7e0). Their handling of UDS protocol does similar filtering to ensure the tx addr is changed from 0x7df to something in the range 0x7e0-0x7e7 (based on the first response received).
Proposed Design
The OBD code should have a single transmit socket with
tx_id = 0x7df
, and eight listening sockets withrx_id
in the range 0x7e8 - 0x7ef (andtx_id
for FC frames set to a value that is 8 less than therx_id
). This would follow the specification, allowing 8 ECUs to respond to OBD queries simultaneously. If it is a simple request, like to get the VIN, then that function can just take the first response and return it. In order for this to be possible, you will first have to implement the suggestion in Issue #11 to have a software-based ISO-TP channel. Thesocketcan-isotp
crate definitely supports this, as demonstrated in their examples.