I recently entered the dark world of automotive diagnostics. A bad diagnostic trouble code and a jaw dropping repair quote led me down this path. What started off as an unsteady and reluctant journey has yielded some hidden treasures in the form of a standard microcontroller and an endless source of data.
Since the mid nineties (1996) all vehicles in the US by law must support a standard set of diagnostic codes called OBD-II. These are monitored and reported by the engine control unit (sometimes also known as the ECM). These codes can be read from scan tools that range from simple to quite sophisticated. Under the hood many of the aftermarket scan tools use a PIC microcontroller loaded with Elm Software’s firmware known by its model number, ELM327. The ELM327 normalizes the various protocols that ECUs speak providing a single API to program against.
In short to talk to your car you need the following items:
- an ELM327 device (bluetooth or usb)
- a serial connection (zterm, pyserial/miniterm, etc)
- the ELM327 data sheet
- the OBD-II PID reference
For example, I used the BAFX bluetooth reader (which actually looks like an ELM knock-off — oops), which easily establishes a bluetooth connection with my laptop creating a new serial port. I then connect using the free miniterm that is linked from pyserial’s website. I also tried pyobd, which has potential but didn’t support my car’s handshake quite right. After modifying some of the source, I found it easier to use miniterm directly until I knew what I was looking at.
Notes About elm and Serial Connections
Serial connections can seem like a bygone era populated with such quaint devices as phonographs and ham radios. In reality they are still a staple to any device manufacturer that needs to directly communicate with an embedded device. These protocols are easily defined and implemented, and many protocols are so widespread that drivers are readily available. Although fairly simple, serial interfaces are not very forgiving if the basic communication parameters are incorrect. For ISO9141-2 (what I’m connected with) you need to ensure that parity is off, there are 8 data bits, 1 stop bit, and a baud rate of 10.4k. The underlying protocol used to communicate to the ECM the link to your computer seems to require a compatible baud rate. I haven’t verified this, but I think the reason is that many of the commands are passed through to the ECU. I’ve read that for older cars ELM327 is too fast, but it appears that by setting the baud rate properly fixes this issue.
Anyone serious about interfacing with their car should really read the elm data sheet. However I’ll admit that data sheets aren’t for everyone (one of my “talents” is to enjoy “boring stuff”), so here are some salient points. To start, any commands that begin with AT are for the microcontroller itself and not for the ECM. ELM327 is case insensitive and also ignores whitespace. This means that ATZ and AT Z are equivalent. A command that is unrecognized returns with a ‘?’ while the ECM will respond with NO DATA (which happens to be ambiguous at times — more on that later). One final note is that ELM327 by default only sends carriage returns. Make sure you handle this correctly on linux machines otherwise the output will be difficult to use.
Handshake and Initialization
When connecting to an elm device, it typically responds with the version of the firmware it is running. This is equivalent to resetting the system, which is done with AT Z. After this step you need to choose a protocol to speak to the ECM. With my device this seems to be automatic, but if you plan to write software to connect it is better to make this explicit via AT SP Ax (x = protocol). Note that this will write to the EEPROM and set this as the default (with fallback as a search). To just try a protocol, use AT TP x.
Once connected you can verify the protocol used (should be the default) with AT DP. Note that when connecting via software you will want to turn off echo (AT 0) so that the data received is strictly the response and not the echo of your input. The final step in this process is to issue the 0100 command directly to the ECM, which requests all supported PIDs between 00 and 10 in hex. A PID is actually two things: the mode and the parameter ID. The modes act as a high-level grouping. The response is a sequence of bytes where each bit acts as a flag indicating whether a p-code is supported starting at 0101 and ending at 0120 in hex. The mapping is taking each byte in sequence, so expand the hex values to binary and move along in that order. For example if your first byte is 8A, this translates to
hex: 8 A ------- ------- 0 1 0 0 1 0 1 0 PID: 1 2 3 4 5 6 7 8
OBD-II responses are variable length but start with a predictable sequence. Valid responses begin with 4x, where x is the mode and the following byte contains the PID. The remainder is the output from the ECM. These values are also in hex and need to be converted. If you get a NO DATA response then nothing was returned from the ECM. From what i gather this could result from one of two things: an unsupported code or a faulty sensor that is not returning data. To know which it is requires ensuring that you interpret the output of the 0100 command properly.
So where to from here? For a data junkie like me the next step is reading real time data and plotting the results. It could get interesting integrating the car data with GPS. Feel free to send me ideas.