Socket
Socket
Sign inDemoInstall

github.com/rzetterberg/elmobd

Package Overview
Dependencies
1
Alerts
File Explorer

Install Socket

Detect and block malicious and high-risk dependencies

Install

    github.com/rzetterberg/elmobd

Package elmobd provides communication with cars OBD-II system using ELM327 based USB-devices. Using this library and a ELM327-based USB-device you can communicate with your cars on-board diagnostics system to read sensor data. Reading trouble codes and resetting them is not yet implemented. All assumptions this library makes are based on the official Elm Electronics datasheet of the ELM327 IC: https://www.elmelectronics.com/wp-content/uploads/2017/01/ELM327DS.pdf After that introduction - Welcome! I hope you'll find this library useful and its documentation easy to digest. If that's not the case, please create a GitHub-issue: https://github.com/rzetterberg/elmobd/issues You'll note that this package has the majority its types and functions exported. The reason for that is that there are many different commands you can send to a ELM327 device, and this library will never we able to define all commands, so most functionality is exported so that you can easily extend it. You'll also note that there are A LOT of types. The reason for this is each command that can be sent to the ELM327 device is defined as it's own type. The approach I wanted to take with this library was to get as much type safety as possible. With that said, as an end user of this library you'll only need to know about two kinds types: the Device type and types that implement the OBDCommand interface. The Device type represents an active connection with an ELM327 device. You plug in your device into your computer, get the path to the device and initialize a new device: This library is design to be as high level as possible, you shouldn't need to handle baud rates, setting protocols or anything like that. After the Device has been initialized you can just start sending commands. The function you will be using the most is the Device.RunOBDCommand, which accepts a command, sends the command to the device, waits for a response, parses the response and gives it back to you. Which means this command is blocking execution until it has been finished. The default timeout is currently set to 5 seconds. The RunOBDCommand accepts a single argument, a type that implements the OBDCommand interface. That interface has a couple of functions that needs to exist in order to be able to generate the low-level request for the device and parse the low-level response. Basically you send in an empty OBDCommand and get back a OBDCommand with the processed value retrieved from the device. Suppose that after checking the device connection (in our example above) we would like to retrieve the vehicles speed. There's a type called VehicleSpeed that implements the OBDCommand interface that we can use. We start by creating a new VehicleSpeed using its constructor NewVehicleSpeed that we then give to RunOBDCommand: At the moment there are around 15 sensor commands defined in this library. They are all defined in the file commands.go of the library (https://github.com/rzetterberg/elmobd/blob/master/commands.go). If you look in the end of that file you'll find an internal static variable with all the sensor commands called sensorCommands. That's the basics of this library - you create a device connection and then run commands. Documentation of more advanced use-cases is in the works, so don't worry! If there's something that you wish would be documented, or something that is not clear in the current documentation, please create a GitHub-issue.


Version published

Readme

Source

-- org-confirm-babel-evaluate: nil --

  • README

