1.打开蓝牙

2.蓝牙扫描,列出可用设备

3.关闭蓝牙扫描(不关闭会一直扫描)

4.找到目标蓝牙设备进行连接

5.连接成功,进行通信

6.关闭蓝牙释放资源

接下来我们要根据上面6个步骤进行API的说明,在说明前,我先说明一下

BLE 即 Bluetooth Low Energy,蓝牙低功耗技术,是蓝牙4.0引入的新技术,在安卓4.3(API 18)以上为BLE的核心功能提供平台支持和API。与传统的蓝牙相比,BLE更显著的特点是低功耗,所以现在越来越多的智能设备使用了BLE,比如满大街的智能手环,还有体重秤、血压计、心电计等很多BLE设备都使用了BLE与终端设备进行通信。

蓝牙BLE4.x

BLE分为三部分:

  • Service

  • Characteristic

  • Descriptor

    这三部分都用UUID作为唯一标识符。UUID为这种格式:0000ffe1-0000-1000-8000-00805f9b34fb。比如有3个Service,那么就有三个不同的UUID与Service对应。这些UUID都写在硬件里,我们通过BLE提供的API可以读取到。

  • 一个BLE终端可以包含多个Service, 一个Service可以包含多个Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个Value。Characteristic是比较重要的,是手机与BLE终端交换数据的关键,读取设置数据等操作都是操作Characteristic的相关属性。

与BLE设备相互通信的大致流程

扫描并与指定的BLE设备进行连接。

连接成功就能拿到设备的GATT、Service、Characteristic、Descriptor这几个关键的东西(其实能拿到很多东西),并且每个Service、Characteristic都有自己唯一的UUID。

开启数据通道,这里的一条数据通道就相当于一个Characteristic,而具体开启哪一个或哪几个,需要根据BLE设备的工程师给的协议文档,如果工程师给的文档很坑的话,就要自己调试,判断等。

手机就可以向BLE设备发送数据或接受BLE向手机发送的数据。

一般BLE设备向手机发送的数据都16进制的数据报,类似于IP数据报,需要自己解析,具体的解析规则BLE设备的工程师都会给一份协议文档的,看着解析就行了,到这里就完成了与BLE设备的通信了。

步骤:

(1)Service蓝牙功能集合,每一个Service都有一个UUID,

(2)Characteristic 在service中也有好多个Characteristic 独立数据项,其中也有独立UUID

上面的两个uuid需要从硬件工程师中获取,这样你才能匹配到你要的。

(3)BluetoothAdapter 蓝牙的打开关闭等基本操作

(4)BluetoothDevice 蓝牙设备,扫描到的

(5)BluetoothGatt 蓝牙连接重连断开连接等操作的类

(6)BluetoothGattCharacteristic 数据通信操作类,读写等操作

1.打开蓝牙

需要权限

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

BluetoothAdapter 这个类就是蓝牙的基本操作,比如打开关闭等

初始化蓝牙,得到BluetoothAdapter

