Pull to refresh

Разработка Android приложения для работы с OBDII протоколом

Reading time 5 min
Views 66K
Original author: Ruslan Yanchyshyn@Lemberg Solutions
image

Почему это нужно для вашего автомобиля?


Задумывались ли вы над тем чтоб отобразить параметры работы вашего автомобиля в собственном Android приложении? Если да, тогда добро пожаловать под кат. Мы как раз будем обсуждать вопрос разработки подобного приложения.

Для начала давайте взглянем на протоколы, используемые для диагностики транспортных средств.
OBD — это сокращение для “on-board diagnostics” и относится к средствам само-диагностики и отчетности автомобиля. Протокол изначальное предназначен для ускорения процесса диагностики обслуживающим персоналом. Первые версии позволяли диагностировать некоторые проблемы в двигателе. Сейчас же в дополнении к возможностям диагностики добавляются и другие возможности такие как получение разной информации например о текущем расходе топлива, управление разными узлами например АКПП, режиме работы трансмиссии, получение координат GPS и другое. Узнать более детально как это работает и историю вы можете в Wikipedia.

Необходимые материалы


image
Прежде всего нам нужен OBDII адаптер способный работать с вашим автомобилем. Существует множество таких адаптеров. Некоторые из них имеют COM интерфейс, некоторые — USB интерфейс, а некоторые — Bluetooth интерфейс. Теоретически любой может быть использован для нашего приложения, но на практике лучшим вариантом все-же будет Bluetooth. Также адаптеры могут отличатся поддерживаемыми OBDII протоколами (т.е. фактически поддерживаемыми автомобилями). Так что если у вас под рукой есть автомобиль и подходящий OBDII адаптер, мы можем начать разработку нашего приложения.

Подождите — у вас действительно есть автомобиль достаточно близко к среде разработки? На самом деле мы могли бы использовать симулятор на первых порах. Один из вариантов, работающий у меня — это приложение OBDSim. Это открытый проект доступный для многих платформ. Но поскольку Bluetooth не поддерживается в Windows, то приложение нужно будет собрать из исходных кодов в Linux. Также обратите внимание, что скорее всего вам нужно будет внести изменения в исходный код для того чтоб изменить RFCOMM канал на первый доступный вместо предлагаемого канала 1.

Второй вариант — это аппаратный симулятор, который можно использовать вместо автомобиля. Я использовал ECUsim 2000 standard с включенным протоколом ISO 15765 (CAN). А OBDII адаптер я использовал ELM327 v.1.5
image

Разработка приложения



Давайте начнем с описания протокола, используемого для связи между Android устройством и OBDII адаптером/автомобилем. Это текстовый polling протокол. Это значит что все что вам нужно — это послать команду для того чтоб получить ответ. И знание какие команды можно посылать является ключевым.

Мы будем подключатся к адаптеру через Bluetooth. Похоже что Bluetooth Low Energy API был бы хорошим вариантом. Но поскольку он поддерживается всего несколькими устройствами, то сейчас слишком рано использовать его.

Протокол поддерживает некоторые AT комманды например выключение эха и управление возвратом каретки. Вторая часть протокола — это непосредственно протокол управления OBDII.

Общая схема работы приложения следующая:
  • подключится в OBDII адаптеру через Bluetooth
  • инициализировать OBDII адаптер с помощью AT комманд
  • непрерывно получать требуемые данные с автомобиля путем отправки соответствующих PID кодов


Подключение к OBDII адаптеру достаточно стандартное. Но одна вещь которую нужно сделать перед подключением — это выбор Bluetooth устройства. Отображение alert диалога со списком устройств вполне подойдет:

ArrayList<String> deviceStrs = new ArrayList<String>();
final ArrayList<String> devices = new ArrayList<String>();

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> 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<String> adapter = new ArrayAdapter<String>(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();


Не забудьте где нибудь сохранить адрес выбранного устройства. Теперь мы можем подключится к выбранному устройству:

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();


UUID в коде выше представляет «последовательный» интерфейс через Bluetooth. Конечно этот код должен быть исполнен в не UI потоке. Также я бы рекомендовал посмотреть здесь за деталями и решением ошибки в Android которая может приводить к невозможности подключения в некоторых случаях.

Теперь мы можем обмениваться данными. Для этого мы будем использовать OBD-Java-API библиотеку. Библиотека достаточно простая. Она имеет несколько классов, которые соответствуют разным OBD командам. Не забудьте инициализировать OBDII адаптер путем посылки конфигурационных команд:
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());


Теперь мы готовы посылать другие команды:
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());
}


image

Здесь я хочу отметить что библиотека имеет некоторые проблемы с парсингом и часто падает из-за недостаточно хорошей обработки ошибок. Первая проблема это метод performCalculations, который присутствует во всех классах команд. Было бы хорошо проверять размер буфера перед доступом к нему потому что в некоторых случаях ответ может быть короче чем нужно. Само собой проблема короткого ответа лежит на стороне OBDII адаптера/автомобиля, но библиотека должна быть готова к таким проблемам.

Кроме прочего там есть еще некоторые проблемы, так что эта библиотека все еще требует улучшений или просто может быть использована как источник информации.

Полученные данные могут быть сохранены где-нибудь для дальнейшего анализа, например в ElasticSearch.

А сейчас мы работаем над приложением Hours of Service для водителей грузовиков и продолжаем делится опытом в нашем блоге. Stay tuned!

P.S. На самом деле я также являюсь и автором оригинальной англоязычной версии статьи, которая была опубликована на blog.lemberg.co.uk, так что могу ответить на технические вопросы.
Tags:
Hubs:
+29
Comments 40
Comments Comments 40

Articles