整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

低功耗蓝牙(BLE)和传感器的使用

、低功耗蓝牙的使用

Android中关于蓝牙的开发文档,可以参考Google提供的官方蓝牙文档:https://developer.android.google.cn/guide/topics/connectivity/bluetooth.html

在Android开发中,应用可通过官方提供的蓝牙API执行以下操作:

  • 扫描其他蓝牙设备
  • 查询本地蓝牙适配器的配对蓝牙设备
  • 建立 RFCOMM 通道
  • 通过服务发现连接到其他设备
  • 与其他设备进行双向数据传输
  • 管理多个连接

蓝牙

一个近距离无线通信技术,最早是由爱立信研发出来。蓝牙 Bluetooth 这个词是一个丹麦的国王的绰号,当时研发它的工程师正在看一个关于这个国王的书,就起了这个名字。蓝牙的技术特点是:

  • 近距离通信:典型距离是 10 米以内;
  • 传输效率:传输速度最高可达 24 Mbps
  • 多连接:蓝牙技术支持多设备连接、安全性高

蓝牙从被发明到目前,经过了几个版本的变化:

  • 1.0版本:99年发布
  • 2.1版本:使用范围最广泛,经典蓝牙
  • 3.0版本:高速蓝牙,最高传输速度达到24Mbps
  • 4.0/4.1版本:新增低功耗蓝牙
  • 5.0版本:物联网

低功耗蓝牙

低功耗蓝牙全称为Bluetooth Low Energy,简称为BLE,最大特点就是低功耗,另外低功耗蓝牙还具备成本低,连接速度快,安全性高的特点。当然,低功耗蓝牙也相应的会有一些不足,比如说:低功耗对应的是低传输效率,因此低功耗蓝牙主要用来传输少量数据,结合低功耗的特点,非常适合用在移动智能设备上。

低功耗蓝牙分为两种模式:单模和双模。

  • 单模:只能执行低功耗协议栈,即只支持BLE。
  • 双模:既支持传统蓝牙又支持BLE蓝牙。

注意:需要在Android 4.3及以上版本才能支持具备低功耗功能的蓝牙4.0。

BLE协议栈

首先来看一下使用蓝牙的基本流程:

先简单来了解一下低功耗蓝牙的协议框架,在BLE协议栈中,大致分为三个部分,从下到上依次为:控制器(Controller) 、主机(Host)、应用(Applications)。

  • 控制器:协议栈的最底层,直接与硬件相关,由厂商直接实现。
  • 主机:硬件层的抽象层,与具体的硬件和常见无关,可以理解为接口。
  • 应用层:使用Host层提供的API,进而开发的应用。

协议层从下往上,依次包含如下协议:

  • Attribute Protocol:简称为 ATT,属性协议,Host层的一个协议,是BLE通信的基础。ATT 把数据封装,向外暴露为“属性”,提供“属性”的为服务端,获取“属性”的为客户端。ATT 是专门为低功耗蓝牙设计的,结构非常简单,数据长度很短。每个属性都有一个唯一的UUID,属性以characteristics and services的形式传输。
  • Generic Attribute Profile:简称为GATT,通用属性配置文件,建立在前面说的 ATT 的基础上,对 ATT 进行进一步的逻辑封装,定义数据的交互方式和含义。GATT 按照层级定义了三个概念:
    • Service:服务,一个 Service 包含若干个 Characteristic。
    • Characteristic:特征,一个 Characteristic 可以包含若干 Descriptor。
    • Descriptor:描述,数据的读写操作。
  • Generic Access Profile:简称为GAP,通用访问控制配置文件,用来控制设备连接和广播,GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。

Android BLE API

  • Android SDK 中 BLE 相关的 API 都在 android.bluetooth.* 中。
  • 5.0版本中:android.bluetooth.le*

权限

  • 要在 APP 中使用蓝牙功能,需要在 Manifest 中申请蓝牙相关的权限。
  • Android 6.0及以上版本:蓝牙 + 定位权限。为什么会有定位权限?BLE有定位的功能和能力。

