ble的概念以及原理的简单理解
蓝牙是一种短距离无线通信技术,而蓝牙低功耗(BLE)是在蓝牙4.0协议上修改以适用低功耗应用场景的一种蓝牙协议。
那么4.0以后的蓝牙为什么低功耗了呢?传统蓝牙是通过广播收发状态,连接建立后通过socket建立连接,交互数据
ble相对于传统蓝牙
- 广播频段和广播时射频开启时间的减少:传统蓝牙使用16~32个频段进行广播,而BLE仅使用3个广播频段; 每次广播时的射频
开启时间由传统蓝牙的22ms减少为0.6~1.2ms; - BLE设计了深度睡眠状态(Duty-Cycle)来替代传统蓝牙的空闲时间,并且在Duty-Cycle时,发送数据间隔也被增大.
- BLE 的连接采用先进的Sniffer-Subrating模式.
- 传统蓝牙规范规定,若某一设备正在进行广播,则它不会响应当前正在进行的设备扫描,而BLE允许正在进行广播的设备连接正
在扫描的设备,这有效避免了重复连接。通过对连接机制的改进,BLE连接建立过程可控制在3ms内完成。 - BLE在每个从设备和每个数据包上使用32位的存取地址,优化了传统蓝牙一对一的连接,实现一对多(目前测试的是1带6)。
- BLE增加了GFSK调制,降低峰值功耗。
以上参考了https://blog.csdn.net/ZQ07506149/article/details/82380509
什么是GATT?
通用属性配置文件层(Generic Attribute profile,简写 GATT),简单点就是ble应用运行的环境或者场景,官方的专业解释,个人实在懒的贴出来,而且觉得也不好理解
GATT的层次结构
GATT通常有一个或者多个“Services”组成,一个 Characteristic 可以包含若干 Descriptor和value。而 Characteristic 定义了数值和操作。Characteristic 的操作这几种权限:读、写、通知等权限。
uuid
Service、Characteristic 还有 Descriptor 都是使用 UUID 唯一标示的。一会你可以看到在实际的操作中,都是通过uuid来查找你对应的设备
ble的开发
ble也是C/S的开发模式,特别注意一点ble开发要在主线程(底层源码注释),所以当两个设备建立连接之后,它们就处于下面两种角色之一:
- GATT服务器:为GATT客户端提供数据服务的设备。外围设备 可以创建uuid的服务
- GATT客户端:从GATT服务器读写应用数据的设备。中央设备
android的支持版本
官方说从android4.3版本开始,就支持了ble,但是在实际测试可不是这样,国内好多机型开始支持的版本号都不一样啊,这也是一个android碎片化的体现
GATT的连接问题
GATT连接是独占的。外设只能建立一个连接,但是客户端可以连接多台设备
GATT通信
中央设备在给外设发消息的时候,有时候只能发送一次, 因为写特征值前可以设置写的类型setWriteType(),写类型有三种,如下:
- WRITE_TYPE_DEFAULT 默认类型,需要外围设备的确认,也就是需要外围设备的回应,这样才能继续发送写。
- WRITE_TYPE_NO_RESPONSE 设置该类型不需要外围设备的回应,可以继续写数据。加快传输速率。
- WRITE_TYPE_SIGNED 写特征携带认证签名,具体作用不太清楚。
适配ios
在开发ble服务端的时候,遇到过一个很坑的问题,调试了很久,需要在Characteristic特征中加入以下这个BluetoothGattCharacteristic.PROPERTY_NOTIFY,否则ios接收不到,android没事,不知道ios的ble模块是怎样实现的,有知道可以留言一下
Beacon
非连接型的ble的数据收发
外围设备代码开发
初始化
mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(), createAdvData(), mAdvertiseCallback);
广告模式的回调
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(TAG, "onStartSuccess");
}
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "onStartFailure " + errorCode);
}
};
设置广播数据
private AdvertiseSettings createAdvSettings() {
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
//手机的发射功率,简单说就是蓝牙走多远
builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
builder.setConnectable(true);
builder.setTimeout(0);
//ADVERTISE_MODE_LOW_POWER 在低功耗模式下执行蓝牙LE广告。这是默认和首选的广告模式,因为它消耗最少的电力。
//ADVERTISE_MODE_BALANCED 在平衡电源模式下执行蓝牙LE广告。这是广告频率和功耗之间的平衡。
//ADVERTISE_MODE_LOW_LATENCY 在低延迟,高功率模式下执行蓝牙LE广告。这是最高的功耗,不应该用于连续的背景广告。
// TODO: 18-1-7 测试模式的值
builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
return builder.build();
}
设置响应数据
private AdvertiseData createAdvData() {
AdvertiseData.Builder builder = new AdvertiseData.Builder();
builder.addServiceUuid(ParcelUuid.fromString(UUID_SAMPLE_NAME_SERVICE));
//这样即使定义service uuid跟别人的有冲突,也可以在中心过滤该magic number来找到符合自己需求的外围设备,,但目前x是以设备名+系列号
byte mLeManufacturerData[] = {(byte) 0x4C, (byte) 0x00, (byte) 0x02, (byte) 0x15, (byte) 0x15, (byte) 0x15, (byte) 0x15};
builder.addManufacturerData(0x3103 + 1, mLeManufacturerData);
builder.setIncludeTxPowerLevel(false);
builder.setIncludeDeviceName(true);
return builder.build();
}
初始化GATT服务
//初始化GATT服务
BluetoothGattService nameService = new BluetoothGattService(UUID.fromString(UUID_SAMPLE_NAME_SERVICE),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//增加读写特征
mBleGattCharacteristicWrite = new BluetoothGattCharacteristic(
UUID.fromString(UUID_SAMPLE_NAME_CHARACTERISTIC),
BluetoothGattCharacteristic.PROPERTY_READ
| BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ
| BluetoothGattCharacteristic.PERMISSION_WRITE);
nameService.addCharacteristic(mBleGattCharacteristicWrite);
mGattServer.addService(nameService);
GATT服务的回调
mGattServer = mBluetoothManager.openGattServer(mContext, new BluetoothGattServerCallback() {
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
{
switch (newState) {
case BluetoothProfile.STATE_DISCONNECTED:
Log.d(TAG, device.getName() + " 断开");
sendHandlerMsg(device.getName() + " 断开");
break;
case BluetoothProfile.STATE_CONNECTED:
Log.d(TAG, device.getName() + " 连接");
sendHandlerMsg(device.getName() + " 连接");
break;
default:
break;
}
}
}
@Override
public void onServiceAdded(int status, BluetoothGattService service) {
super.onServiceAdded(status, service);
Log.d(TAG, "onServiceAdded");
}
/**A remote client has requested to read a local characteristic.*/
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
Log.d(TAG, "read");
}
/**
* 接收消息的方法 - 接收具体的字节
*/
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
characteristic.setValue(mReceiveSuccess.getBytes());
//触发发消息给客户端
mGattServer.notifyCharacteristicChanged(device,characteristic,false);
String msg = new String(value);
Log.d(TAG, "write " + msg);
sendHandlerMsg(msg);
//.处理响应内容
//onResponseToClient(requestBytes, device, requestId, characteristic);
}
/**
* 特征被读取。当回复响应成功后,客户端会读取然后触发本方法
*
*/
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
Log.d(TAG, "DescriptorRead");
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
}
/**
* 2.描述被写入时
*/
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
Log.d(TAG, "DescriptorWrite");
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
@Override
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
super.onExecuteWrite(device, requestId, execute);
Log.d(TAG, "onExecuteWrite");
}
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
Log.d(TAG, "onNotificationSent");
}
});
客户端代码 - 中央设备
初始化-扫描-连接-连接GATT服务-搜索服务-通信
检查蓝牙打开,ble可用,得到BluetoothAdapter对象
扫描
该方法专门针对ble的扫描设备,不是传统扫描,无法扫描非ble外设,api要求21以上
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
mBluetoothLeScanner.startScan(mScanCallback);
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (null != result) {
BluetoothDevice device = result.getDevice();
}
}
};
连接
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
return false;
}
//根据地址连接远程设备
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
return false;
}
//连接已开启的GATT服务
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
mBluetoothDeviceAddress = address;
GATT服务的回调,成功连接后开始搜索服务 mBluetoothGatt.discoverServices();
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
/**
* 连接状态的改变
*/
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//成功连接后开始搜索服务
if (newState == BluetoothProfile.STATE_CONNECTED) {
discoverServices();
setConnectionStatus(STATE_CONNECTED);
if (null != mIBleGattCallback) {
mIBleGattCallback.onConnectionStateChange(gatt, mConnectionState);
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
setConnectionStatus(STATE_DISCONNECTED);
if (null != mIBleGattCallback) {
mIBleGattCallback.onConnectionStateChange(gatt, mConnectionState);
}
}
}
/**
* 发现服务
*/
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(TAG,"onServicesDiscovered");
if (status == BluetoothGatt.GATT_SUCCESS) {
if (null != mIBleGattCallback) {
mIBleGattCallback.onServicesDiscovered(gatt);
}
} else {
excuteError(status);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (null != mIBleGattCallback) {
mIBleGattCallback.onCharacteristicRead(gatt, characteristic);
}
} else {
excuteError(status);
}
}
/**
* 接收外设的消息
* @param gatt BluetoothProfile的对象
* @param characteristic 特征
*/
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if (null != mIBleGattCallback) {
mIBleGattCallback.onCharacteristicChanged(gatt, characteristic);
onReceiveMsg(characteristic);
}
}
};
发数据
获取可用的特征
可以通过协定好的uuid获取,也可以遍历获取可写的服务
//判断特征可写
public boolean ifCharacteristicWritable(BluetoothGattCharacteristic characteristic){
return ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 ||
(characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0);
}
//直接获取相应uuid的服务
UUID uuid = UUID.fromString(BleStatusConstant.UUID_SAMPLE_NAME_SERVICE);
BluetoothGattService service = gatt.getService(uuid);
UUID uuid2 = UUID.fromString(BleStatusConstant.UUID_SAMPLE_NAME_CHARACTERISTIC);
mBleCharacteristic = service.getCharacteristic(uuid2);
写入属性
/**
* 写入属性
*
* @param characteristic 特征
* @return true写入成功 false写入失败
*/
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
return false;
}
return mBluetoothGatt.writeCharacteristic(characteristic);
}
结束别忘了释放,如果有扫描也需要停止扫描
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;