前言
根据GB18285,GB3847 机动车辆的检测引入了OBD检查,所以需要开发一款OBD诊断仪,配合之前的工作,将车辆OBD数据上报。当初接到这个任务的时候,可以说是一脸懵逼,因为特么之前没接触过,什么是OBD,OBD长什么样我特么都不知道,你就给我派任务,这不是搞笑吗。当时真想说,做不了,可是作为一个合格的程序员,说做不了是有点LOW了,所以咱就接下来这个任务了,但是开发周期可以自己定,摆事实讲道理,就是多要点时间,哈哈哈,何必把自己弄得那么狼狈呢,轻轻松松上路开发不好吗。
准备
至于什么是OBD,OBD工作原理,请自行baidu或google。
要进行OBD的开发,肯定是要配合相关硬件的,也就是ELM327,去淘宝或者京东买就可以,也不贵,一个几十块钱。我买的是一款叫TDA327 OBD的产品,因为他们家的产品看介绍挺好的,还支持OEM,并且还做了格式化,能让你快速二次开发,提供蓝牙,WIFI,3G或USB转串口模块,当时觉着哇塞,这不就是我想要的理想的硬件吗,开始开发了才知道(O(∩_∩)O哈哈~),不是这样,这里暂且不表,后面再说。
这里的说的准备工作,主要是Bluetooth的开发。
之前蓝牙开发接触的比较少,所以开发之前看了不少前辈写的博客,然后又去google看了官方的蓝牙开发教程,就可以自己上手了,当然如果你比较懒,你可以去github直接搜bluetooth,有别人封装好的蓝牙库供你使用,但是有一点大多是基于Bluetooth 4.0也就是BluetoothBLE的,Bluetooth 3.0的比较少。好了,废话不多说了,开始正题吧。
正题Bluetooth
一个好用的的Bluetooth程序,要包括一下几个功能点:
(1)扫描其它蓝牙设备
(2)查询本地蓝牙适配器的配对蓝牙设备
(3)建立RFCOMM通道
(4)通过服务发现连接到其它设备
(5)与其它设备进行双向数据传输
蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--在比较高的Android版本上,还要添加上定位权限,才能搜索到蓝牙设备-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
扫描蓝牙设备
1.扫描前的准备
蓝牙3.0的准备工作
//针对经典蓝牙,也就是3.0
//首先,要确认设备是否支持蓝牙功能,蓝牙是否开启,
/**
* 是否支持蓝牙
*
* @return true 支持,false 不支持
*/
private boolean isSupportBluetooth() {
return bluetoothAdapter != null;
}
/**
* 检查蓝牙是否可用
*
* @return true 可用,false 可用
*/
private boolean checkBluetoothEnable() {
return bluetoothAdapter.isEnabled();
}
/**
* 开启蓝牙
*/
private void enableBluetooth() {
if (!checkBluetoothEnable()) {
//bluetoothAdapter.enable();这样打开,不需要用户同意
//下面这种打开方式,需要用户同意打开蓝牙,会有系统弹框
Log.e(TAG, "enable bluetooth.......");
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mContext.startActivityForResult(intent, Constant.REQUEST_CODE_ENABLE_BLUETOOTH);
} else {
Log.e(TAG, "bluetooth is already enabled........");
}
}
/**
* 请求开启蓝牙回调,因为蓝牙打开是一个耗时操作,所以做了3s的延迟
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode != Activity.RESULT_OK)
return;
if (requestCode == Constant.REQUEST_CODE_ENABLE_BLUETOOTH) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
startDiscovery();
}
}, 3000);
}
}
2.扫描工作
蓝牙3.0扫描
/**
* 注册蓝牙广播,你可以多监听状态,也可以只监听你需要的,看你的需求
*/
private void registerBlueReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(receiver, intentFilter);
Log.e(TAG, "register receiver........");
}
/**
* 解绑
*/
public void unregisterReceiver() {
if (receiver != null)
mContext.unregisterReceiver(receiver);
}
/**
* 开始搜索蓝牙设备
*/
private void startDiscovery() {
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
bluetoothAdapter.startDiscovery();
}
/**
* 取消搜索
*/
public void cancelDiscovery() {
if (bluetoothAdapter != null) {
bluetoothAdapter.cancelDiscovery();
}
}
/**
*蓝牙监听广播
*/
public class BluetoothReceiver extends BroadcastReceiver {
private final String TAG = this.getClass().getSimpleName();
private Handler mHandler;
public BluetoothReceiver(Handler handler) {
this.mHandler = handler;
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.e(TAG, "bluetooth receiver:" + action);
switch (action) {
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
Log.e(TAG, "start discovery bluetooth device...");
mHandler.obtainMessage(Constant.BLUETOOTH_START_DISCOVERY).sendToTarget();
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
Log.e(TAG, "finish discovery bluetooth device...");
mHandler.obtainMessage(Constant.BLUETOOTH_FINISH_DISCOVERY).sendToTarget();
break;
case BluetoothDevice.ACTION_FOUND:
Log.e(TAG, "found bluetooth device...");
foundBluetoothDevice(intent);
break;
//连接状态改变
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
Log.e(TAG,"bluetooth state changed........");
changeState(intent);
break;
case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
Log.e(TAG,"bluetooth connect state changed....");
changeConnectState(intent);
break;
}
}
}
配对,连接
- 3.0配对
3.0有进行配对的步骤,4.0貌似没有,直接连接了
/** * 开始配对 * @param address */ public void startPaired(String address) { try { BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address); Method createBondMethod = BluetoothDevice.class.getMethod("createBond"); createBondMethod.invoke(bluetoothDevice); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } /** * 获取已配对过的设备,并尝试连接 * 已配对过的设备,直接连接就行 */ public void getBonedList() { if (bluetoothAdapter == null) { return; } Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices(); if (devices.size() <= 0) { return; } for (Iterator<BluetoothDevice> it = devices.iterator(); it.hasNext(); ) { BluetoothDevice device = it.next(); //自动连接设备 createBond(device); } }
- 3.0连接
蓝牙3.0的连接,其中主要是3个线程参与,一个连接线程ConnectThread,一个监听连接线程ListenerThread,一个读取数据的线程ReadThread
/** * 连接蓝牙 * @param address */ public void connect(String address) { if (bluetoothAdapter.isDiscovering()) { bluetoothAdapter.cancelDiscovery(); } mHandler.sendEmptyMessage(Constant.BLUETOOTH_CONNECT_START); BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); BluetoothSocket bluetoothSocket = null; try { bluetoothSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID)); //启动连接线程 connectThread = new ConnectThread(bluetoothSocket, true); connectThread.start(); } catch (IOException e) { e.printStackTrace(); mHandler.sendEmptyMessage(Constant.BLUETOOTH_CONNECT_FAILURE); try { if (bluetoothSocket != null) { bluetoothSocket.close(); } } catch (IOException e1) { e1.printStackTrace(); } } }
分别看下各个线程
ConnectThread负责连接,重连工作/** * 连接,读取数据,线程, * 也可以连接,读取数据 分开 */ public class ConnectThread extends Thread { private BluetoothSocket socket; private boolean activeConnect; public ConnectThread(BluetoothSocket bluetoothSocket, boolean activeConnect) { this.socket = bluetoothSocket; this.activeConnect = activeConnect; } @Override public void run() { try { //如果是自动连接,则调用连接方法 if (activeConnect) { socket.connect(); } mHandler.sendEmptyMessage(Constant.BLUETOOTH_CONNECT_SUCCESS); } catch (IOException e) { Log.e(TAG, "connect thread occur error..........." + e.getMessage()); Class<?> clazz = socket.getRemoteDevice().getClass(); Class<?>[] paramTypes = new Class<?>[]{Integer.TYPE}; try { Method m = clazz.getMethod("createRfcommSocket", paramTypes); Object[] params = new Object[]{Integer.valueOf(1)}; socket = (BluetoothSocket) m.invoke(socket.getRemoteDevice(), params); Thread.sleep(500); socket.connect(); Log.e(TAG, "retry connect success...."); mHandler.sendEmptyMessage(Constant.BLUETOOTH_CONNECT_SUCCESS); } catch (Exception e1) { e1.printStackTrace(); Log.e(TAG, "retry connect still failed......."); mHandler.sendEmptyMessage(Constant.BLUETOOTH_CONNECT_FAILURE); } } //启动读数据线程 readThread = new ReadThread(socket); readThread.start(); } }
ReadThread工作就是读取、发送数据了:
public class ReadThread extends Thread { private final BluetoothSocket socket; private final InputStream inputStream; private final OutputStream outputStream; public ReadThread(BluetoothSocket socket) { this.socket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Get the input and output streams, using temp objects because // member streams are final try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "read thread init occur error:" + e.getMessage()); } inputStream = tmpIn; outputStream = tmpOut; } @Override public void run() { byte[] buffer = new byte[BUFFER_SIZE]; int bytes; while (true) { try { //读取数据 bytes = inputStream.read(buffer); if (bytes > 0) { final byte[] data = new byte[bytes]; System.arraycopy(buffer, 0, data, 0, bytes); mHandler.obtainMessage(Constant.READ_DATA, data).sendToTarget(); } } catch (Exception e) { Log.e(TAG, "read data occur error........" + e.getMessage()); break; } } } /** * 发送数据,这个是我由于我的硬件要求哈 * <p> * AT 指令全为大写,均以回车、换行字符结尾,所有发送给 QBD 芯片的指令必须在指令结尾附带一个 * 回车符(0x0D)作为指令结束的标志,否则 QBD 芯片不响应该指令。(另 所有空格将被忽略。如 * ATV 和 AT V 芯片默认为同一指令)同样 QBD61 的回传数据也是回车符(0x0D)作为结束标志 注意: * 在连接上车辆后,由于汽车总线速度的限制,发送给 ECU 指令的频率不能过快,特别是 K 线,建议 * 上位机判断 QBD61 的响应再进行下一个指令的发送, * * @param msg */ public void sendMsg(final String msg) { //如果硬件对指令没有特殊要求,用这个就好 byte[] bytes = msg.getBytes(); byte[] bytes = handleMsg(msg); try { if (outputStream != null) { outputStream.write(bytes); } } catch (IOException e) { Log.e(TAG, "send msg occur error:" + e.getMessage()); } } /** * @param msg */ private byte[] handleMsg(String msg) { int i, n = 0; byte[] bos = msg.getBytes(); for (i = 0; i < bos.length; i++) { if (bos[i] == 0x0a) { n++; } } byte[] bos_new = new byte[bos.length+n]; n=0; for(i=0;i<bos.length;i++){//手机中换行为0a,将其改为0d 0a后再发送 if(bos[i]==0x0a){ bos_new[n]=0x0d; n++; bos_new[n]=0x0a; }else{ bos_new[n]=bos[i]; } n++; } return bos_new; } /** * 结束蓝牙,释放资源 */ public void cancel() { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
再看一下监听线程ListenerThread,这个其实要最先启动,一直监听
/** * 监听线程 */ public class ListenerThread extends Thread { private final BluetoothServerSocket bluetoothServerSocket; // private BluetoothSocket bluetoothSocket; public ListenerThread(BluetoothAdapter adapter) { // Use a temporary object that is later assigned to mmServerSocket, // because mmServerSocket is final BluetoothServerSocket tmp = null; try { tmp = adapter.listenUsingRfcommWithServiceRecord(NAME, UUID.fromString(MY_UUID)); } catch (IOException e) { e.printStackTrace(); } bluetoothServerSocket = tmp; } @Override public void run() { BluetoothSocket bluetoothSocket = null; while (true) { try { //线程阻塞,等待设备连接 bluetoothSocket = bluetoothServerSocket.accept(); Log.e(TAG, "listen thread............."); connectThread = new ConnectThread(bluetoothSocket, false); connectThread.start(); if (bluetoothSocket != null) { bluetoothServerSocket.close(); } } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "listen thread occur error:" + e.getMessage()); } } } }
结束语
好了,关于蓝牙3.0的开发的就写这么多,4.0的放在下一篇写。限于能力有限,存在错误之处,请各位指正,有问题的小伙伴,可以下方留言。
蓝牙4.0可以点击这里,OBDII车载诊断仪开发记录之一波三折(二)BlueTooth 4.0
代码下载地址