前言

根据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() {@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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 finaltry {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;}@Overridepublic 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 finalBluetoothServerSocket tmp = null;try {tmp = adapter.listenUsingRfcommWithServiceRecord(NAME, UUID.fromString(MY_UUID));} catch (IOException e) {e.printStackTrace();}bluetoothServerSocket = tmp;}@Overridepublic 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
    代码下载地址

OBDII车载诊断仪开发记录之一波三折(-)BlueTooth 3.0相关推荐

  1. Android车载应用开发与分析(13)- 系统设置-蓝牙设置

    1. 前言 Android 车载应用开发与分析是一个系列性的文章,这个是第13篇分析系统设置,该系列文章旨在分析原生车载Android系统中核心应用的实现方式,帮助初次从事车载应用开发的同学,更好地理 ...

  2. 比亚迪智慧开放平台开发记录

    比亚迪智慧开放平台开发记录 前言 注册账号 SDK和API文档下载 项目运行 安装AVD模拟器 启动AVD模拟器 BYD车机数据模拟器(ApiSimulator) 结尾 前言 最近在做一个比亚迪开发平 ...

  3. Android 8.0 BLE 低功耗蓝牙开发记录

    Android 8.0 BLE 低功耗蓝牙开发记录(1-3)--------------(权限申请篇未完待续) 目的:开源博客,希望大家一起修改博客错误地方,共同完善并会鸣谢提供意见的朋友.为大家提供 ...

  4. Android车载应用开发与分析(6)- 车载多媒体(一)- 音视频基础知识与MediaPlayer

    多媒体应用是车载信息娱乐系统的一个重要组成部分,一般包含音视频播放.收音机.相册等.车载应用多媒体系列初步计划分为六篇,这是第一篇. 参考资料 视频和视频帧:视频和帧基础知识整理 百度百科 - 声道 ...

  5. Anytime项目开发记录0

    Anytime,中文名:我很忙. 开发者:孤独的猫咪神. 这个项目会持续更新,直到我决定不再维护这个APP. 2014年3月10日:近日有事,暂时断更.希望可以会尽快完事. 2014年3月27日:很抱 ...

  6. CozyRSS开发记录3-标题栏再加强

    CozyRSS开发记录3-标题栏再加强 1.更精炼的标题栏 接下来,我们把窗口的边框和默认的标题栏给去掉,让Cozy看起来更像一个平板应用. 在主窗口的属性里,修改下列两个属性: 效果一目了然: 2. ...

  7. CozyRSS开发记录19-窗口标题栏交互

    CozyRSS开发记录19-窗口标题栏交互 1.谈谈对mvvm解耦的看法 在使用mvvm时,如何操作窗口,这是一个问题.这个问题的关键点是:mvvm是把view和viewmodel解耦了的,很多写法一 ...

  8. TMS320F28335项目开发记录9_28335之中断系统

    TMS320F28335项目开发记录9_28335之中断系统 2014年11月08日 12:00:12 阅读数:3104 28335中断系统 1.中断系统 在这里我们要十分清楚DSP的中断系统.C28 ...

  9. ASP.NET Core 1.0 开发记录

    ASP.NET Core 1.0 更新比较快(可能后面更新就不大了),阅读注意时间节点,这篇博文主要记录用 ASP.NET Core 1.0 开发简单应用项目的一些记录,以备查阅. ASP.NET C ...

最新文章

  1. python练习:猜价钱小游戏
  2. 终于把微软BING搜索-SPTAG算法的原理搞清了
  3. 有史以来最会写代码的农民诞生!腾讯元老、上市公司CTO赚够钱后辞职!到安徽农村隐居,亲手建造200亩农场!...
  4. PHP自动加载类函数__autoload
  5. 图像分割中mask的保存
  6. ISO base media file format---iso 基础媒体文件格式(专业名称)
  7. leetcode343. 整数拆分(思路+详解)
  8. matlab 多文件编程,是否有可能在MATLAB中为每个文件定义多个函数,并从该文件外部访问它们?...
  9. [html] html标签中的lang属性有什么作用?
  10. “睡服”面试官系列第十七篇之Reflect(建议收藏学习)
  11. 各类木材强度_凯狄解析各类抽芯铆钉的工作原理
  12. python信用卡客户_Python开发之基于模板匹配的信用卡数字识别功能
  13. ToB销售的成交,需要客户对供应商有三个信任
  14. ---PHP中的OOP--面对过程与面对对象基础概念与内容--(封装、继承、多态)...
  15. EXPRESS语言与IFC体系
  16. endnote文献使用简明教程+遇到问题
  17. Go学习笔记 一篇到底
  18. 自定义微博小尾巴 源码+解析
  19. 昱辉阳光出售英国26MW光伏项目
  20. java 生成.pcap_java抓包后对pcap文件解析示例

热门文章

  1. codeforces解题总结:#693 (Div. 3)
  2. 名词:broken pipe
  3. 搞搞字节,byte的小知识
  4. 上网成瘾改变大脑结构:语言功能受影响,让人话都说不利索
  5. Matlab:生成不同的随机数
  6. PubSub订阅与发布
  7. html手机端点击图片放大并根据手势缩放
  8. python 归一化保存与加载
  9. 第一次做学年设计 学生成绩管理系统
  10. 合成控制法(SyntheticControlMethod)及Stata实现