基于蓝牙4.0进行通讯,以手机为CentralManager,简单串一下通讯流程如下:
在iOS10以后,需要在 Info.plist 文件里面设置 NSBluetoothPeripheralUsageDescription 字段,添加访问蓝牙权限的描述,否则程序会崩溃,且上传程序是会被拒的。
1、扫描
(1)初始化一个CentralManager
CBCentralManager 的创建是异步的,如果初始化完成之后没有被当前创建它的类所持有,控制台会报错(说白了如果是局部变量就会在控制台报错)
btle[3800:1288058] [CoreBluetooth] XPC connection invalid
self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey:@YES}];
- queue:指明在哪个队列处理事件,为nil时表示在主线程处理
- options:这个字典有俩个官方的key
CBCentralManagerOptionShowPowerAlertKey:提示蓝牙开关未打开时会弹出警告框
CBCentralManagerOptionRestoreIdentifierKey:一个指定中央管理器的uid(和蓝牙程序进入后台有关,没有多做研究)
(2)监听CentralManager蓝牙状态
当创建CentralManager成功后,系统就会回调CBCentralManagerDelegate的代理方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBManagerStatePoweredOn:
//打开状态
if (self.centralManager.state == CBCentralManagerStatePoweredOn) {
//开始扫描
[self.centralManager scanForPeripheralsWithServices:self.deviceServiceArray options:nil];
}
break;
case CBManagerStatePoweredOff:
//关闭状态
break;
case CBManagerStateResetting:
//复位
break;
case CBManagerStateUnsupported:
//表明设备不支持蓝牙低功耗
break;
case CBManagerStateUnauthorized:
//该应用程序是无权使用蓝牙低功耗
break;
case CBManagerStateUnknown:
//未知
break;
default:
break;
}
}
(3)开始扫描
只有当蓝牙状态为CBManagerStatePoweredOn的时候,才可以扫描周边设备,否则控制台报错
btle[3815:1292851] [CoreBluetooth] API MISUSE: <CBCentralManager: 0x174263380> can only accept this command while in the powered on state
if (self.centralManager.state == CBCentralManagerStatePoweredOn) {
//开始扫描
[self.centralManager scanForPeripheralsWithServices:self.deviceServiceArray options:nil];
}
- serviceUUIDs:为nil时,会扫描正在广播的所有Peripheral。如果你指定某个Service或者一组Service,只能扫描提供这些Service的Peripheral。(Service里面放的是CBUUID)
- options:这个字典有俩个官方的key
CBCentralManagerScanOptionAllowDuplicatesKey:为NO,表示不会重复扫描已经发现的设备。为YES,扫描中会出现已经扫到的设备。默认为NO
CBCentralManagerScanOptionSolicitedServiceUUIDsKey:以一个数组的形式存在,指定这个选项后central便会找指定的服务的CBUUID
(3)扫描成功
每当扫描到一个外设,系统就会回调CBCentralManagerDelegate的代理方法
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
if (advertisementData[CBAdvertisementDataLocalNameKey] == nil || advertisementData[CBAdvertisementDataServiceUUIDsKey] == nil) return;
if (![self.connectedPeripheralArray containsObject:peripheral]) {
//停扫
[self.centralManager stopScan];
self.currentPeripheral = [peripheral copy];
//设备连接
[self.centralManager connectPeripheral:self.currentPeripheral options:nil];
}
}
连接
(1)设备连接
当系统扫描到的peripheral进行连接,成功与失败都会有相应的回调方法响应,没有连接超时的回调,苹果官方文档说明,只有连接失败
self.currentPeripheral = [peripheral copy];
//设备连接
[self.centralManager connectPeripheral:self.currentPeripheral options:nil];
- peripheral:将要连接的peripheral,需要保持一下peripheral
- options:这个字典有俩个官方的key
这些key值和蓝牙后台有关,没有多做研究
CBConnectPeripheralOptionNotifyOnConnectionKey
CBConnectPeripheralOptionNotifyOnDisconnectionKey
CBConnectPeripheralOptionNotifyOnNotificationKey
(2)当设备连接成功
当你调用设备连接的时候,成功就会回调CBCentralManagerDelegate的代理方法,这里我们需要去发现CBPeripheral的一些相关服务
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
if (![self.connectedPeripheralArray containsObject:peripheral]){
[self.connectedPeripheralArray addObject:peripheral];
//设置代理发现服务
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
}
}
(3)设备连接失败
当你调用设备连接的时候,失败就会回调CBCentralManagerDelegate的代理方法,可以再次连接外设
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
//重新开扫
[self startScan];
}
(4)设备断开
当你调用设备连接成功后,又断开的时候就会回调CBCentralManagerDelegate的代理方法
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
if([self.connectedPeripheralArray containsObject:peripheral]){
[self.connectedPeripheralArray removeObject:peripheral];
}
//重新开扫
[self startScan];
}
发现服务
(1)发现CBPeripheral的Services
当CBPeripheral连接成功后,需要设置CBPeripheralDelegate代理方法去发现CBPeripheral的相关服务
//设置代理发现服务
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
- serviceUUIDs:可以添加一组CBUUID的service,这样就可以发现指定的Services(官方推荐),如果为nil,则去发现所有有效的Services(相对较慢,不推荐)
(2)发现Services成功
成功发现Services,就会回调CBPeripheralDelegate的代理方法,需要通过peripheral的services属性去获取发现的CBService。同时,我们需要在此去发现每一个CBService下的characteristics
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error == nil){
for (CBService *aService in peripheral.services) {
//发现特征
[peripheral discoverCharacteristics:nil forService:aService];
}
}else{
//如果有错,则主动断开,然后会走(centralManager:didDisconnectPeripheral:error:)
[self.centralManager cancelPeripheralConnection:peripheral];
}
}
发现特征
(1)发现Characteristics
for (CBService *aService in peripheral.services) {
//发现特征
[peripheral discoverCharacteristics:nil forService:aService];
}
}
- characteristicUUIDs:可以添加一组CBService的characteristic,这样就可以发现指定的characteristic(官方推荐),如果为nil,则去发现所有有效的characteristic(相对较慢,不推荐)
- service:被发现Characteristics的CBService
(2)发现Characteristics成功
成功发现Characteristics就会回调CBPeripheralDelegate的代理方法,通常会有两中characteristics:CBCharacteristicPropertyRead和CBCharacteristicPropertyWrite。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error == nil) {
for (CBCharacteristic *aChar in service.characteristics){
//判断Characteristic属性类型
if ((aChar.properties & CBCharacteristicPropertyRead)){
self.readCharacteristic = aChar;
[peripheral readValueForCharacteristic:aChar];
}
if ((aChar.properties & CBCharacteristicPropertyWrite)){
self.writeCharacteristic = aChar;
[peripheral setNotifyValue:YES forCharacteristic:aChar];
NSLog(@"----%@",aChar);
}
}
}
else{
//如果有错,则主动断开,然后会走(centralManager:didDisconnectPeripheral:error:)
[self.centralManager cancelPeripheralConnection:peripheral];
}
}
(3)写使能
对于Characteristic是CBCharacteristicPropertyNotify的写使能,就会回调CBPeripheralDelegate的代理方法
[peripheral setNotifyValue:YES forCharacteristic:aChar];
(4)判断使能是否写成功
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (characteristic.isNotifying) {
NSLog(@"成功");
}
}
(5)从Peripheral读取数据
对于Characteristic是CBCharacteristicPropertyRead的写使能,就会回调CBPeripheralDelegate的代理方法
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSData *responseData = characteristic.value;
NSLog(@"data--%@",responseData);
}
实现通讯
(1)写入数据
写入数据时,当你把type设置为CBCharacteristicWriteWithResponse,就会回调CBPeripheralDelegate的代理方法,如果为CBCharacteristicWriteWithoutResponse则不会
- (void)sendData{
uint8_t sendData[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
NSMutableData *data=[[NSMutableData alloc]init];
[data appendBytes:(void *)(&sendData[0]) length:sizeof(sendData)];
//写入数据
[self.currentPeripheral writeValue:data.copy forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse];
}
(2)写入成功
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSLog(@"成功");
}
(3)接收数据
当你写入成功数据成功之后,CBPeripheral收到后若有返回的数据则继续会出现在(5)从Peripheral读取数据中,以此返回进行通信。。。。