IOS蓝牙技术—CoreBluetooth
一、iOS蓝牙简述
二、CoreBluetooth简介
1、基本概念
2、CoreBluetooth 框架介绍
三、开发模式
1、中心模式
2、外设模式
一、iOS蓝牙简述
iOS开发中关于蓝牙技术的框架有四种:
GameKit.framework 多用于游戏开发,iOS设备之间的连接。
MultipeerConnectivity.framework iOS设备之间传递文件,点对点的连接。
ExternalAccessory.framework 主要是用于和蓝颜2.0(需要苹果认证)设备连接通信的框架。
CoreBluetooth.framework 主要用户和蓝牙4.0的设备连接和通讯。蓝牙4.0也叫 BLE。
在这里我们就主要针对蓝牙4.0的开发,也就是iOS的BLE开发。
二、CoreBluetooth简介
1、基本概念
中心设备(central)。主动发起连接的设备。
外设(peripheral)。外部设备,比如耳机,音箱,智能手环。(当然手机也可以是外部设备,中兴设备也可能成为外部设备)。
服务。每个外设都有若干个服务,可以添加和删除服务。外设可以广播服务供中心设备区扫描。每个服务都有一个UUID,用来标识每个服务。
特征。每个服务都有若干个特征,设备之间是通过特征值来进行数据交互的。这些特征值有很多属性(比如:读,写,通知)。只有这个特征设置了相应的属性才能进行相应的操作。如果没有读的属性,中心设备数无法读取到外设的数据的。
特征的属性。每个特征都有它的属性值。不同的属性限制了外设对外提供的服务。读的属性可以有对外提供数据的能力,写属性可以对外提供数据写入特征的能力。
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x200
};
2、CoreBluetooth 框架介绍
CBCentralManager 中心设备管理,检测设备状态,扫描外部设备,链接外部设备等。
CBPeripheralManager 外部设备管理,检测外部设备状态,添加/移除服务,广播服务,更新特征值,响应中心设备的读写等。
CBPeripheral 外设对象 向外设写/读数据,订阅通知,查找服务,查找服务特征等。
CBCentral 中心设备
CBMutableService 特征服务
CBMutableCharacteristic 特征
CBUUID 表示id
CBATTError 状态码, 比如响应请求返回状态码。
typedef NS_ENUM(NSInteger, CBATTError) {
CBATTErrorSuccess NS_ENUM_AVAILABLE(10_9, 6_0) = 0x00,
CBATTErrorInvalidHandle = 0x01,
CBATTErrorReadNotPermitted = 0x02,
CBATTErrorWriteNotPermitted = 0x03,
CBATTErrorInvalidPdu = 0x04,
CBATTErrorInsufficientAuthentication = 0x05,
CBATTErrorRequestNotSupported = 0x06,
CBATTErrorInvalidOffset = 0x07,
CBATTErrorInsufficientAuthorization = 0x08,
CBATTErrorPrepareQueueFull = 0x09,
CBATTErrorAttributeNotFound = 0x0A,
CBATTErrorAttributeNotLong = 0x0B,
CBATTErrorInsufficientEncryptionKeySize = 0x0C,
CBATTErrorInvalidAttributeValueLength = 0x0D,
CBATTErrorUnlikelyError = 0x0E,
CBATTErrorInsufficientEncryption = 0x0F,
CBATTErrorUnsupportedGroupType = 0x10,
CBATTErrorInsufficientResources = 0x11
};
三、开发模式
1、中心模式
开发步骤:
创建中心设备管理(CBCentralManager)。
扫描外设。
链接外部设备。
找到外设服务。
找到外设服务特征。
通过特征做数据交互。
交互完毕断开链接。
交互图如下:
首先创建中心设备管理:
- (void)p_initCentralManager
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_6_0
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],CBCentralManagerOptionShowPowerAlertKey,
@"CMBCBluetoothRestore",CBCentralManagerOptionRestoreIdentifierKey,
nil];
#else
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],CBCentralManagerOptionShowPowerAlertKey,
nil];
#endif
NSArray *backgroundModes = [[[NSBundle mainBundle] infoDictionary]objectForKey:@"UIBackgroundModes"];
if ([backgroundModes containsObject:@"bluetooth-central"]) {
//后台模式
self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:options];
}
else {
//非后台模式
self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
}
}
这个时候代理会回调检测设备状态
- (void)getCentralManagerCompletionHandler:(void(^)(CBCentralManagerState state))handler
{
_centralStatusBlock = handler;
if (!_centralManager) {
[self p_initCentralManager];
} else {
//如果已经知道了状态,立刻通知
if (_centralManager.state != CBCentralManagerStateUnknown) {
if (_centralStatusBlock) {
_centralStatusBlock((CBCentralManagerState)_centralManager.state);
}
}
}
}
扫描到外设的回调
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary<NSString *, id> *)advertisementData
RSSI:(NSNumber *)RSSI
{
NSLog(@"发现设备%@",advertisementData);
NSString *name = peripheral.name;
NSLog(@"设备名称:%@",name);
[self.discoverPeripherals addObject:peripheral];
if (_discoverPeripheralsBlock) {
_discoverPeripheralsBlock(central,peripheral,advertisementData,RSSI);
}
}
链接外部设备:
- (void)connectPeripheral:(nonnull CBPeripheral *)peripheral
options:(nullable NSDictionary<NSString *, id> *)options
compeletionHandler:(void(^)(CBPeripheral *peripheral, NSError *error))handler
{
[self p_cancelTimer];
self.connectedResultBlock = handler;
[self.connectedPeripherals addObject:peripheral];
[self.centralManager connectPeripheral:peripheral options:options];
peripheral.delegate = self;
[self.centralManager stopScan];
}
链接回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"链接成功");
// 停止扫描
[self.centralManageer stopScan];
// 设置代理
self.perpheral.delegate = self;
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"连接失败");
}
查找外设服务
[peripheral discoverServices:nil];
找到外设服务的回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
for (CBService *service in peripheral.services) {
// 外设服务
}
}
通过找的外设服务再查找外设服务的特征
[peripheral discoverCharacteristics:nil forService:service];
查找到服务特征的回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
for(CBCharacteristic *character in service.characteristics){
//character 所有的特征
}
}
读取特征服务的数据
- (void)readToPeripheral:(nonnull CBPeripheral *)peripheral
characteristic:(nonnull CBUUID *)characteristicUUID
compeletionHandler:(void(^)(CBPeripheral *peripheral,CBCharacteristic *charatic, NSError *error))handler
{
self.readChsticBlock = handler;
NSError *error;
CBCharacteristic *targetChtic = [self p_findCharacteristic:characteristicUUID inPeripheral:peripheral withError:&error];
if (error) {
if (_readChsticBlock) {
_readChsticBlock(peripheral,nil,error);
}
return;
}
if (targetChtic.properties & CBCharacteristicPropertyRead) {
[peripheral readValueForCharacteristic:targetChtic];
}else {
NSError *error = [NSError errorWithDomain:@"CMBCReadCharacterNoPermissions" code:-2 userInfo:nil];
if (_readChsticBlock) {
_readChsticBlock(peripheral,nil,error);
}
}
}
特征读取回调(外设服务特征值被修改也会回调到这里)
#pragma mark - 读
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(nullable NSError *)error
{
//订阅数据发生改变,始终通知。
if (_subscribeUpdateBlock && (characteristic.properties & CBCharacteristicPropertyNotify)){
_subscribeUpdateBlock(peripheral,characteristic,error);
return;
}
//如果是读取
if (_readChsticBlock && (characteristic.properties & CBCharacteristicPropertyRead)) {
_readChsticBlock(peripheral,characteristic,error);
}
}
给特征写数据
-(void)writeToPeripheral:(nonnull CBPeripheral *)peripheral
characteristic:(nonnull CBUUID *)characteristicUUID
value:(nullable NSData *)value
compeletionHandler:(void(^)(CBPeripheral *peripheral,CBCharacteristic *charactic, NSError *error))handler
{
self.writeChsticBlock = handler;
NSError *error;
CBCharacteristic *targetChtic = [self p_findCharacteristic:characteristicUUID inPeripheral:peripheral withError:&error];
if (error) {
if (_writeChsticBlock) {
_writeChsticBlock(peripheral,nil,error);
}
return;
}
if (targetChtic.properties & CBCharacteristicPropertyWrite) {
[peripheral writeValue:value forCharacteristic:targetChtic type:CBCharacteristicWriteWithResponse];
} else {
NSError *error = [NSError errorWithDomain:@"CMBCWriteCharacterNoPermissions" code:-2 userInfo:nil];
if (_writeChsticBlock) {
_writeChsticBlock(peripheral,nil,error);
}
}
}
写数据回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if(!error){
NSLog(@"写数据");
}else{
// 可能写入数据过大或者是写入需要外设响应但是外设没有设置响应,这个时候也会出错
NSLog(@"写数据失败");
}
}
订阅特征
// 开启和关闭订阅
//只有订阅开启了之后 外设特征跟新才会通知到中心设备 (中心设备的回调才能接收到)
[self.perpheral setNotifyValue:BOOL forCharacteristic:characteristicl];
// 订阅状态的回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if(!error){
NSLog(@"订阅成功");
}else{
NSLog(@"订阅失败");
}
}