您的位置:首页 > 产品设计 > UI/UE

Android5.0(Lollipop) 蓝牙BLE(Bluetooth Low Energy)

2016-07-07 00:28 495 查看
一、蓝牙BLE(Bluetooth Low Energy)介绍概要      蓝牙低能耗(BLE)技术是低成本、短距离、可互操作的鲁棒性无线技术,工作在免许可的2.4GHz ISM射频频段。它从一开始就设计为超低功耗(ULP)无线技术。      蓝牙低能耗技术的三大特性成就了ULP性能,这三大特性分别是最大化的待机时间、快速连接和低峰值的发送/接收功耗。      无线“开启”的时间只要不是很短就会令电池寿命急剧降低,因此任何必需的发送或接收任务需要很快完成。被蓝牙低能耗技术用来最小化无线开启时间的第一个技巧是仅用3个“广告”信道搜索其它设备,或向寻求建立连接的设备宣告自身存在。相比之下,标准蓝牙技术使用了32个信道。      蓝牙低能耗技术“完成”一次连接(即扫描其它设备、建立链路、发送数据、认证和适当地结束)只需3ms。而标准蓝牙技术完成相同的连接周期需要数百毫秒。再次提醒,无线开启时间越长,消耗的电池能量就越多。      具有低功耗蓝牙模块的设备可以扮演2个角色,中心,周边。周边是数据提供者,中心是数据接收/处理者。IOS设备可以很好的扮演这2个角色,利用现成的API就能开发出具有周边和中心功能的应用,我大Android就有点悲催了,自Android 4.3(API 18)的系统就规定了BLE的API,但是仅限于中心,至于周边一直没有API的支持。直到2014.6.26 Android Lollipop(Android 5.0 API 21)的面世,才带来了周边API的支持(BluetoothLeAdvertiser)。      ble在抓包软件或硬件上又分为Master和Slave。Master可以同时与多个设备连接,通信;Slave只能和一个Master通信。Cnetral主机(常作为client端):如手机,PCPeripheral从机(常作为Service端):如心率计,血糖计二、关键概念:(1)Generic Attribute Profile (GATT)      通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。(2)Attribute Protocol (ATT)      GATT是基于ATT Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。(3)Characteristic      Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。(4)Descriptor      对Characteristic的描述,例如范围、计量单位等。(5)Service      Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic。(6)关联      BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor,一个Descriptor包含一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY。三、相关权限<!-- 允许程序进行发现和配对新的蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 允许程序进行发现和配对新的蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
下面这两个权限是ScanBle需要的(二选一即可)
注意:标准蓝牙和低功耗在Android 6.0以上都是需要该权限才能扫描得到的,而Android 6.0以下不用该权限标准蓝牙也能扫描的到。
<!-- 访问CellID或WiFi,只要当前设备可以接收到基站的服务信号,便可获得位置信息 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 更精确的GPS需要该权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
代码判断是否支持BLE
getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
四、主要代码介绍
public class BluetoothBleDemo extends Activity {private static final int REQUEST_CODE_BLUETOOTH_ENABLE = 1;private static final int SCAN_PERIOD = 10000;private BluetoothAdapter bluetoothAdapter;//import android.bluetooth.le.ScanCallback;//Call requires API level 21 (current min is 18): new android.bluetooth.le.ScanCallback//java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan resultsprivate ScanCallback scanCallback = new ScanCallback() {@Overridepublic void onBatchScanResults(List<ScanResult> results) {//Batch 一批for(ScanResult result : results) {System.out.println("onBatchScanResults " + result);}}@Overridepublic void onScanResult(int callbackType, ScanResult result) {System.out.println("onScanResult " + result);BluetoothDevice device = result.getDevice();System.out.println("Device name: " + device.getName());//Returns the hardware address of this BluetoothDevice. For example, "00:11:22:AA:BB:CC".System.out.println("Device address: " + device.getAddress());System.out.println("Device service UUIDs: " + device.getUuids());//Get the Bluetooth device type of the remote device.System.out.println("Device type: " + device.getType());//Possible values for the bond state are: BOND_NONE, BOND_BONDING, BOND_BONDED.System.out.println("Device bondState: " + device.getBondState());ScanRecord record = result.getScanRecord();//Returns the advertising flags indicating the discoverable mode and capability of the device. Returns -1 if the flag field is not set.System.out.println("Record advertise flags: 0x" + Integer.toHexString(record.getAdvertiseFlags()));/*txPowerLevel 发射功率等级Returns the transmission power level of the packet in dBm. Returns Integer.MIN_VALUE if the field is not set. This value can be used to calculate the path loss of a received packet using the following equation:pathloss = txPowerLevel - rssi*/System.out.println("Record Tx power level: " + record.getTxPowerLevel());System.out.println("Record device name: " + record.getDeviceName());System.out.println("Record service UUIDs: " + record.getServiceUuids());//Returns a map of service UUID and its corresponding service data.System.out.println("Record service data: " + record.getServiceData());//Returns a sparse array of manufacturer identifier and its corresponding manufacturer specificSystem.out.println("Record manufacturer specific data: " + record.getManufacturerSpecificData());//RSSI 信号强度,可以用来测算距离  Returns the received signal strength in dBm. The valid range is [-127, 127].System.out.println("result rssi: " + result.getRssi());//Returns timestamp since boot when the scan record was observed.System.out.println("result timestampNanos: " + result.getTimestampNanos());switch(callbackType) {case ScanSettings.CALLBACK_TYPE_ALL_MATCHES:break;case ScanSettings.CALLBACK_TYPE_FIRST_MATCH:break;case ScanSettings.CALLBACK_TYPE_MATCH_LOST:break;}}@Overridepublic void onScanFailed(int errorCode) {System.out.println("onScanFailed errorCode = " + errorCode);}};private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);}@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);}@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorRead(gatt, descriptor, status);}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorWrite(gatt, descriptor, status);}@Overridepublic void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {super.onMtuChanged(gatt, mtu, status);}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {super.onReadRemoteRssi(gatt, rssi, status);}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {super.onReliableWriteCompleted(gatt, status);}};private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//判断是否支持BLE特性if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {initBluetoothBle();} else {showToast("this device can not support Bluetooth BLE");}}private void initBluetoothBle() {//BluetoothAdapter是Android系统中所有蓝牙操作都需要的,它对应本地Android设备的蓝牙模块,在整个系统中BluetoothAdapter是单例的。当你获取到它的示例之后,就能进行相关的蓝牙操作了。//BluetoothManager在Android4.3以上支持(API level 18)BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);bluetoothAdapter = bluetoothManager.getAdapter();}/*** 开启蓝牙* 打开和关闭蓝牙模块, 都可以通过ACTION_STATE_CHANGED广播来监听*/private void requestEnable() {//第一种方法打开蓝牙, 系统会提示应用要打开蓝牙,是否授权;disable不会有任何的提示。//boolean result = bluetoothAdapter.enable();//第二种方法发送广播, 会弹出一个对话框, 选择是否打开蓝牙, 选择是蓝牙才打开。if(bluetoothAdapter != null && !bluetoothAdapter.isEnabled()) {Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent, REQUEST_CODE_BLUETOOTH_ENABLE);}}/*** 扫描BLE设备.注意该方法无法扫描标准蓝牙,只能扫描BLE设备*/private void scanBleDevice(final boolean enabled) {if(bluetoothAdapter == null) {return;}/*为什么不能再使用单例的BluetoothAdapter? 原因如下:bluetoothAdapter.startLeScan() //deprecated http://stackoverflow.com/questions/30223071/startlescan-replacement-to-current-api Remember that the method: public BluetoothLeScanner getBluetoothLeScanner () isn't static.If you do: BluetoothAdapter.getBluetoothLeScanner()you will get an error, since getDefaultAdapter() is a static method, but getBluetoothLeScanner() isn't.You need an instance of a BluetoothAdapter.*/final BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();if(enabled) {//scan分为2类,而在android L之前,搜索条件只有uuid//(1)直接搜索全部周围peripheral(外围的)设备,搜索结果将通过这个callback返回scanner.startScan(scanCallback);//(2)根据过滤条件搜索设备final List<ScanFilter> scanFilters = new ArrayList<ScanFilter>();//uuid格式8-4-4-4-12(32位,128bit)//address格式(12位,48bit)scanFilters.add(new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("00000000-0000-0000-0000-000000000000")).setDeviceAddress("00:00:00:00:00:00").build());ScanSettings scanSettings = new ScanSettings.Builder()//require API 23//.setCallbackType(0).setMatchMode(0).setNumOfMatches(0).setReportDelay(0).setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE).build();scanner.startScan(scanFilters, scanSettings, scanCallback);handler.postDelayed(new Runnable() {@Overridepublic void run() {scanner.stopScan(scanCallback);}}, SCAN_PERIOD);} else {scanner.stopScan(scanCallback);}}/*** 两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server。数据发送方向总是从server推送到client*/private void connectToGATTServer(BluetoothDevice device) {//函数成功,返回BluetoothGatt对象,它是GATT profile的封装。通过这个对象,我们就能进行GATT Client端的相关操作。BluetoothGattCallback用于传递一些连接状态及结果。BluetoothGatt bluetoothGatt = device.connectGatt(this, false, gattCallback);//连接远程设备boolean connectResult = bluetoothGatt.connect();//搜索连接设备所支持的serviceboolean discoverResult = bluetoothGatt.discoverServices();//断开与远程设备的GATT连接bluetoothGatt.disconnect();//关闭GATT Client端bluetoothGatt.close();//读取指定的characteristic。//boolean readResult = bluetoothGatt.readCharacteristic(characteristic);//设置当指定characteristic值变化时,发出通知//boolean setResult = bluetoothGatt.setCharacteristicNotification(characteristic, enabled);//获取远程设备所支持的servicesList<BluetoothGattService> gattServices = bluetoothGatt.getServices();}private void showToast(String msg) {Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();}}
扫描结果如下图
(未完待续,上面讲了Android作为GattClient,剩下通信、Android做为GattServer还有数据分包的情况未讲——先记录一下,免得忘记)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: