Have you ever thought about different vehicle parameters being displayed in your own Android application? If so, then you've come to the right place. We will discuss what it takes to develop an OBDII reader application.
First, let's take a quick look at the protocols used in vehicles for diagnostics.
OBD is an acronym for “on-board diagnostics” and refers to vehicle's self-diagnostics and reporting capability. Originally it was intended to help mechanics run quick vehicle diagnostics. The early versions of it allow checking for possible problems with the engine. Nowadays, in addition to preserving the “diagnostics” part, this system has been dramatically enhanced and allows a vehicle owner to be in control of different parameters like the current fuel consumption rate, gear and transmission modes, GPS module and so on. For a full understanding of how it works and the history behind it, you can read the full article on Wikipedia called “On-board diagnostics”.
Necessary materials
First of all, you would need a OBDII adapter to be able to use the OBDII interface. There are many adapters from different manufacturers. Some of them have a COM interface, others have a USB interface, and some have a Bluetooth interface. Theoretically, any adapter can be used by the Android application, but in practice, your best choice is the Bluetooth interface.Also, adapters can be differentiated by supported OBDII protocols (i.e. by supported vehicles). So, if you have a car within reach and good OBDII adapter we can start developing OBDII reader application.
Hold on, do you really have a car close enough to your development environment? Actually, we can use a simulator for a start. The one that worked for me is OBDSim: it is an open source project available for different platforms. Since Bluetooth is not supported on Windows, rebuilding it from scratch in Linux will be necessary. Please note that most likely it will require you to modify the source code and change RFCOMM channel to the first available instead of channel 1.
What's more, a hardware emulator can be used instead of a real car. We used ECUsim 2000 standard with ISO 15765 (CAN) protocol enabled. As for the OBDII adapter we used ELM327 v.1.5 adapter.
Developing the application
Let's start with describing the protocol that is used for communication between the Android device and OBDII adapter/vehicle. It is text-based polling type protocol. This means that all you need is to send a command to receive a response. And knowing what commands to send is key.
We will connect to the adapter through Bluetooth. It seems that Bluetooth Low Energy API is just the thing we should use. However, since it is now supported only by several devices, it is too early to use it.
The protocol supports some AT commands like turning echo and control line feed. Another part of the protocol is OBDII control protocol itself.
The general workflow of the application functionality should go like this:
1) connect to the OBDII adapter through Bluetooth;
2) initialize OBDII adapter with AT commands;
3) continuously get data from the vehicle by issuing the corresponding PID codes.
Connecting to the OBDII adapter is straightforward. But one thing you need to do before connecting is to select the Bluetooth device. Displaying an alert dialogue with single choice items is a good way to go:
ArrayList deviceStrs = new ArrayList();
final ArrayList devices = new ArrayList();
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
Set pairedDevices = btAdapter.getBondedDevices();
if (pairedDevices.size() > 0)
{
for (BluetoothDevice device : pairedDevices)
{
deviceStrs.add(device.getName() + "\n" + device.getAddress());
devices.add(device.getAddress());
}
}
// show list
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.select_dialog_singlechoice,
deviceStrs.toArray(new String[deviceStrs.size()]));
alertDialog.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
String deviceAddress = devices.get(position);
// TODO save deviceAddress
}
});
alertDialog.setTitle("Choose Bluetooth device");
alertDialog.show();
Don’t forget to save the selected device address somewhere. Now we can connect to the selected device:
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = btAdapter.getRemoteDevice(deviceAddress);
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(uuid);
socket.connect();
The UUID, as we mentioned, represents a “serial” interface through Bluetooth. Of course, this code should be executed in a non-UI thread. Also, we would recommend checking this article for details and a solution of a bug in Android that can lead to connection failure in some cases. We can now make the data exchange. We will use an OBD-Java-API library for this. The library is pretty simple to use. It has several classes, which correspond to different OBD commands. Don't forget to initialize the OBDII adapter first by issuing configuration commands:
new EchoOffObdCommand().run(socket.getInputStream(), socket.getOutputStream());
new LineFeedOffObdCommand().run(socket.getInputStream(), socket.getOutputStream());
new TimeoutObdCommand().run(socket.getInputStream(), socket.getOutputStream());
new SelectProtocolObdCommand(ObdProtocols.AUTO).run(socket.getInputStream(), socket.getOutputStream());
We are now ready to issue any other commands:
EngineRPMObdCommand engineRpmCommand = new EngineRPMObdCommand();
SpeedObdCommand speedCommand = new SpeedObdCommand();
while (!Thread.currentThread().isInterrupted())
{
engineRpmCommand.run(sock.getInputStream(), sock.getOutputStream());
speedCommand.run(sock.getInputStream(), sock.getOutputStream());
// TODO handle commands result
Log.d(TAG, "RPM: " + engineRpmCommand.getFormattedResult());
Log.d(TAG, "Speed: " + speedCommand.getFormattedResult());
}
Here we should point that this library has some issues with parsing the response and it often crashes because of poor error handling. The first flaw is the “performCalculations” method present in all command classes. It would be good to check the buffer size before accessing buffer data, because in some cases the response may be shorter than required. Of course, the problem of a shorter response is on the OBDII adapter/vehicle side, but the library should be ready for such issues.
Apart from this, there are some other issues, so this library still requires improvements or can be used simply as a reference.
The obtained data can be stored somewhere for further analysis, for example, on the ElasticSearch instance.
Right now, we are working on a Hours of Service application for commercial vehicle drivers and will continue sharing our experience on OBDII in our blog.