I am in the process of writing an OBD scanner app to run in the Raspberry Pi, or at least an article about how to do it. Naturally I want to look for prior art. I am sure lots of folks have done this already. It should be built on top of SocketCAN, because that’s what I have been writing about lately.
It didn’t take too much research before I came across a good start. Ethan Vaughan wrote a fairly cool tool in C that he posted on GitHub. It’s a command line app that queries the OBD port for a list of PIDs supported and allows you to query each one of them.
His code is pretty cool. It worked, well at least on the OBD port going to my GM motor. But when I tried it on my 2010 Wrangler’s OBD port…. nothing? Remember from my first article I mentioned how I have a motor swap. I double checked my wiring and then fired up the cansniffer tool to see if I can find any clues.
What I found was interesting indeed. On the GM OBD bus I saw.
99999 | 7DF | 02 01 00 ...
99999 | 7E8 | 06 41 00 BF FF B9 93 AA .A......
But on the Chrysler ODB bus.
99999 | 7DF | 02 01 00 ...
and no response.
We can simulate this ourselves with the cansend tool.
cansend can1 7df#020100
With exactly the same behavior.
So what’s going on here?. If we look at how OBD works again. SAE J1979 OBD2 is a protocol standard running over CAN. It’s used to run diagnostic services. Since there are so many variations of automobile control modules, you need to query the device to see what services or parameter IDs (PID) it supports.
Typically we start with the Service 01 PID 00 - Show PIDs supported. We broadcast a message 0x7DF which is considered the diagnostic device. It contains an OBD length byte of 2 and the service 1 (Show current data) and ask for PIDs supported.
7DF | 02 01 00
We expect that an answer should come from 7E8.
Someone buy me a clue
I seem to remember that ODB-2 message lengths on CAN had 8 bytes of data and that the first byte of actual data is a length of the ODB message. hmm. How is it that we can send a 3 byte message? What if I send 8 bytes on the Jeep instead.
cansend can1 7df#0201000000000000
99999 | 7DF | 02 01 00 00 00 00 00 00 ........
99999 | 7E8 | 06 41 00 BF BE B9 93 00 .A......
WOW! that worked..
OK so what about the short length message.
Watching the detectives
As I dug into Ethan code I noticed that he opened his SocketCAN connection in a different way than I did in my code.
s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP)
where I calling
s = socket(PF_CAN, SOCK_RAW, CAN_RAW)
He was using the datagram service and a CAN ISO-TP socket. Well!
The auto manufacturers realized soon enough that that send only 8 bytes of data was not only a restriction, but was wasteful. So the updated the CAN protocol to ISO 15765-2 CAN-TP. This allows them to send up to 4095 bytes of data and figures out how to do the framing.
This was added to the SocketCAN API in the can-isotp implementation. It’s pretty well documented there too. There are even a set of utilities that are part of the can utilities.
isotpsend - send PDUs from stdin to CAN
isotprecv - print received PDU on stdout
isotpdump - dump CAN frames with PCI decoding
isotpsniffer - dump reassembled ISO-TP PDUs (using CAN_ISOTP socket)
The bottom line
My Jeep is a 2010 model. ISO 15765-2 was revised in 2016. And even then, Chrysler/Jeep has a poor record of adhering to standards and does a lot of propriety stuff. Most of the ODB sniffer hardware out there doesn’t use the CAN_ISOTP stuff but rather opens the connection is some form of RAW mode. I suspect most of them are built on ELM327 chip’s anyhow.