、低功耗蓝牙的使用
Android中关于蓝牙的开发文档,可以参考Google提供的官方蓝牙文档:https://developer.android.google.cn/guide/topics/connectivity/bluetooth.html
在Android开发中,应用可通过官方提供的蓝牙API执行以下操作:
一个近距离无线通信技术,最早是由爱立信研发出来。蓝牙 Bluetooth 这个词是一个丹麦的国王的绰号,当时研发它的工程师正在看一个关于这个国王的书,就起了这个名字。蓝牙的技术特点是:
蓝牙从被发明到目前,经过了几个版本的变化:
低功耗蓝牙全称为Bluetooth Low Energy,简称为BLE,最大特点就是低功耗,另外低功耗蓝牙还具备成本低,连接速度快,安全性高的特点。当然,低功耗蓝牙也相应的会有一些不足,比如说:低功耗对应的是低传输效率,因此低功耗蓝牙主要用来传输少量数据,结合低功耗的特点,非常适合用在移动智能设备上。
低功耗蓝牙分为两种模式:单模和双模。
注意:需要在Android 4.3及以上版本才能支持具备低功耗功能的蓝牙4.0。
首先来看一下使用蓝牙的基本流程:
先简单来了解一下低功耗蓝牙的协议框架,在BLE协议栈中,大致分为三个部分,从下到上依次为:控制器(Controller) 、主机(Host)、应用(Applications)。
协议层从下往上,依次包含如下协议:
UUID 是全局唯一标识,是128bit的值,为了便于识别和阅读,一般标示成:8-4-4-12 的16进制格式。
Android 中提供了 UUID.randomUUID() 来生成一个随机的 UUID。
在低功耗蓝牙中,长度为128bit的UUID数据长度是受限的,因此蓝牙中又产生出来了16bit和32bit的UUID,本质上和128bit的UUID一样。
开发BLE应用,主要有两大类:
本篇文章中,我们来讨论面向连接的通信的情况。如果要与另外一个BLE设备进行通信,需要经过连接,确认状态,然后再通信的过程。首先是开启连接,然后会触发对应的连接回调,然后发现服务,触发发现服务回调,获取服务内部的特征值,对其读写命令(和 BLE 共同约束的规范),就是这么一个过程,比较简单。
每个移动智能设备几乎都带有WIFI连接功能,在Android系统中,同样也提供了WIFI开发的相关的API。
Android系统提供的WIFI API,主要包含在两个包中:
和wifi相关的核心API主要有以下几个内容:
public void addNetworkAndConn(WifiConfiguration wcg) {
int netId = mWifiManager.addNetwork(wcg);
mWifiManager.enableNetwork(netId, true);
}
public void disconnectWifi(int netId) {
mWifiManager.disableNetwork(netId);
mWifiManager.disconnect();
}
public void startScan() {
mWifiManager.startScan();
// 得到扫描结果
List<ScanResult> wifiList = mWifiManager.getScanResults();
// 得到配置好的网络连接
List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
}
在进行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 Direct 意为通过 WIFI 直接建立连接。允许无线网络中的设备无须通过无线路由器即可相互连接。这种标准支持 WIFI 的无线设备像蓝牙那样以点对点的形式互连,但是在传输速度与传输距离方面都比蓝牙有大幅提升。
WIFI Direct 提供 WifiP2pManager 类,其功能主要分为以下三部分:
WifiP2pManager的核心API用法说明如下所示:
在WifiP2pManager使用时,同样支持使用各种监听回调接口:
Android 传感器属于虚拟设备,可提供来自以下各种物理传感器的数据:
以上的这些均可以归纳为传感器类别,在Android中,这些传感器有一个相同的定义文件,存在一个 sensors.h文件,其中定义了Android系统支持的每一种传感器。格式为:ENSOR_TYPE_传感器名称。
该图为Android系统中传感器的的架构和分层。可以看出,几乎和Android系统整体的架构一样。从上层到下层,从应用层到底层内核层。
Android传感器框架放在android.hardware包中,核心的API如下所示:
无论如何变化,其实通过上面的描述和介绍,我们看到,传感器是底层系统提供的,数据也是相关的API返回获取的。因此,在涉及到传感器开发时,开发者的核心操作只有两个:
因此,Android中的传感器部分的应用开发,重点不是在于传感器的使用,是开发者自己特定的应用,在获取到数据后,对数据的处理和挖掘,是重中之重。
Android中支持的传感器分为很多类别,主要有:
经过本篇文档的介绍,结合之前的课程内容,我们可以总结出一个规律。在Android开发时,很多情况下我们都可以直接通过某个上下文,获取xxxManager,往往是某个管理者。这些管理者是Android系统提供的系统服务,我们可以统称为SystemService,现在我们了解一下SystemService有关的内容,并做个总结。
SystemService是系统提供给开发者的调用系统层的控制接口,应用层的开发者只需要了解这些接口的使用方式,就可以非常方便的进行系统控制,完成自己想要的功能操作,获取系统的相关信息,而不需要了解接口的具体内部实现方式。这些SystemManager是在framework层或者更底层进行实现的。
相反的对于Framework层的开发者而言,需要了解XXXManager服务的实现细节和方式,并维护Manager接口,扩展或者实现新接口等。
我们可以列举一下我们在学习过程中遇到的Manager,比如:
除此以外,还有很多很多,以上这些管理者,其实背后都是有一个系统服务SystemService。
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
*请认真填写需求信息,我们会在24小时内与您取得联系。