BLE核心API

  • BluetoothManager:蓝牙管理服务。在Android基本框架中可以发现蓝牙属于最底层的驱动模块中,可以公国context.getSystemService(Context.BLUETOOTH_SERVICE) 来进行获取。
  • BluetoothAdapter:本地设备蓝牙适配器。BluetoothAdapter可以完成:启动设备发现,查询已绑定(配对)设备的列表,使用已知MAC地址实例化 BluetoothDevice,并创建一个 BluetoothServerSocket 以监听来自其他设备的连接请求,并启动扫描蓝牙LE设备等操作,该类属于核心中的核心。
  • BluetoothAdapter.LeScanCallback:BLE扫描结果回调接口,在 Android 5.0以上 使用抽象类 ScanCallback。
  • BluetoothLeScanner:蓝牙LE设备执行扫描相关操作类,使用该API要求Android 5.0(API21)以上。
  • BluetoothDevice:远程蓝牙设备,BluetoothDevice允许创建与相应设备的连接或关于它的查询信息,例如名称,地址,类和绑定状态等。
  • BluetoothProfile:配置文件代理。每个公共配置文件实现这个接口。它有几个直接子类,每个子类再不同场景中使用,如 BluetoothA2dp, BluetoothGatt, BluetoothGattServer, BluetoothHeadset, BluetoothHealth。在当前例子中使用到的是 BluetoothGatt。
  • BluetoothGatt:提供蓝牙GATT功能,以实现与蓝牙智能或智能就绪设备的通信。使用该类做连接、断开、关闭等操作。
  • BluetoothGattCallback:设备连接时的回调接口。

UUID

UUID 是全局唯一标识,是128bit的值,为了便于识别和阅读,一般标示成:8-4-4-12 的16进制格式。

Android 中提供了 UUID.randomUUID() 来生成一个随机的 UUID。

在低功耗蓝牙中,长度为128bit的UUID数据长度是受限的,因此蓝牙中又产生出来了16bit和32bit的UUID,本质上和128bit的UUID一样。

Android 中BLE的操作步骤

  • ① 获取到BluetoothAdapter:代表设备自己的蓝牙适配器,整个系统只有一个蓝牙适配器,应用程序可以使用此对象与其交互。获取方法是通过 BluetoothManager 获取。
  • ② 启用蓝牙设备:isEnable() 查看是否启用,通过 BluetoothAdapter.ACTION_REQUEST_ENABLE 来启动
  • ③ 查找BLE设备:通过startLeScan(callBack); 方法来开启扫描;另外还可以使用BluetoothLeScanner来扫描。

与Android BLE设备通信

开发BLE应用,主要有两大类:

  • 基于非连接的通信应用:使用的是BLE的广播机制,又称作是Beacon。共有两个角色:发送方负责发送广播,称之为Boradcaster,另外一方为监听方,监听广播信号,称之为Observer。
  • 基于连接的通信应用:基于连接的通信是通过建立GATT连接,让后进行数据的收发。也有两个角色:连接发起方,称之为中心设备(Central),另外一方是被连接的设备,称之为外设Peripheral。

本篇文章中,我们来讨论面向连接的通信的情况。如果要与另外一个BLE设备进行通信,需要经过连接,确认状态,然后再通信的过程。首先是开启连接,然后会触发对应的连接回调,然后发现服务,触发发现服务回调,获取服务内部的特征值,对其读写命令(和 BLE 共同约束的规范),就是这么一个过程,比较简单。

  • 连接到GATT服务器:如果要与BLE进行通信,第一步就是要连接到该设备的 GATT 服务。使用connectGatt方法链接,有三个参数,最后一个参数为连接的回调函数,表是处理链接的状态,所有交互均从回调中进行处理。
  • 读 BLE 属性:一旦Android设备连接到GATT的服务器并且发现了BLE服务,则可以读取或者写入相关的属性,执行相关的操作了。
  • 释放GATT:当操作完成后,要记得关闭设备bluetoothGatt.close()。

二、WIFI的使用

每个移动智能设备几乎都带有WIFI连接功能,在Android系统中,同样也提供了WIFI开发的相关的API。

WIFI API

