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)。
扫描外设。
链接外部设备。
找到外设服务。
找到外设服务特征。
通过特征做数据交互。
交互完毕断开链接。
交互图如下:
iOS 蓝牙技术CoreBluetooth

首先创建中心设备管理:

- (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(@"订阅失败");
    }
}

相关文章: