- Version
- 0.2.2
Go library for communicating with cars OBD-II system using ELM327 based USB-devices.
To make this library as good as possible - feedback, bug reports and feature requests are very welcome in the GitHub issues of this project.
There are more than 10 different OBD-II signal protocol variations used by the various cars that exist. To avoid having to handle all the details of these protocols the ELM327 exists. The ELM327 acts a facade between the computer and the car. You talk to the ELM327 using a simple text based protocol similar to the Hayes command set and the ELM327 takes care of the communication details of the car.
Communicating with the ELM327 is similar to communicating with a web server. You make a request and wait for a response. However, in this context we are calling a command and waiting for one or more responses.
This library is designed to be used in a way that resembles the way you
physically use the device. You have a type called Device
that represents
a ELM327 device connected to the computer. This Device
then has a function
called RunCommand
that sends a command to the actual device and then waits
for a response.
This library aims to be as type safe as possible, which means that you don’t deal with raw text commands, instead you have different command types.
All command types need to implement the OBDCommand
interface to be
able to be run on the device. Since there are A LOT of OBD commands, you can
easily extend this library, by just implementing the OBDCommand
interface
of your commands.
Let’s start by looking at some example of how you use the library.
First of all, you need to plug in your ELM327 device into your computer and get the path to the device. You can plugin the device and check dmesg, this is what I get on my computer:
$ dmesg | tail [359720.858480] usb 6-2: Manufacturer: FTDI [359720.858482] usb 6-2: SerialNumber: A503GJEX [359720.897717] usbcore: registered new interface driver usbserial [359720.897733] usbcore: registered new interface driver usbserial_generic [359720.897748] usbserial: USB Serial support registered for generic [359720.901755] usbcore: registered new interface driver ftdi_sio [359720.901767] usbserial: USB Serial support registered for FTDI USB Serial Device [359720.901839] ftdi_sio 6-2:1.0: FTDI USB Serial Device converter detected [359720.901913] usb 6-2: Detected FT232RL [359720.904481] usb 6-2: FTDI USB Serial Device converter now attached to ttyUSB0
Now that I know that the device is available at /dev/ttyUSB0
I can use the
library to connect to the device and check the ELM327
version of the device:
example1.go
package main
import (
"flag"
"fmt"
"github.com/rzetterberg/elmobd"
)
func main() {
serialPath := flag.String(
"serial",
"/dev/ttyUSB0",
"Path to the serial device to use",
)
flag.Parse()
dev, err := elmobd.NewDevice(*serialPath, false)
if err != nil {
fmt.Println("Failed to create new device", err)
return
}
version, err := dev.GetVersion()
if err != nil {
fmt.Println("Failed to get version", err)
return
}
fmt.Println("Device has version", version)
}
When I run this executable on my machine with the ELM327 device plugged in I get the following result:
$ go run example.go Device has version OBDII by elm329@gmail.com
The next step is to run some OBD commands on the device. For this we need to plug in the ELM327 into our car and turn on the ignition.
Like mentioned before you use the function RunCommand
that accepts a
OBDCommand
to run. A OBDCommand
has 3 responsibilities:
- Tell the ELM327 what command to run
- Store the value
- Convert the value to a common format
So you start out by creating a new OBDCommand
that does not contain a value.
You then take that OBDCommand
and call the RunCommand
function with it.
RunCommand
will then return the OBDCommand
with the value from the car.
Let’s try this out by checking the RPM of the engine. There is a OBDCommand
for that defined in the library already, called EngineRPM
. We start by
creating a new EngineRPM
that we call RunCommand
with:
example2.go
package main
import (
"flag"
"fmt"
"github.com/rzetterberg/elmobd"
)
func main() {
serialPath := flag.String(
"serial",
"/dev/ttyUSB0",
"Path to the serial device to use",
)
flag.Parse()
dev, err := elmobd.NewDevice(*serialPath, false)
if err != nil {
fmt.Println("Failed to create new device", err)
return
}
rpm, err := dev.RunOBDCommand(elmobd.NewEngineRPM())
if err != nil {
fmt.Println("Failed to get rpm", err)
return
}
fmt.Printf("Engine spins at %s RPMs\n", rpm.ValueAsLit())
}
There are more than 180 different OBD commands, and cars have different support for these commands. So to avoid sending OBD commands to the car that it does not support we can check what commands the car support:
example3.go
package main
import (
"flag"
"fmt"
"github.com/rzetterberg/elmobd"
)
func main() {
serialPath := flag.String(
"serial",
"/dev/ttyUSB0",
"Path to the serial device to use",
)
flag.Parse()
dev, err := elmobd.NewDevice(*serialPath, false)
if err != nil {
fmt.Println("Failed to create new device", err)
return
}
supported, err := dev.CheckSupportedCommands()
if err != nil {
fmt.Println("Failed to check supported commands", err)
return
}
rpm := elmobd.NewEngineRPM()
if supported.IsSupported(rpm) {
fmt.Println("The car supports checking RPM")
} else {
fmt.Println("The car does NOT supports checking RPM")
}
}
The supported
here is a SupportedCommands
which is a special type that
stores the raw lookup table and exposes two helper functions that reads this
table:
IsSupported
FilterSupported
These two functions are used to check if a single command is supported and filter out the supported commands of a list of commands.
Please see the godocs for a more detailed explanation of the library and it’s structure.
- [X] Reading sensor data
- [ ] Reading trouble codes
- [ ] Resetting Check Engine Light
- [ ] Reading freezed sensor data
(Sorted by priority)
Next release (0.3.0
) will happen during December this year and will focus on
performance and sensor support:
- Faster sensor reading
- More robust testing
A feature complete release 1.0.0
is planned to be done in 2018 Q2.
You can check the CHANGELOG for details of historic releases.
The library has been built and tested on the following platforms:
Operating system | Go version |
---|---|
Linux 4.9.25 x86_64 | 1.9 |
The library has been used successfully on the following cars:
Car | Library version |
---|---|
Lexus IS200 Manual 2004 | 0.1.0 |