本文是对已实现的蓝牙防丢器项目的总结,阐述蓝牙防丢器的原理、实现与android客户端的蓝牙BLE接口编程。在这里重点关注如何利用BLE接口来进行工程实现,对于BLE的协议、涉及到JNI的BLE接口内部源码实现,笔者以后再详细剖析。但要求读者对BLE协议有一定的认识,如GAP、GATTprofile在BLE中的角色和作用,如何使用Service、Characteristic等。 >一、蓝牙防丢器原理和产品需求 蓝牙防丢器的核心原理是根据接收到的蓝牙设备端的无线信号强度(RSSI)来估算距离。其计算公式是: ![](https://box.kancloud.cn/2016-01-12_5694d4a3b7935.jpg) d是计算距离,RSSI是信号强度,A为发射端和接收端相隔1米时的信号强度,n是环境衰减因子。对于不同的蓝牙设备该值是不一样的,同样的设备在不同的发射功率的情况下其信号强度也是不一样的,而且对于同是1米的情况下,环境对于信号强度也是有影响的。n是环境衰减因子,自然跟环境有关。所以在确切发射功率的情况下,A和n对于同一款设备来说,也是一个经验值。 在实际的防丢器产品中,一般有以下功能: 1\. 当手机(接收端)检测到发射端设备的距离超过一定距离时,发出告警提示,设备根据告警级别进行相应的指示,如发出不同频率的音频或者闪灯。 2\. 当发射设备端发现和手机端建立的链路断开(意味着距离已经超过连接范围)时,其会自动发出某种形式的警告。 >二、蓝牙防丢profile 笔者以业界目前功耗最低的蓝牙单芯片(Dialog公司的DA14580)来说明。针对DA14580,Dialog公司有提供开发SDK(以后会对该SDK框架进行分析,以指导开发),其中就有实现防丢profile,命名是Proximity。     该profile针对以上防丢的功能提供的Characteristic如下: 1.TXP(txpower) Characteristic, 设备端需要通过主机控制接口HCI来获得发射功率参数,并以read属性提供给master。 2.IAS(**immediate alter service**), write属性,供master写告警级别。当master写入新的值时,设备端会收到write的回调,其根据告警级别进行相应告警。 3\. LLS(link loss service),write/read属性,供master设置链路断开情况下默认的告警级别。 RSSI通过接收端的接口来获得,并不需要设备端提供service。 以上Characteristic都通过GATT profile提供服务,在蓝牙通信协议上,每个Characteristic都会对应一个UUID。 >三、android蓝牙BLE接口编程 androidBLE接口在android4.3版本以上提供。 **1.判断当前系统是否支持BLE** getPackageManager().hasSystemFeature(PackageManager.*FEATURE_BLUETOOTH_LE*) 返回真表示支持。 **2.获得蓝牙适配器类** 用户通过统一的蓝牙适配器类BluetoothAdapter来使用BLE API。 先获得蓝牙管理器: BluetoothManagerbluetoothManager = getSystemService(Context.*BLUETOOTH_SERVICE*); 再获得蓝牙适配器实例(单体对象): BluetoothAdaptermBluetoothAdapter = bluetoothManager.getAdapter(); **3.启动手机蓝牙硬件功能(相当于在设置界面开启蓝牙功能)** mBluetoothAdapter.enable(); **4.开始扫描** BluetoothAdapter.startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallbackcallback) callback是当扫描到蓝牙设备时的回调接口。实现callback中的onLeScan接口: @Override **public void** onLeScan(**final**BluetoothDevice device, **int** rssi, **byte**[] scanRecord) 其中,device代表扫描到的设备,可以获得其MAC地址、设备名等等;rssi即信号强度,这是未连接时获取RSSI的方法;scanRecord代表扫描设备得到的响应参数,ibeacon即通过该参数来获得广播内容。 假设String bluetoothAddress = device.getAddress(),获取蓝牙48位MAC地址 **5.连接GATT,获取设备端的UUID服务,并进行数据通信交互** 通过MAC地址获得代表设备端的蓝牙设备类 BluetoothDevicedevice = mBluetoothAdapter.getRemoteDevice(bluetoothAddress); 连接GATT BluetoothGatt mBluetoothGatt = device.connectGatt(android.content.Context context, booleanautoConnect, android.bluetooth.BluetoothGattCallback callback); Callback是连接GATT之后,所有数据交互的回调入口。分别包括: 1)设备服务发现 @Override **publicvoid**onServicesDiscovered(BluetoothGatt gatt, **int** status) mBluetoothGatt.getServices()代表设备服务集合, **for** (BluetoothGattService gattService : mBluetoothGatt.getServices()) 对于每个服务service,用getUuid()可以获得服务的UUID,getCharacteristics()代表该服务的Characteristic集合。 **for**(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) 对于每个Characteristic,getUuid()获得UUID,getPermissions()获得属性权限,getValue()获得属性值。 在该回调中我们只提取感兴趣的三个Characteristic的UUID,对于其他的如电池、设备服务等UUID可以不管。 *gattCharacteristic_char5_TXP=*gattCharacteristic; 2)连接状态改变 @Override **public void**onConnectionStateChange(BluetoothGatt gatt, **int** status,**int**newState) 有两种状态,BluetoothProfile.*STATE_CONNECTED*代表连接,BluetoothProfile.*STATE_DISCONNECTED*代表断开连接。 3)读回调 @Override **public void**onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristiccharacteristic, **int**status) 其对应手机端发出读请求后,当收到设备端的数据时的回调。如 mBluetoothGatt.readCharacteristic(*gattCharacteristic_char5_TXP*) 4)设备端数据变化回调 这里对应设备的characteristic的属性是notify或者indication,即相当手机端订阅这个characteristic的值变更服务,当设备端的characteristic发生变化时,设备端会主动发出通知给手机端。 @Override **public void** onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristiccharacteristic) 在回调中获得新的值characteristic.getValue()。 5)获取到RSSI值的回调 RSSI在扫描时可以通过扫描回调接口获得,但是在连接之后要不断地使用 mBluetoothGatt.readRemoteRssi()向底层驱动发出读取RSSI请求,当底层获取到新的RSSI后会进行以下回调: @Override **public void**onReadRemoteRssi(BluetoothGatt gatt, **int** rssi, **int** status) rssi即是新的信号强度值。 连接后,由于手机和设备端的距离在发生变化,因此要不断地读取RSSI,实时计算两者之间的距离才能保证防丢功能的实现。 Android 4.3之后的SDK有提供BLE接口使用的样例,可以通过研读源码进行理解。对防丢器的蓝牙设备端和android客户端的开发感兴趣者可以关注 微信公众号:嵌入式企鹅圈 进行交流。谢谢!嵌入式企鹅圈分享嵌入式Linux和物联网原创技术经验,敬请关注: ![](https://box.kancloud.cn/2016-01-12_5694d4a143a64.jpg)