Android系统提供的WIFI API,主要包含在两个包中:

  • android.net.wifi包
  • android.net.wifi.p2p包

和wifi相关的核心API主要有以下几个内容:

  • WifiManager:提供管理WiFI连接的大部分API
  • ScanResult:已经检测出的接入点(包括接入点的地址、名称、身份认证、频率、信号强度)
  • WifiConfiguration:WiFi连接的网络配置(包括安全配置等)
  • WifiInfo:WiFi无线连接的描述,主要包括接入点、网络连接状态、隐藏的接入点、IP地址、连接速度、MAC地址、网络ID、信号强度等等信息。
  • WifiManager.WifiLock:通常情况下当用户在一段时间内没有任何操作的时候,WiFi网络会自动关闭。我们使用WifiLock来锁定WiFi网络,使其一直保持连接,直到这个锁被释放。多个应用程序可能有多个锁,当多有的应用程序的锁都被释放的时候,WiFi才被关闭。

WIFI 使用说明

  • WifiManager:context.getSystemService(Context.WIFI_SERVICE);
  • 打开WiFi:mWifiManager.setWifiEnabled(true); 关闭wifi为false
  • 创建一个WifiLock:mWifiManager.createWifiLock("lock_name");
  • 锁定WifiLock:mWifiLock.acquire();
  • 释放WifiLock:mWifiLock.release();
  • WiFi连接:
public void addNetworkAndConn(WifiConfiguration wcg) {
    int netId = mWifiManager.addNetwork(wcg);
    mWifiManager.enableNetwork(netId, true);
}
  • 断开一个指定ID的WiFi:
public void disconnectWifi(int netId) {
    mWifiManager.disableNetwork(netId);
    mWifiManager.disconnect();
}
  • 扫描可接入的WiFi:
public void startScan() {
    mWifiManager.startScan();
    // 得到扫描结果
    List<ScanResult> wifiList = mWifiManager.getScanResults();
    // 得到配置好的网络连接
    List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
}

WIFI 权限

在进行wifi开发时,既要用到网络,也要用到硬件资源,因此需要申请一些必要的权限,而且涉及到的还比较的多,主要的权限如下:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> 
<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"></uses-permission> 
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission> 
<uses-permission android:name="android.permission.INTERNET"></uses-permission> 
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> 
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"></uses-permission>

WIFI直连

WIFI Direct 意为通过 WIFI 直接建立连接。允许无线网络中的设备无须通过无线路由器即可相互连接。这种标准支持 WIFI 的无线设备像蓝牙那样以点对点的形式互连,但是在传输速度与传输距离方面都比蓝牙有大幅提升。

WIFI Direct 提供 WifiP2pManager 类,其功能主要分为以下三部分:

  • WifiP2pManager 类提供相关 API 用于发现可连接的点,并进行请求和建立连接。
  • 每个 WifiP2pManager 的方法都要求传入对应的监听器,用于监听对该方法是否成功运行。
  • 当检测到特定事件,如可连接的点减少或者发现了新的可连接的点,WIFI Direct 框架会通过 Intent 通知用户。

WifiP2pManager的核心API用法说明如下所示:

  • initialize:为应用程序注册 WIFI 框架。该方法必须在任何其他 WIFI Direct 方法被调用前调用,常放在Application中调用。
  • connect:与具有指定配置的 WIFI 设备建立点对点连接
  • cancelConnect:断开连接
  • requestConnectInfo:获取设备的连接信息
  • createGroup:以当前设备为拥有者创建一个点对点组
  • removeGroup:删除当前的点对点组
  • requestGroupInfo:获取点对点组的信息
  • discoverPeers:初始化发现对等点设备服务
  • requestPeers:获取当前已发现的对等点设备列表

在WifiP2pManager使用时,同样支持使用各种监听回调接口:

  • ActionListener:Wifi连接过程中的某个动作监听。主要包括:connect、cancelConnect、createGroup、removeGroup、discoverPeers等回调函数
  • ChannelListener:initialize初始化的回调
  • ConnectionInfoListener:请求连接的回调,回传连接信息
  • GroupInfoListener:点对点组的监听信息
  • PeerListListener:点对点设备的监听回调接口