[[file:https://img.shields.io/badge/status-active-green.svg]] [[https://travis-ci.org/rzetterberg/elmobd][file:https://travis-ci.org/rzetterberg/elmobd.svg?branch=master]] [[https://goreportcard.com/report/github.com/rzetterberg/elmobd][file:https://goreportcard.com/badge/github.com/rzetterberg/elmobd?status.svg]] [[https://godoc.org/github.com/rzetterberg/elmobd][file:https://godoc.org/github.com/rzetterberg/elmobd?status.svg]]

#+NAME: version_output #+begin_src emacs-lisp :results raw :exports results (with-temp-buffer (insert-file-contents "./VERSION") (format "- Version :: %s" (buffer-string)))) #+end_src

#+RESULTS: version_output

  • Version :: 0.8.0

Go library for communicating with cars [[https://en.wikipedia.org/wiki/On-board_diagnostics][OBD-II]] system using [[https://www.elmelectronics.com/ic/elm327/][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.

** How it works

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 [[https://en.wikipedia.org/wiki/Facade_pattern][facade]] between the computer and the car. You talk to the ELM327 using a simple text based protocol similar to the [[https://en.wikipedia.org/wiki/Hayes_command_set][Hayes command set]] and the ELM327 takes care of the communication details of the car.

#+LATEX: \vspace{0.5cm} #+LATEX: \begin{center} #+ATTR_LATEX: :width 8cm #+RESULTS: fig:overview [[file:docs/assets/overview-diagram.png]] #+LATEX: \end{center}

As shown in the diagram above this library connects to a serial device of the operating system. The library is not concerned with what is connected to that serial device, whether it's a bluetooth USB-dongle with a ELM327 at the other end or a ELM327 connected directly via an USB-cable.

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.

** Example usage

Note: these examples are performed on Linux. If you are using another platform there should be minimal changes, but they are not documented yet. Go ahead and put a :+1: on issue #11 if you think this should be prioritized.

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:

#+BEGIN_EXAMPLE $ 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 #+END_EXAMPLE

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 #+NAME: src:example1 #+BEGIN_SRC go :tangle ./examples/example_1/main.go :mkdirp yes 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.NewTestDevice(*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)

} #+END_SRC

Note: These examples uses the function NewTestDevice, which uses a mocked ELM327 device. To use a real ELM327 device, you instead use NewDevice. The reason why a mocked device is used is because the examples should be runnable without using a real device.

#+BEGIN_EXAMPLE $ go run example.go Device has version OBDII by elm329@gmail.com #+END_EXAMPLE

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 #+NAME: src:example2 #+BEGIN_SRC go :tangle ./examples/example_2/main.go :mkdirp yes 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.NewTestDevice(*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())

} #+END_SRC

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 #+NAME: src:example3 #+BEGIN_SRC go :tangle ./examples/example_3/main.go :mkdirp yes 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.NewTestDevice(*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")
}

} #+END_SRC

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 :: Check if given command is supported
  • FilterSupported :: Filters out supported commands from given list

For simplicity there's a function called GetSensorCommands which gives you a list of all the commands defined in the library. You can use this list of commands and filter out what commands are supported on by car:

example4.go #+NAME: src:example4 #+BEGIN_SRC go :tangle ./examples/example_4/main.go :mkdirp yes 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.NewTestDevice(*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
}

allCommands := elmobd.GetSensorCommands()
carCommands := supported.FilterSupported(allCommands)

fmt.Printf("%d of %d commands supported:\n", len(carCommands), len(allCommands))

for _, cmd := range carCommands {
	fmt.Printf("- %s supported\n", cmd.Key())
}

} #+END_SRC

Besides checking sensor values, you can also check whether the [[https://en.wikipedia.org/wiki/Check_engine_light][MIL]] is on and if there are any [[https://en.wikipedia.org/wiki/On-board_diagnostics#EOBD_fault_codes][DTCs]]:

example5.go #+NAME: src:example5 #+BEGIN_SRC go :tangle ./examples/example_5/main.go :mkdirp yes 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.NewTestDevice(*serialPath, false)

if err != nil {
	fmt.Println("Failed to create new device", err)
	return
}

cmd, err := dev.RunOBDCommand(elmobd.NewMonitorStatus())

if err != nil {
	fmt.Println("Failed to get monitor status", err)
	return
}

    status := cmd.(*elmobd.MonitorStatus)

fmt.Printf("MIL is on: %t, DTCamount: %d\n", status.MilActive, status.DtcAmount)

} #+END_SRC

Please see [[https://godoc.org/github.com/rzetterberg/elmobd][the godocs]] for a more detailed explanation of the library and it's structure.

** Features

  • Reading sensor data
  • Reading trouble codes
  • Resetting Check Engine Light
  • Reading freezed sensor data

** Roadmap

The project uses quarterly milestones to plan upcoming changes. The current quarter will focus on implementing new features. To see the details of what will be done see the milestone [[https://github.com/rzetterberg/elmobd/milestone/4][2018 Q3]].

Changes of the library are tracked in the [[file:CHANGELOG.md][CHANGELOG]].

** Compability

*** Platforms

The library has been built and tested on the following platforms:

| Operating system | Go version | |---------------------+------------| | Linux 4.9.25 x86_64 | 1.9 |

*** Cars

The library has been used successfully on the following cars:

| Car | Library version | Tester | |---------------------------+-----------------+--------------| | Lexus IS200 Manual 2004 | 0.3.0 | @rzetterberg | | Ford Ka 2011 | 0.5.0 | @Enrico204 | | Ford Transit Automat 2019 | 0.6.0 | @mikspec |

FAQs

Last updated on 04 Apr 2023

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc