蓝牙ble(Bluetooth Low Energy):蓝牙低能耗技术蓝牙4.0
适用范围:Android4.3之后才支持BLE;iOS7.0开始支持BLE,Swift语言需要iOS8.0以上,硬件要求iPhone4s以上(包含4s)
蓝牙通信关系:主从关系,每一对设备进行蓝牙通讯时,必须一个为中心设备central,另一个为从设备peripheral.通信时必须central进行查找,发起配对,连接成功后双方可收发数据.一个central可以最多同时连接7个从设进行通讯,一个peripheral只能连接一个central.
Android开发手机做central:
1 获取权限
//判断手机是否有硬件BLE模块
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />
//获取权限
<uses-permission android:name="android.permission.BLUETOOTH" />
//允许程序连接到已配对的蓝牙设备
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
//允许程序发现和配对蓝牙设备
Android6.0蓝牙权限需要添加模糊定位权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
模糊定位是Dangerous Permission,涉及到用户隐私,使用时需要用户实时授权:
在代码中添加首次进入判断://动态运行时权限
private void checkBluetoothPermission() {
if (Build.VERSION.SDK_INT >= 23) {
//校验是否已具有模糊定位权限
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
} else {
//具有权限
}
} else {
//系统不高于6.0直接执行
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
doNext(requestCode, grantResults);
}
2 初始化蓝牙外设
获取到系统BluetoothManager:getSystemService(Context.BLUETOOTH_SERVICE)
获取到蓝牙适配器:manager.getAdapter
打开蓝牙:adapter.enable
/**
* 初始化蓝牙外设
*/
public boolean initialize() {
//判断手机是否配置蓝牙4.0
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Log.e(TAG, "initialize:不能初始化蓝牙,因为您的手机不适配蓝牙4.0,没有蓝牙模块 " );
return false;
}
//获取到蓝牙管理者
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "initialize:初始化蓝牙管理器失败 ");
return false;
}
}
//获取到蓝牙适配器
if(mBluetoothAdapter == null) {
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "initialize:初始化蓝牙适配器失败 " );
return false;
}
}
//打开手机蓝牙
if(mBluetoothAdapter == null) {
Log.e(TAG, "initialize:初始化蓝牙适配器失败 " );
return false;
} else {
//判断是否有蓝牙模块
boolean hasBle = mContext.getPackageManager().hasSystemFeature("android.hardware.bluetooth_le");
if (!hasBle) {
Log.e(TAG, "initialize:不能初始化蓝牙,因为您的手机不适配蓝牙4.0,没有蓝牙模块 ");
return false;
}
//打开手机蓝牙
mBluetoothAdapter.enable();
return true;
}
}
3 扫描外设,断开扫描
蓝牙扫描外设Android:Android5.0(SDK21)之前 startLeScan ,stopLeScan,回调函数LeScanCallback
Android5.0之后版本:先获取到BluetoothLeScanner: mBluetoothAdapter.getBluetoothLeScanner();
扫描:mBluetoothLeScanner.startScan(mScanCallback), 停止扫描:mBluetoothLeScanner.stopScan(mScanCallback)
扫描结果回调函数:ScanCallback
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private static class VersionA {
//扫描外设的回调函数
private static BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
Log.e(TAG, "onLeScan: 扫描一次" );
mBleCallBack.deviceScanned(bluetoothDevice, i, bytes);
}
};
public static void startLeScan() {
if (mBluetoothAdapter == null) {
Log.e(TAG, "startLeScan:开始扫描,适配器失败 ");
return;
}
mBluetoothAdapter.startLeScan(mLeScanCallback);
}
public static void stopLeScan() {
if (mBluetoothAdapter == null) {
Log.e(TAG, "stopLeScan:停止扫描,适配器失败 ");
return;
}
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
/**
* 5.0之后版本的外设扫描
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static class VersionB {
private static ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
//if (isDevice(result.getScanRecord().getBytes())) {
// TODO: 16/6/24 扫描回调函数
Log.e(TAG, "onScanResult: 扫描一次" );
// Log.d(TAG, "onScanResult: 扫描回调函数,回调一次"+result.getDevice().getAddress());
mBleCallBack.deviceScanned(result.getDevice(), callbackType, result.getScanRecord().getBytes());
// }
}
};
public static void startScan() {
if (mBluetoothLeScanner == null) {
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
mBluetoothLeScanner.startScan(mScanCallback);
}
public static void stopScan() {
if (mBluetoothLeScanner != null) {
mBluetoothLeScanner.stopScan(mScanCallback);
}
}
}
3 连接设备:
手动连接设备,通过address连接
通过address获取到设备device:mBluetoothAdapter.getRemoteDevice(address);
创建连接,连接gatt通道:device.connectGatt(mContext, false, mGattCallback);
GATT:定义两个BLE设备通过Service和characteristic的东西进行通信,GATT连接是独占的,一个BLE外设同时只能被一个中心设备连接,一旦外设被连接就不再广播,这样它对其他设备不可见,设备断开时,它又开始广播.
GATT中
一个低功耗蓝牙(ble)可以包括多个Profile
一个Profile中有多个Service(通过uuid就可以找到对应的Service)
一个Service中有多个Characteristic(通过uuid就可以找到对应的Characteristic)
一个Characteristic中包括一个value和多个Descriptor(通过uuid就可以找到对应的Descriptor)
/**
* 手动连接设备,通过address连接
*/
public boolean connect(final String address) {
currentDevice = null;
if (mBluetoothAdapter == null || address == null) {
Log.e(TAG, "connect: 蓝牙适配器初始化失败或者蓝牙外设地址为空");
return false;
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.e(TAG, "外设没有找到,连接失败");
return false;
}
currentDevice = device;
// 曾经连接上过,重新连接
// if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
// && mBluetoothGatt != null) {
// Log.e(TAG, "尝试连接一个曾经连接上的外设");
// mBluetoothGatt.close();
// if (mBluetoothGatt.connect()) {
// //已经连接上了,但是基于蓝牙协议,并没有连接上,手动扫描service从而实现连接
// mBluetoothGatt.discoverServices();
// return true;
// } else {
// return false;
// }
// }
if (mBluetoothGatt != null){
mBluetoothGatt.close();
mBluetoothGatt = null;
}
//连接设备
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
Log.e(TAG, "尝试创建一个新连接,连接外设Gatt新通道");
mBluetoothDeviceAddress = address;
if (mBluetoothGatt != null){
Log.e(TAG, "Gatt通道建立成功");
}else {
Log.e(TAG, "Gatt通道建立失败");
}
return true;
}
4 断开连接:gatt.disConnect
5发送数据到外设:
this.writeCharacteristic.setValue(data);
this.mBluetoothGatt.writeCharacteristic(this.writeCharacteristic);
6 一旦扫描到外设,就可以获取到service了,这样就可以对属性进行读写了
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// TODO: 16/6/24 设备连接
mBleCallBack.deviceConnected(currentDevice.getAddress());
//搜索连接到的蓝牙外设的服务
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBluetoothGatt.discoverServices();
//监测到设备连接成功后,扫描service
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// TODO: 16/6/24 设备断开
if (mBluetoothGatt != null){
mBluetoothGatt.close(); // 释放资源
mBluetoothGatt = null;
}
mBleCallBack.deviceDisconnected(currentDevice.getAddress());
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(TAG, "onServicesDiscovered: BluetoothGatt Status:"+status);
if (status == BluetoothGatt.GATT_SUCCESS){
// TODO: 16/6/27 搜索服务中
if(bleServiceList != null && bleServiceList.size() > 0) {
bleServiceList.clear();
}
if(mBluetoothGatt != null) {
bleServiceList = mBluetoothGatt.getServices();
}
Log.d(TAG, "onServicesDiscovered: gatt success");
setCharacteristicNull();
BluetoothGattService bluetoothGattService = mBluetoothGatt.getService(SERVICE_UUID);
if (bluetoothGattService != null){
List<BluetoothGattCharacteristic> characteristicList = bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristicList) {
Log.d(TAG, "onServicesDiscovered: Characteristic:"+characteristic.getUuid()+"propertity:"+characteristic.getProperties());
if (characteristic.getUuid().equals(UUID.fromString(""))) {
if ((characteristic.getProperties() & 18) != 0) {
Log.d(TAG, "onServicesDiscovered: readCharacteristic:"+characteristic.getProperties());
readCharacteristic = characteristic;//获取到读写characteristic
setCharacterNotification(readCharacteristic,true);
} else if ((characteristic.getProperties() & 12) != 0) {
Log.d(TAG, "onServicesDiscovered: writeCharacteristic:"+characteristic.getProperties());
characteristic.setWriteType(1);
writeCharacteristic = characteristic;
}
}
}
}
}
BluetoothGattService bluetoothGattServicen = mBluetoothGatt.getService(SERVICE_UUIDN);
if (bluetoothGattServicen != null) {
List<BluetoothGattCharacteristic> characteristicListn = bluetoothGattServicen.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristicListn) {
if (characteristic.getUuid().equals(UUID.fromString("d44bc439-abfd-45a2-b575-925416129601"))) {
readCharacteristic = characteristic;
setCharacterNotification(readCharacteristic,true);
Log.e(TAG, "读:" + characteristic.getProperties());
}
if (characteristic.getUuid().equals(UUID.fromString("d44bc439-abfd-45a2-b575-925416129600"))) {
writeCharacteristic = characteristic;
//characteristic.setWriteType(1);
Log.e(TAG, "写:" + characteristic.getProperties());
}
}
}
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if(status == 0) {
// TODO: 16/6/27 读characteristic的值
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
byte[] var3 = characteristic.getValue();
Log.d(TAG,"write character response:"+CharUtils.bytesToHexString(var3));
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
byte[] var3 = characteristic.getValue();
Log.d(TAG, "onCharacteristicChanged: data:"+CharUtils.bytesToHexString(var3));
// TODO: 16/6/27 接收数据
mBleCallBack.deviceReceiveData(currentDevice.getAddress(),var3);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if(status == 0) {
// TODO: 16/6/27 写入descriptor 之后回调
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
// TODO: 16/6/27 读取Rssi的值
mBleCallBack.deviceRssiReport(currentDevice.getAddress(),rssi);
}
};
7 开启notify接收数据
如果设备主动给手机发信息,则可以通过notification的方式,如果notificaiton方式对于某个Characteristic是enable的,那么当设备上的这个Characteristic改变时,手机上onCharacteristic回调就会被促发。
private void setCharacterNotification(BluetoothGattCharacteristic characteristic, boolean enabled){
Log.e(TAG,"set ChatacterNotification");
boolean isOk = mBluetoothGatt.setCharacteristicNotification(characteristic,enabled);
Log.e(TAG,"set ChatacterNotification:"+isOk);
if(!isOk) {
Log.e(TAG, "设置通知失败");
}
// BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
// if(descriptor != null) {
// byte[] bytes = enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
// descriptor.setValue(bytes);
// boolean isWrite = mBluetoothGatt.writeDescriptor(descriptor);
// if (isWrite){
// Log.d(TAG, "writeDescriptor:ok ");
// }else{
// Log.d(TAG, "writeDescriptor:on ");
// }
// }
List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
if(descriptorList != null && descriptorList.size() > 0) {
Log.e(TAG, "descriptor != null");
for(BluetoothGattDescriptor descriptor : descriptorList) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean isWrite = mBluetoothGatt.writeDescriptor(descriptor);
if (isWrite){
Log.d(TAG, "writeDescriptor:ok ");
}else {
Log.d(TAG, "writeDescriptor:on ");
}
}
}
}
本文详细介绍蓝牙低能耗(BLE)技术的基础知识与开发流程,涵盖Android与iOS平台的权限配置、蓝牙初始化、设备扫描与连接、数据收发及通知设置等内容。
4816

被折叠的 条评论
为什么被折叠?