三、传感器

Android 传感器属于虚拟设备,可提供来自以下各种物理传感器的数据:

  • 加速度计
  • 陀螺仪
  • 磁力计、
  • 气压计
  • 湿度传感器
  • 压力传感器
  • 光传感器
  • 近程传感器
  • 心率传感器

以上的这些均可以归纳为传感器类别,在Android中,这些传感器有一个相同的定义文件,存在一个 sensors.h文件,其中定义了Android系统支持的每一种传感器。格式为:ENSOR_TYPE_传感器名称。


该图为Android系统中传感器的的架构和分层。可以看出,几乎和Android系统整体的架构一样。从上层到下层,从应用层到底层内核层。

传奇器核心API

Android传感器框架放在android.hardware包中,核心的API如下所示:

  • SensorManager:用于创建传感器服务实例。该类提供了访问和罗列传感器的各种方法,用于注册和注销传感器事件监听器并获取方向信息。该类也提供了几个常量,用于报告传感器的精度、数据获取率和校正传感器。
  • Sensor:用作创建某个特定传感器的实例。该类提供了用于确定传感器能力的各种方法。
  • SensorEvent:创建传感器事件对象。传感器事件对象包含传感器事件的相关信息,包括原始的传感器数据、传感器类型、产生的事件、事件精度以及事件发生的时间戳等。
  • SensorEventListener:是一个接口,包含两个回调方法。当传感器的值发生改变或者传感器的精度发生改变时,相关方法就会自动被调用。

传感器核心操作

无论如何变化,其实通过上面的描述和介绍,我们看到,传感器是底层系统提供的,数据也是相关的API返回获取的。因此,在涉及到传感器开发时,开发者的核心操作只有两个:

  • 分析需求,明确使用哪个一个类别的传感器,明确要获取的传感器数据。
  • 调用系统的API方法,监听传感器的的回调时间,获取数据。

因此,Android中的传感器部分的应用开发,重点不是在于传感器的使用,是开发者自己特定的应用,在获取到数据后,对数据的处理和挖掘,是重中之重。

Android传感器分类

Android中支持的传感器分为很多类别,主要有:

  • TYPE_ACCELEROMETER:运动探测传感器,硬件传感器
  • TYPE_AMBIENT_TEMPERATURE:环境温度传感器,硬件传感器
  • TYPE_GRAVITY:运动探测,软件或者硬件传感器
  • TYPE_GYROSCOPE:旋转,硬件传感器
  • TYPE_LIGHT:屏幕亮度传感器,属于硬件传感器
  • TYPE_LINEAR_ACCELERATION:加速度传感器,软件或者硬件都有
  • TYPE_ORIENTATION:方向传感器,属于软件传感器
  • TYPE_PRESSURE:空气压力传感器,属于硬件传感器
  • TYPE_PROXIMITY:距离传感器,用于监测打电话时手机与耳朵的距离,属于硬件传感器

四、SystemService

经过本篇文档的介绍,结合之前的课程内容,我们可以总结出一个规律。在Android开发时,很多情况下我们都可以直接通过某个上下文,获取xxxManager,往往是某个管理者。这些管理者是Android系统提供的系统服务,我们可以统称为SystemService,现在我们了解一下SystemService有关的内容,并做个总结。

SystemService

SystemService是系统提供给开发者的调用系统层的控制接口,应用层的开发者只需要了解这些接口的使用方式,就可以非常方便的进行系统控制,完成自己想要的功能操作,获取系统的相关信息,而不需要了解接口的具体内部实现方式。这些SystemManager是在framework层或者更底层进行实现的。

相反的对于Framework层的开发者而言,需要了解XXXManager服务的实现细节和方式,并维护Manager接口,扩展或者实现新接口等。

我们可以列举一下我们在学习过程中遇到的Manager,比如:

  • WindowManager:窗口操作的窗口管理服务
  • NotifacationManager:通知管理
  • AudioManager:Android系统的音频管理者
  • LocationManager:GPS定位服务管理
  • StatusBarManager:状态栏的管理者

除此以外,还有很多很多,以上这些管理者,其实背后都是有一个系统服务SystemService。

getSystemService

getSystemService是Android很重要的一个API,它是Activity的一个方法,根据传入的NAME来取得对应的Object,然后转换成相应的服务对象。

款指纹模块

支持APP远程控制


HLK-EL605A是一款蓝牙半导体指纹锁解决方案,与市面上已有的产品相比,该模组具有功耗低、识别速度快、识别准确度高等优势。


HLK-EL605A使用方便,尤其适合室内房门锁等体积较小、使用电池供电的设备中,低功耗的同时可以保持优异的反应性能及高速的识别速度。


HLK-EL605A模块使用05mm*8Pin FPC插座与扩展板连接可以使用普通线缆进行连接,增强连接可靠性。


HLK-EL605A的资料链接:

https://h.hlktech.com/Mobile/download/fdetail/237.html


一、指纹+APP两种解锁方式


HLK-EL605A指纹模块支持BLE5.1蓝牙通讯,在日常使用中支持指纹识别解锁和APP远程控制解锁两种解锁方式。


01

指纹极速解锁




HLK-EL605A指纹模块采用电容式指纹传感器,通过测量指纹信号,可以有效检测假手指问题。指纹传感器表面使用高硬度涂层,在日常使用中,可以极大的减少对指纹传感器的磨损。


0.3S内极速解锁,指纹就是钥匙,开门快人一步,非常适用于智能门锁、考勤门锁等产品。


算法规格:

① 认假率FAR(FalseAcceptanceRate):<1/100000

② 拒真率 FRR(False Rejection Rate): <1%

③ 响应速度(平均):特征提取时间<0.25s,单枚平均匹配时间<0.002s

④ 指纹存储:支持存储20枚指纹特征

⑤ 支持指纹拼接,拼接最大次数:6次

⑥ 支持360°识别

⑦ 支持自学习功能


02

APP远程控制

HLK-EL605A指纹模块支持通过涂鸦APP远程控制。在通过蓝牙添加指纹设备后,可以实现APP对指纹模块的智能管理。


涂鸦APP智能控制指纹模块功能:

① 剩余电量实时显示

② 查看开锁记录和警告信息

③ 快速添加或者删除指纹

④ 远程开锁


除了上述功能外,用户可根据需求开发更多功能,提高产品的实用性。


二、BLE5.1蓝牙通讯

HLK-EL605A指纹模块支持存储20枚指纹特征; 支持指纹拼接,拼接最大次数6次。

同时,HLK-EL605A指纹模块支持BLE5.1蓝牙通讯,可以通过蓝牙连接小程序和APP控制模块,支持定制开发小程序和APP。



三、支持电机、蜂鸣器等控制

HLK-EL605A指纹模块支持 3.4V~6V供电;支持1路电机芯片正反转控制支持1路电机堵转检测;支持1路蜂鸣器控制;支持1路按键检测;支持1路可扩展输入/输出IO。



四、自学习功能

当手指小面积接触到采集器时,系统自动激活并采集对比指纹图像及特征点信息。

指纹识别过程中,新提取的指纹特征值识别成功后将该特征值融合到指纹数据库中。要次用户成功解锁手机后,指纹传感器就会记录之前尚未录入的指纹部分,然后将该区域添加到数据中,使指纹数据更完整。随着时间的推移,数据库中关于指纹的信息会更多,解锁会更快。

ndroid ble蓝牙开发

BLE介绍

安卓4.3(API 18)为BLE的核心功能提供平台支持和API,App可以利用它来发现设备、查询服务和读写特性。相比传统的蓝牙,BLE更显著的特点是低功耗。这一优点使AndroidApp可以与具有低功耗要求的BLE设备通信,如近距离传感器、心脏速率监视器、健身设备等。

BLE开发

BLE权限添加

为了在app中使用蓝牙功能,必须声明蓝牙权限BLUETOOTH。利用这个权限去执行蓝牙通信,例如请求连接、接受连接、和传输数据。如果想让你的app启动设备发现或操纵蓝牙设置,必须声明BLUETOOTH_ADMIN权限。注意:如果你使用BLUETOOTH_ADMIN权限,你也必须声明BLUETOOTH权限。在你的app manifest文件中声明蓝牙权限。

