Here's a progress update.
The protocol for sending OBD messages is called ISO_15765-2. Since CAN doesn't support message with more than 8 bytes, long data like the Vehicle Identification Number (VIN) are encoded across several messages.
Here's the full exchange to get my VIN 3FADP4FJ2BM11391. I'll go into what each message means below. In addition I am working on publishing a class that automates the decoding this protocol.
can0 7DF [8] 02 09 02 00 00 00 00 00
can0 7E8 [8] 10 14 49 02 01 33 46 41
can0 7E0 [8] 30 00 00 00 00 00 00 00
can0 7E8 [8] 21 44 50 34 46 4A 32 42
can0 7E8 [8] 22 4D 31 31 33 39 31 33
You have the right request to get the VIN. It's a Parameter ID (PID) request (0x09) for the VIN (0x02). You send that as a broadcast message (0x7DF) with a length of 2.
The first message sent in reply by the ECU (ID 0x7E8) starts with 0x1014 which means extended message with a length of 0x14 = 20 bytes. Then 0x49 means a reply to the 0x09 PID request and 0x02 is the PID that was requested. The start of the data follows.
Next the requestor must send a flow control message to say: "OK to continue sending the rest of the message". The flow control message must be sent with a CAN ID that is the ID of the reply from the ECU minus 8. So the flow control here is sent with ID 0x7E0 with data 0x30 followed by 7 times 0x00.
Then the ECU sends each subsequent piece of data with a frame that starts with 0x2y where y is a sequence number. The other 7 bytes are data.
I generated the exchange using the Carloop in running the socketcan firmware.
I ran these commands on my Linux laptop after flashing the socketcan firmware and connecting the Carloop to my car with the ignition and and to my laptop by USB:
sudo slcand -o -c -s6 /dev/ttyACM* can0
sudo ifconfig can0 up
# See all messages with ID larger than 0x780
candump can0,780:780
# In a different terminal
cansend can0 7df#0209020000000000 && sleep 0.01 && cansend can0 7e0#3000000000000000
I'll keep working on wrapping this request into a demo app I can share with everybody.
Here's the beginning of the OBD message decoder class:
To use it you'd do (I have not tested the code yet. I'll fix errors in this code as soon as I have time to test it):
void setup() {
carloop.begin();
CANMessage message;
message.id = OBD_CAN_BROADCAST_ID;
message.len = 8; // just always use 8
message.data[0] = 0x02; // 0 = single-frame format, 2 = num data bytes
message.data[1] = 0x09;
message.data[2] = 0x02;
carloop.can().transmit(message);
}
void loop() {
static OBDMessage obd;
CANMessage message;
if (carloop.can().receive(message)) {
if (message.id == 0x7E8) {
// Add the data to our OBD message
bool needsFlowControl = obd.addMessageData(message);
if (needsFlowControl) {
// Sending flow control
CANMessage flowControl = obd.flowControlMessage();
carloop.can().transmit(flowControl);
}
// Use the data when the message is complete
if (obd.complete()) {
for (auto it = obd.data().begin(); it != obd.data().end(); ++it) {
uint8_t data = *it;
Serial.printf("%c", data);
}
Serial.println("");
obd.clear();
}
}
}