private void initBlueTooth() {BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);if (manager != null) {bluetoothAdapter = manager.getAdapter();if (bluetoothAdapter != null) {//蓝牙没有打开if (!bluetoothAdapter.isEnabled()) {openBle();} else {Toast.makeText(MainActivity.this, "蓝牙已打开", Toast.LENGTH_SHORT).show();scanLeDevice(true);}} else {openBle();}}
}

打开蓝牙

   private void openBle() {//以下两种方式 第二种方式在onActivityResult处理回调
//        boolean enable = bluetoothAdapter.enable();//打开蓝牙'直接打开,用户不知权,用于定制系统'
//        Toast.makeText(MainActivity.this, "正在打开蓝牙", Toast.LENGTH_SHORT).show();
//        if (enable) {
//            Log.e("open",enable+"");
//            new Handler().postDelayed(new Runnable() {
//                @Override
//                public void run() {
//                    scanLeDevice(true);
//                }
//            },2000);
//
//        }//提示用户正在打开蓝牙Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK && requestCode == REQUEST_ENABLE_BT) {scanLeDevice(true);}
}

2.3.打开或者停止扫描,放在同一个方法中

/*** 打开或者停止扫描** @param enable*/
private void scanLeDevice(final boolean enable) {if (enable) {mScanning = true;// 定义一个回调接口供扫描结束处理bluetoothAdapter.startLeScan(mLeScanCallback);// 预先定义停止蓝牙扫描的时间(因为蓝牙扫描需要消耗较多的电量)new Handler().postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;bluetoothAdapter.stopLeScan(mLeScanCallback);}}, SCAN_PERIOD);} else {mScanning = false;bluetoothAdapter.stopLeScan(mLeScanCallback);}
}

扫描回调,回调之后得到 BluetoothDevice 的集合,可以放到列表中去

/*** 扫描回调*/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {if (bluetoothDevice.getName() != null) {if (!bluetoothDeviceArrayList.contains(bluetoothDevice)) {//去下重bluetoothDeviceArrayList.add(bluetoothDevice);}Log.e(TAG, "scan--" + bluetoothDevice.getName());}}
};

4.5.进行蓝牙连接

/*** 连接蓝牙 参数为目标设备
/
public void connectBle(BluetoothDevice bluetoothDevice) {mBluetoothDevice = bluetoothDevice;if (bluetoothDevice != null) {//第二个参数 是否重连mBluetoothGatt = bluetoothDevice.connectGatt(MainActivity.this, false, bluetoothGattCallback);}}

连接回调

  /*** 蓝牙连接成功回调*/private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {@Overridepublic void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyUpdate(gatt, txPhy, rxPhy, status);}@Overridepublic void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyRead(gatt, txPhy, rxPhy, status);}//不要执行耗时操作@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if (newState == BluetoothProfile.STATE_CONNECTED) {//连接成功Log.e(TAG, "onConnectionStateChange 蓝牙连接");//这里要执行以下方法,会在onServicesDiscovered这个方法中回调,如果在                        //onServicesDiscovered方法中回调成功,设备才真正连接起来,正常通信gatt.discoverServices();} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {Log.e(TAG, "onConnectionStateChange 蓝牙断连");if (mBluetoothDevice != null) {//关闭当前新的连接gatt.close();characteristic = null;}}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);//回调之后,设备之间才真正通信连接起来if (status == BluetoothGatt.GATT_SUCCESS) {Log.e(TAG, "onServicesDiscovered 蓝牙连接正常");BluetoothGattService service = gatt.getService(UUID.fromString(BleConstantValue.serverUuid));//uuid从硬件工程师获取characteristic = service.getCharacteristic(UUID.fromString(BleConstantValue.charaUuid));gatt.readCharacteristic(characteristic);//执行之后,会执行下面的                onCharacteristicRead的回调方法//设置通知,一般设备给手机发送数据,需要以下监听setCharacteristicNotification(characteristic, true);//耗时操作,如果有ui操作,需要用到handleradapterFreshHandler.sendEmptyMessage(0);} else {Log.e(TAG, "onServicesDiscovered 蓝牙连接失败");}}//这个方法一般用不到@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);Log.e(TAG, "callback characteristic read status " + status+ " in thread " + Thread.currentThread());if (status == BluetoothGatt.GATT_SUCCESS) {Log.e(TAG, "read value: " + characteristic.getValue());}}//这个方法是写入数据时的回调,可以和你写入的数据做对比@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);Log.e(TAG, "write value: " + FormatUtil.bytesToHexString(characteristic.getValue()));}//设备发出通知时会调用到该接口,蓝牙设备给手机发送数据,在这个方法接收@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);Log.e(TAG, "接收:" + FormatUtil.bytesToHexString(characteristic.getValue()));//byte[]转为16进制字符串bleWriteReceiveCallback();}};

/**
* 设置通知
/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {if (bluetoothAdapter == null || mBluetoothGatt == null) {return;}mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);}

参考写入指令

/*** 写入命令*/
private void write(byte[] cmd) {if (characteristic != null) {// 发出数据characteristic.setValue(cmd);if (mBluetoothGatt.writeCharacteristic(characteristic)) {Log.e(TAG, "写入成功");} else {Log.e(TAG, "写入失败");}} else {Toast.makeText(MainActivity.this, "蓝牙未连接", Toast.LENGTH_SHORT).show();}
}
发送数据
给设备发送数据,就是发送指令,文档上会写可以给设备发送指令,也就是功能,然后还会说明指令格式等等,指令最终也还是字节数组。 
如下代码,就是一条指令
    private byte[] getAllDataOrder() {
        Log.i(TAG, "获取全部数据指令");
        byte[] data = new byte[7];
        data[0] = (byte) 0x93;
        data[1] = (byte) 0x8e;
        data[2] = (byte) 0x04;
        data[3] = (byte) 0x00;
        data[4] = (byte) 0x08;
        data[5] = (byte) 0x05;
        data[6] = (byte) 0x11;
        return data;
    }
然后调用下面这2个方法写入指令,设备成功收到的话会触发onCharacteristicChanged回调,并收到数据。这里的characteristicWrite是我们前面拿到的那个。
    characteristicWrite.setValue(getAllDataOrder());
    gatt.writeCharacteristic(characteristicWrite);
--------------------- 

6.断开连接 释放资源

/*** 断开蓝牙设备*/
public void bleDisConnectDevice(BluetoothDevice device) {if (mBluetoothGatt != null) {mBluetoothGatt.disconnect();}
}/*** 释放资源 */private void releaseResource() {Log.e(TAG, "断开蓝牙连接,释放资源");if (mBluetoothGatt != null) {mBluetoothGatt.disconnect();mBluetoothGatt.close();}}

最后别忘了蓝牙广播:

 /*** 注册蓝牙监听广播*/private void registerBleListenerReceiver() {IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);registerReceiver(bleListenerReceiver, intentFilter);}
 /*** 蓝牙监听广播接受者*/private BroadcastReceiver bleListenerReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();//连接的设备信息BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);Log.e(TAG, "蓝牙广播" + action);if (mBluetoothDevice != null && mBluetoothDevice.equals(device)) {Log.e(TAG, "收到广播-->是当前连接的蓝牙设备");if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {Log.e(TAG,"广播 蓝牙已经连接");} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {Log.e(TAG,"广播 蓝牙断开连接");}} else {Log.e(TAG, "收到广播-->不是当前连接的蓝牙设备");}if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);switch (state) {case BluetoothAdapter.STATE_OFF:Log.e(TAG, "STATE_OFF 蓝牙关闭");adapter.clear();releaseResource();break;case BluetoothAdapter.STATE_TURNING_OFF:Log.e(TAG, "STATE_TURNING_OFF 蓝牙正在关闭");//停止蓝牙扫描scanLeDevice(false);break;case BluetoothAdapter.STATE_ON:Log.d(TAG, "STATE_ON 蓝牙开启");//扫描蓝牙设备scanLeDevice(true);break;case BluetoothAdapter.STATE_TURNING_ON:Log.e(TAG, "STATE_TURNING_ON 蓝牙正在开启");break;}}}};

所有完成,基本的蓝牙操作及通信功能,通信协议需要和蓝牙硬件厂商工程师获取。

3.ble

蓝牙开发小技巧

蓝牙4.0入门开发总结

1.有什么调试工具吗

nRF,网上下一个
https://jingyan.baidu.com/article/ab0b5630620287c15afa7df4.html

2.搜索出来很多蓝牙,怎么过滤掉不是我们的设备?

叫嵌入式那边改变蓝牙的名称,然后根据蓝牙名称过滤

3.链接ble设备,有时候一下子就能连接,有时候又链接不上这么办?

如果链接失败可以在代码里面重试链接,可以大大提高成功率

4.在for循环里面发包有时候设备会收不到怎么办

手机发太快设备处理不过来,所以发完一个包后要休眠50ms以上

5.ble只能一次发20个byte吗?能不能扩容?

可以扩容,除了手机端设置之外,还要嵌入式端修改代码,缺一不可
参考
https://blog.csdn.net/leconiot/article/details/76814107

应用地方:

1.摩拜

2.身份证

3.智能手环

4.温度计,

5.汗液仪,

6.心电图,

7.血压计等

蓝牙的选用

既然有经典蓝牙和低功耗蓝牙之分,我们在设计物联网产品和智能硬件产品的时候,如何选择呢?

经典蓝牙:蓝牙最初的设计意图,是打电话放音乐。3.0版本以下的蓝牙,都称为“经典蓝牙”。功耗高、传输数据量大、传输距离只有10米。

低功耗蓝牙:就是BLE,通常说的蓝牙4.0(及以上版本)。低功耗,数据量小,距离50米左右。

传声音的,用经典蓝牙:

如蓝牙耳机、蓝牙音箱。蓝牙设计的时候就是为了传声音的,所以是近距离的音频传输的不二选择。

电池供电、连手机APP的,用BLE:

如共享单车锁、蓝牙智能锁、蓝牙防丢器、蓝牙室内定位,是目前手机和智能硬件通信的性价比最高的手段。直线距离约50米,一节5号电池能用一年,传输模组成本10块钱,远比WIFI、4G等大数据量的通信协议更实用。

又要声音又要数据的,用双模蓝牙: 双模蓝牙,就是同时支持经典蓝牙音频和低功耗蓝牙。

如智能电视遥控器、降噪耳机等。很多智能电视配的遥控器带有语音识别,需要用经典蓝牙才能传输声音

传大数据量的,用经典蓝牙: 如某些工控场景,使用Android或Linux主控,外挂蓝牙遥控设备的,可以使用经典蓝牙里的SPP协议,当作一个无线串口使用。速度比BLE传输快多了。

远距离的,不用蓝牙。 固定供电的、不考虑功耗的、要传超过几十米距离的、要传高速数据的,这些都不适合蓝牙。远距离的可以用2G、4G、NB-IOT,大数据量的可以用WIFI。

---------------------

Android 蓝牙开发 BLE(低功耗) 摩拜单车共享汽车开门实例相关推荐

  1. Android 蓝牙开发(三) -- 低功耗蓝牙开发

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 前面已经学习了经典蓝牙开发,学习了蓝牙的配对连接和通信,又通 ...

  2. Android蓝牙开发 — 经典蓝牙BLE蓝牙

    一,前期基础知识储备 1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI): 支持移动电话.笔记本电脑.无线耳机等设备之间进行信息的交换: Android支持的蓝牙协议栈:Bl ...

  3. Android 蓝牙开发(一) -- 传统蓝牙聊天室

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 一.蓝牙概览 以下是蓝牙的介绍,来自维基百科: 蓝牙(英语: ...

  4. Android 蓝牙开发(二) --手机与蓝牙音箱配对,并播放音频

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 上一章中,我们已经学习了传统蓝牙的开发,这一章,我们来学习如 ...

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

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

  6. 摩拜单车小程序开发实践与框架分析

    以打造内容全.技术新.可实操的小程序小册为目标,整本小册共包含 21 节,不仅讲述了小程序开发的一些基础知识,也通过摩拜单车业务案例深入小程序开发,此外,还加入了主流框架的使用对比和深入的技术细节分析 ...

  7. Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,导航等)

    Android百度地图实例详解之仿摩拜单车APP(包括附近车辆.规划路径.行驶距离.行驶轨迹记录,导航等) 标签: android百度地图行驶轨迹记录共享单车行驶距离和时间 2017-03-08 20 ...

  8. Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,轨迹回放,导航等)

    转载请标明地址:http://blog.csdn.net/gaolei1201/article/details/60876811 2016一路有你,2017一起奋斗! 最近共享单车很火,动辄几亿美刀, ...

  9. Android蓝牙开发系列文章-玩转BLE开发(一)

    我们在<Android蓝牙开发系列文章-策划篇>中计划讲解一下蓝牙BLE,现在开始第一篇:Android蓝牙开发系列文章-玩转BLE开发(一).计划要写的BLE文章至少分四篇,其他三篇分别 ...

  10. Android模仿摩拜单车车型选择按钮

    个人感觉Tablayout里的indicator左右滑动和摩拜单车里按钮背景左右滑动很像,所以我的思路是修改Tablayout里indicator的位置和形状,把indicator的位置放在tab下面 ...

最新文章

  1. 【Netty】从 BIO、NIO 聊到 Netty
  2. iOS 数据计算带小数点导致数据不精确问题
  3. java css隔行变色_JS+CSS实现Li列表隔行换色效果的方法
  4. read/fread write/fwrite 的区别
  5. 麦克风增强软件_唱吧麦克风不会唱歌用它唱都好听,《向往的生活》同款麦克风...
  6. 当我们谈AI时,到底该谈什么?
  7. C宏的一个技巧:可变参数
  8. ionic4 组件的使用(二)
  9. Spring 创建Bean的三种方式
  10. 完全关闭App的两种做法
  11. 买书动态规划java_《编程之美》买书问题——动态规划
  12. inDesign教程,如何共享inDesign文件?
  13. 程序员应当注意的肢体语言
  14. iris数据集——决策树
  15. 2022年7月深圳地区CPDA数据分析师认证
  16. 魔百盒M301H-ZN代工-卡刷刷机固件
  17. 简要分析VB6.0和VB.NET区别
  18. 使命召唤手游显示服务器停服,使命召唤手游停服了吗 是手游还是端游
  19. 牛客真题编程——day16
  20. 微信小程序 + shiro 实现登录(安全管理) —— 保姆级教学

热门文章

  1. JavaScript数组every方法
  2. 单页面网站的优化方法大全
  3. 什么是软件危机?它有哪些典型表现?为什么会出现软件危机?
  4. 机动车尾气排放智能抓拍解决应用方案
  5. Python实例---抽屉热搜榜前端代码分析
  6. 【改进灰狼优化算法】贪婪的非分层灰狼优化算法(Matlab代码实现)
  7. 揭秘骗术:黑客人肉、查开房的灰色项目
  8. 掌握计算机基础知识的必要性,浅谈高校开展面向学科门类的计算机基础课程的必要性...
  9. 山东省计算机科学与技术排名,2016山东省大学各学科门类最佳专业排行榜|大学排行榜|最佳专业排行榜_新浪教育_新浪网...
  10. java 并g1_JVM G1详解