设置BLE

你的app能与BLE通信之前,你需要确认设备是否支持BLE,如果支持,确认已经启用。虽然现在的手机基本都支持BLE,但是考虑到程序的健硕性,如果设置为false,这个检查是必需的。

BluetoothAdapter类介绍

获取:所有的蓝牙活动都需要蓝牙适配器。BluetoothAdapter代表设备本身的蓝牙适配器(蓝牙无线)。整个系统只有一个蓝牙适配器,而且你的app使用它与系统交互。下面的代码片段显示了如何得到适配器。注意该方法使用getSystemService()]返回BluetoothManager,然后将其用于获取适配器的一个实例。Android 4.3(API 18)引入BluetoothManager。

// 初始化蓝牙适配器

final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();

有了mBluetoothAdapter之后就可以判断当前蓝牙开关状态、蓝牙未开启情况下代码里面自动开启蓝牙、以及扫描周边的ble设备

开启蓝牙

接下来,你需要确认蓝牙是否开启。调用isEnabled())去检测蓝牙当前是否开启。如果该方法返回false,蓝牙被禁用。下面的代码检查蓝牙是否开启,如果没有开启,可以提示用户去设置开启蓝牙。

// 确保蓝牙在设备上可以开启
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
//蓝牙未开启
}

发现BLE设备

为了发现BLE设备,使用startLeScan())方法。这个方法需要一个参数BluetoothAdapter.LeScanCallback。你必须实现它的回调函数,那就是返回的扫描结果。因为扫描非常消耗电量,你应当遵守以下准则:

1·只要找到所需的设备,停止扫描。

2·不要在循环里扫描,并且对扫描设置时间限制。以前可用的设备可能已经移出范围,继续扫描消耗电池电量。

以下代码显示如何扫描设备和停止扫描设备

// 10秒后停止寻找.
private static final long SCAN_PERIOD = 10000;
private void scanLeDevice(final boolean enable) {
if (enable) {
// 经过预定扫描期后停止扫描
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
...
}
如果你只想扫描指定类型的外围设备,可以改为调用startLeScan(UUID[], BluetoothAdapter.LeScanCallback)),需要提供你的app支持的GATT services的UUID对象数组。

扫描的信息在LeScallCallback里面返回

private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
//device 里面包含设备的mac地址和设备的名称
//scanRecord里面就是ble设备发出的广播包数据
//rssi表示ble设备的信号值,该值为负数,值越大表示信号值越好
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};

连接到GATT服务端

与一个BLE设备交互的第一步就是连接它——更具体的,连接到BLE设备上的GATT服务端。为了连接到BLE设备上的GATT服务端,需要使用connectGatt( )方法。这个方法需要三个参数:一个Context对象,自动连接(boolean值,表示只要BLE设备可用是否自动连接到它),和BluetoothGattCallback调用。

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

连接到GATT服务端时,由BLE设备做主机,并返回一个BluetoothGatt实例,然后你可以使用这个实例来进行GATT客户端操作。请求方(Android app)是GATT客户端。BluetoothGattCallback用于传递结果给用户,例如连接状态,以及任何进一步GATT客户端操作。

private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {//当连接状态发生改变
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {//当蓝牙设备已经连接
//获取ble设备上面的服务
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//当设备无法连接
}
}
@Override
//调用discoverServices后的回调
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//获取服务成功
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// 读写特性
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
}
}
...
};
...
}

发送数据

首先通过UUID拿到对应的服务,再通过UUID拿到服务的特征,设置特征的属性是BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE。设置成功后可以在该特征值上发送数据到ble设备和接收ble设备的数据。看到这里也许各位不熟ble开发和刚ble开发的看官也许就一脸懵逼,我只是想发送数据到ble设备,怎么一下子搞出个UUID 服务和特征值了,难道就不能和B/S开发一样,连接之后我把数据发送到一个接口,服务器端就返回我需要的数据那么简单。这还得从ble蓝牙的架构说起。

BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor,一个Descriptor包含一个Value。service是characteristic的集合.一个characteristic包括一个单一变量和0-n个用来描述characteristic变量的descriptor.Descriptor用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的测量单位。一般来说,Characteristic是手机与BLE终端交换数据的关键.。

举个栗子:当我们想要用手机与BLE设备进行通信时,实际上也就相当于我们要去找一个学生交流,首先我们需要搭建一个管道,也就是我们需要先获取得到一个BluetoothGatt,其次我们需要知道这个学生在哪一个班级,学号是什么,这也就是我们所说的serviceUUID,和charUUID。这里我们还需要注意一下,找到这个学生后并不是直接和他交流,他就好像一个中介一样,在手机和BLE终端设备之间帮助这两者传递着信息,我们手机所发数据要先经过他,在由他传递到BLE设备上,而BLE设备上的返回信息,也是先传递到他那边,然后手机再从他那边进行读取。

在发送数据之前需先设置特征的具有notificaion功能

private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean enabled;
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
mBluetoothGatt.writeDescriptor(descriptor);
设置完成后回调
@Override
public final void onDescriptorWrite(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
//设置成功
if (status == BluetoothGatt.GATT_SUCCESS) {
}
}

设置成功后就开始发送数据了。

//将指令放置进特征中

characteristic.setValue(data);
//设置回复形式characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);

//开始写数据

mBluetoothGatt.writeCharacteristic(chharacteristic);

写入数据成功后回调

protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {

//发送数据成功啦啦啦

}

如何设备回复数据则会回调

@Override
public final void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
}

关闭客户端App

当你的app完成BLE设备的使用后,应该调用close( )),系统可以合理释放占用资源。

public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}

最后分享我在BLE 开发中遇到的坑和一些经验

1 在所有蓝牙的回调中不要操作UI。我是不会告诉你我是怎么发现这个坑的。

2 在所有的蓝牙回调中不要执行耗时操作。

3 发送数据要等到上一条数据发送成功后再发下一条数据,毕竟BLE设备运算没有手机快,这里可以推荐一个开源蓝牙连接工具https://github.com/NordicSemiconductor/Android-nRF-Toolbox,里面非常好的对发送的数据做了一个数据队列。

4 合理的控制扫描过程,一般出现133错误的时候重连就可以先去扫描再去连接。若扫描不到时不要马上又去扫描,不然你把手机放那一夜,把设备远离它,第二天回来看手机时会惊喜的发现手机没电自动关机了

遇到的坑

1 断线重连的时候总是报133错误,

断线后不要马上去连接.先扫描设备,扫描到设备后再去连接。

2 扫描不到设备

手动关闭蓝牙再打开蓝牙开关。这个可能是重连里面的扫描引起的,如果设备未在周边,一直去扫描的话,后来设备在身边也可能扫描不到设备。如果未能连接设备,也不能一直去扫描。扫描不到设备时说明设备并不到周边,可以延迟多少时间后再去扫描

3 连接设备后发送数据,发送数据的回调函数也已经走了。没有接收到数据

查看设置特征值的描述值

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
mBluetoothGatt.writeDescriptor(descriptor)的回调里面是不是回调成功了

4反复断开蓝牙后再重连导致连接失败

断开蓝牙后应该调用close()方法释放资源.连接时应该设置超时,在超时时间内继续去连接,基本低、中、高端机都能重新连接上。

5 连接上之后自动断开连接,重连上之后又自动断开连接,如此反复。

我们的BLE设备在某些低端机会遇到这种问题。听固件工程师说是BLE设备蓝牙芯片频率和手机蓝牙频率问题,需调BLE设备频率。遇到这种问题APP就束手无策了。

6 反复操作断开和连接导致系统蓝牙挂掉(无响应)

基本也是没有合理释放资源导致

7 调用扫描操作导致APP无响应

查看系统蓝牙是否挂掉了。基本和问题6类似

最后附上一个Nordic 公司开源的Android蓝牙开发封装好的库地址

https://github.com/NordicSemiconductor/Android-BLE-Library

参考:http://www.cnblogs.com/cxk1995/p/5693979.html