一、蓝牙

随着蓝牙低功耗技术BLE(Bluetooth Low Energy)的发展,蓝牙技术正在一步步成熟,如今的大部分移动设备都配备有蓝牙4.0,相比之前的蓝牙技术耗电量大大降低。

在iOS中进行蓝牙传输常用的框架有如下几种:
  1. GameKit.framework
    iOS7之前的蓝牙框架,只可用于同应用之间的蓝牙传输。
  2. MultipeerConnectivity.framework
    iOS7开始引入的蓝牙框架,用于取代GameKit,也有缺陷,仅支持iOS设备之间蓝牙传输。
  3. CoreBluetooth.framework
    功能强大的蓝牙框架,但要求设备必须支持蓝牙4.0,可以支持所有设备蓝牙传输,只要该设备支持蓝牙4.0。

应用的比较多的是CoreBluetooth框架,这里就选择CoreBluetooth框架来讲。

二、CoreBluetooth

当前BLE应用相当广泛,不再仅仅是两个设备之间的数据传输,它还有很多其他应用市场,例如室内定位、无线支付、智能家居等等,这也使得CoreBluetooth成为当前最热门的蓝牙技术。
我的理解中,CoreBluetooth蓝牙通信过程和网络通信过程比较类似。

CoreBluetooth中,蓝牙传输都分为两个部分:
  1. 外围设备CBPeripheral
    负责发布并广播服务,告诉周围的中央设备它的可用服务和特征,类似于网络通信中的服务端。
  2. 中央设备CBCentral
    负责和外围设备建立连接,一旦连接成功就可以使用外围设备的服务和特征,类似于网络通信中的客户端

android手机蓝牙传文件给iphone手机失败_ui

 

外围设备和中央设备交互的桥梁是服务特征,两个都有一个唯一标识CBUUID来确定一个服务或者特征:

  • 服务CBService
    中央设备只有通过服务才能与外围设备进行数据传输,类似于客户端通过网址URL才能与服务器连接一样
  • 特征CBCharacteristic
    每个服务可以拥有多个特征,中央设备必须订阅外围设备服务的特征值,才能获取外围设备的数据,类似于GET请求可以请求获取服务器数据,POST请求可以向服务器传输数据一样。

android手机蓝牙传文件给iphone手机失败_外围设备_02

 

三、设备作为外围设备

设备作为外围设备的创建步骤:
  1. 创建外围设备管理器CBPeripheralManager,并设置代理
  2. 创建一个特征CBCharacteristic,绑定一个CBUUID,设置特征属性
  3. 创建一个服务CBService,绑定一个CBUUID,设置服务的特征
  4. 调用外围设备管理器的对象方法,添加服务到外围设备上
-(void)addService:(CBService *)service;
-(void)addService:(CBService *)service;
  1. 外围设备管理器开始广播服务
-(void)startAdvertising:(NSDictionary *)dict;
-(void)startAdvertising:(NSDictionary *)dict;
  1. 在外围设备管理器的代理方法,处理与中央设备的交互

android手机蓝牙传文件给iphone手机失败_代理方法_03

 

外围设备管理器启动服务的相关代理方法:
/* 外围设备管理器状态发生改变后调用,比如外围设备打开蓝牙的时候 */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
/* 外围设备管理器添加了服务后调用,一般在这里进行广播服务 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service /* 添加的服务 */ error:(NSError *)error;/* 添加服务错误信息 */ /* 启动广播服务后调用 */ - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error;/* 启动服务错误信息 */ /* 外围设备恢复状态时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict;
/* 外围设备管理器状态发生改变后调用,比如外围设备打开蓝牙的时候 */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
/* 外围设备管理器添加了服务后调用,一般在这里进行广播服务 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service /* 添加的服务 */ error:(NSError *)error;/* 添加服务错误信息 */ /* 启动广播服务后调用 */ - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error;/* 启动服务错误信息 */ /* 外围设备恢复状态时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict;
外围设备管理器和中央设备进行交互的代理方法:
/* 中央设备订阅外围设备的特征时调用 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral /* 外围设备管理器 */
                  central:(CBCentral *)central /* 中央设备 */ didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;/* 订阅的特征 */ /* 中央设备取消订阅外围设备的特征时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral /* 外围设备管理器 */ central:(CBCentral *)central /* 中央设备 */ didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;/* 特征 */ /* 外围设备收到中央设备的写请求时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(CBATTRequest *)request;/* 写请求 */
/* 中央设备订阅外围设备的特征时调用 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral /* 外围设备管理器 */
                  central:(CBCentral *)central /* 中央设备 */ didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;/* 订阅的特征 */ /* 中央设备取消订阅外围设备的特征时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral /* 外围设备管理器 */ central:(CBCentral *)central /* 中央设备 */ didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;/* 特征 */ /* 外围设备收到中央设备的写请求时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(CBATTRequest *)request;/* 写请求 */
外围设备管理器CBPeripheralManager的常用对象方法:
/* 添加服务 */
- (void)addService:(CBService *)service;
/* 开启广播服务,dict设置设备信息 */
- (void)startAdvertising:(NSDictionary *)dict; /* 更新特征值,centrals为空,表示对所有连接的中央设备通知 */ - (void)updateValue:(NSData *)value /* 特征的特征值 */ forCharacteristic:(CBCharacteristic *)characteristic /* 特征 */ onSubscribedCentrals:(NSArray *)centrals;/* 需要通知更新特征值的中央设备 */
/* 添加服务 */
- (void)addService:(CBService *)service;
/* 开启广播服务,dict设置设备信息 */
- (void)startAdvertising:(NSDictionary *)dict; /* 更新特征值,centrals为空,表示对所有连接的中央设备通知 */ - (void)updateValue:(NSData *)value /* 特征的特征值 */ forCharacteristic:(CBCharacteristic *)characteristic /* 特征 */ onSubscribedCentrals:(NSArray *)centrals;/* 需要通知更新特征值的中央设备 */
下面是设备作为外围设备的实例:
#import "PeripheralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h> #define kPeripheralName @"Liuting's Device" //外围设备名称,自定义 #define kServiceUUID @"FFA0-FFB0" //服务的UUID,自定义 #define kCharacteristicUUID @"FFCC-FFDD" //特征的UUID,自定义 @interface PeripheralViewController ()<CBPeripheralManagerDelegate> @property (strong, nonatomic) CBPeripheralManager *peripheralManager;/* 外围设备管理器 */ @property (strong, nonatomic) NSMutableArray *centralM;/* 订阅的中央设备 */ @property (strong, nonatomic) CBMutableCharacteristic *characteristicM;/* 特征 */ @end @implementation PeripheralViewController - (void)viewDidLoad{ [super viewDidLoad]; self.centralM = [NSMutableArray array]; //创建外围设备管理器 self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil]; } #pragma mark - UI事件 /* 点击更新特征值 */ - (IBAction)changeCharacteristicValue:(id)sender { //特征值,这里是更新特征值为当前时间 NSString *valueStr = [NSString stringWithFormat:@"%@",[NSDate date]]; NSData *value = [valueStr dataUsingEncoding:NSUTF8StringEncoding]; //更新特征值 [self.peripheralManager updateValue:value forCharacteristic:self.characteristicM onSubscribedCentrals:nil]; } #pragma mark - 私有方法 /* 创建特征、服务并添加服务到外围设备 */ - (void)addMyService{ /*1.创建特征*/ //创建特征的UUID对象 CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID]; /* 创建特征 * 参数 * uuid:特征标识 * properties:特征的属性,例如:可通知、可写、可读等 * value:特征值 * permissions:特征的权限 */ CBMutableCharacteristic *characteristicM = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; self.characteristicM = characteristicM; //创建服务UUID对象 CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; //创建服务 CBMutableService *serviceM = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; //设置服务的特征 [serviceM setCharacteristics:@[characteristicM]]; //将服务添加到外围设备 [self.peripheralManager addService:serviceM]; } #pragma mark - CBPeripheralManager代理方法 /* 外围设备状态发生变化后调用 */ - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{ //判断外围设备管理器状态 switch (peripheral.state) { case CBPeripheralManagerStatePoweredOn: { NSLog(@"BLE已打开."); //添加服务 [self addMyService]; break; } default: { NSLog(@"此设备不支持BLE或未打开蓝牙功能,无法作为外围设备."); break; } } } /* 外围设备恢复状态时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict { NSLog(@"状态恢复"); } /* 外围设备管理器添加服务后调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error { //设置设备信息dict,CBAdvertisementDataLocalNameKey是设置设备名 NSDictionary *dict = @{CBAdvertisementDataLocalNameKey:kPeripheralName}; //开始广播 [self.peripheralManager startAdvertising:dict]; NSLog(@"向外围设备添加了服务并开始广播..."); } /* 外围设备管理器启动广播后调用 */ - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error { if (error) { NSLog(@"启动广播过程中发生错误,错误信息:%@",error.localizedDescription); return; } NSLog(@"启动广播..."); } /* 中央设备订阅特征时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"中心设备:%@ 已订阅特征:%@.",central,characteristic); //把订阅的中央设备存储下来 if (![self.centralM containsObject:central]) { [self.centralM addObject:central]; } } /* 中央设备取消订阅特征时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"中心设备:%@ 取消订阅特征:%@",central,characteristic); } /* 外围设备管理器收到中央设备写请求时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(CBATTRequest *)request { NSLog(@"收到写请求"); } @end
#import "PeripheralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h> #define kPeripheralName @"Liuting's Device" //外围设备名称,自定义 #define kServiceUUID @"FFA0-FFB0" //服务的UUID,自定义 #define kCharacteristicUUID @"FFCC-FFDD" //特征的UUID,自定义 @interface PeripheralViewController ()<CBPeripheralManagerDelegate> @property (strong, nonatomic) CBPeripheralManager *peripheralManager;/* 外围设备管理器 */ @property (strong, nonatomic) NSMutableArray *centralM;/* 订阅的中央设备 */ @property (strong, nonatomic) CBMutableCharacteristic *characteristicM;/* 特征 */ @end @implementation PeripheralViewController - (void)viewDidLoad{ [super viewDidLoad]; self.centralM = [NSMutableArray array]; //创建外围设备管理器 self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil]; } #pragma mark - UI事件 /* 点击更新特征值 */ - (IBAction)changeCharacteristicValue:(id)sender { //特征值,这里是更新特征值为当前时间 NSString *valueStr = [NSString stringWithFormat:@"%@",[NSDate date]]; NSData *value = [valueStr dataUsingEncoding:NSUTF8StringEncoding]; //更新特征值 [self.peripheralManager updateValue:value forCharacteristic:self.characteristicM onSubscribedCentrals:nil]; } #pragma mark - 私有方法 /* 创建特征、服务并添加服务到外围设备 */ - (void)addMyService{ /*1.创建特征*/ //创建特征的UUID对象 CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID]; /* 创建特征 * 参数 * uuid:特征标识 * properties:特征的属性,例如:可通知、可写、可读等 * value:特征值 * permissions:特征的权限 */ CBMutableCharacteristic *characteristicM = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; self.characteristicM = characteristicM; //创建服务UUID对象 CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; //创建服务 CBMutableService *serviceM = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; //设置服务的特征 [serviceM setCharacteristics:@[characteristicM]]; //将服务添加到外围设备 [self.peripheralManager addService:serviceM]; } #pragma mark - CBPeripheralManager代理方法 /* 外围设备状态发生变化后调用 */ - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{ //判断外围设备管理器状态 switch (peripheral.state) { case CBPeripheralManagerStatePoweredOn: { NSLog(@"BLE已打开."); //添加服务 [self addMyService]; break; } default: { NSLog(@"此设备不支持BLE或未打开蓝牙功能,无法作为外围设备."); break; } } } /* 外围设备恢复状态时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict { NSLog(@"状态恢复"); } /* 外围设备管理器添加服务后调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error { //设置设备信息dict,CBAdvertisementDataLocalNameKey是设置设备名 NSDictionary *dict = @{CBAdvertisementDataLocalNameKey:kPeripheralName}; //开始广播 [self.peripheralManager startAdvertising:dict]; NSLog(@"向外围设备添加了服务并开始广播..."); } /* 外围设备管理器启动广播后调用 */ - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error { if (error) { NSLog(@"启动广播过程中发生错误,错误信息:%@",error.localizedDescription); return; } NSLog(@"启动广播..."); } /* 中央设备订阅特征时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"中心设备:%@ 已订阅特征:%@.",central,characteristic); //把订阅的中央设备存储下来 if (![self.centralM containsObject:central]) { [self.centralM addObject:central]; } } /* 中央设备取消订阅特征时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"中心设备:%@ 取消订阅特征:%@",central,characteristic); } /* 外围设备管理器收到中央设备写请求时调用 */ - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(CBATTRequest *)request { NSLog(@"收到写请求"); } @end

四、设备作为中央设备

更多的时候,我们需要的是一个中央设备,外围设备不一定是iOS设备,所以上面外围设备的创建不一定会用到,比如外围设备是GPS导航仪、心率仪等,这些只要遵循BLE4.0的规范,中央设备就可以与之连接并寻找服务和订阅特征。

android手机蓝牙传文件给iphone手机失败_特征值_04

 

设备作为中央设备的创建步骤:
  1. 创建中央设备管理器对象CBCentralManager,设置代理
  2. 扫描外围设备,发现外围设备CBPeripheral进行连接,保持连接的外围设备
  3. 在连接外围设备成功的代理方法中,设置外围设备的代理,调用外围设备的对象方法寻找服务
  4. 查找外围设备的服务和特征,查找到可用特征,则读取特征数据。
  • 记住这里是外围设备对象CBPeripheral
    不是上面的外围设备管理器对象CBPeripheralManager
中央设备管理器的代理方法:
/* 中央设备管理器状态改变后调用,比如蓝牙的打开与关闭 */
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
/* 开启扫描后,中央设备管理器发现外围设备后调用 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral /* 外围设备 */ advertisementData:(NSDictionary *)advertisementData /* 设备信息 */ RSSI:(NSNumber *)RSSI; /* 信号强度 */ /* 中央设备管理器成功连接到外围设备后调用 */ - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;/* 外围设备 */ /* 中央设备管理器连接外围设备失败后调用 */ - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral /* 外围设备 */ error:(NSError *)error;/* 连接失败的错误信息 */
/* 中央设备管理器状态改变后调用,比如蓝牙的打开与关闭 */
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
/* 开启扫描后,中央设备管理器发现外围设备后调用 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral /* 外围设备 */ advertisementData:(NSDictionary *)advertisementData /* 设备信息 */ RSSI:(NSNumber *)RSSI; /* 信号强度 */ /* 中央设备管理器成功连接到外围设备后调用 */ - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;/* 外围设备 */ /* 中央设备管理器连接外围设备失败后调用 */ - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral /* 外围设备 */ error:(NSError *)error;/* 连接失败的错误信息 */
中央设备管理器的对象方法:
/* 扫描外围设备,可以指定含有指定服务的外围设备 */
- (void)scanForPeripheralsWithServices:(NSArray<CBUUID *> *)services options:(NSDictionary *)options; /* 停止扫描 */ - (void)stopScan; /* 连接外围设备 */ - (void)connectPeripheral:(CBPeripheral *)peripheral options:(NSDictionary *)options; /* 断开外围设备 */ - (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
/* 扫描外围设备,可以指定含有指定服务的外围设备 */
- (void)scanForPeripheralsWithServices:(NSArray<CBUUID *> *)services options:(NSDictionary *)options; /* 停止扫描 */ - (void)stopScan; /* 连接外围设备 */ - (void)connectPeripheral:(CBPeripheral *)peripheral options:(NSDictionary *)options; /* 断开外围设备 */ - (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
外围设备的代理方法【和上面的外围设备管理器代理不一样】
/**
 *  1.成功订阅外围设备的服务后调用,在该代理方法中寻找服务的特征
 *  @param peripheral 连接到的设备
 *  @param error       错误信息
 */
- (void)peripheral:(CBPeripheral *)peripheral 
        didDiscoverServices:(NSError *)error;
/** * 2.成功找到外围设备服务的特征后调用,在该代理方法中设置订阅方式 * @param peripheral 连接的设备 * @param service 拥有的服务 * @param error 错误信息 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error; /** * 3.外围设备读取到特征值就会调用 * @param peripheral 连接的设备 * @param characteristic 改变的特征 * @param error 错误信息 */ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error; /** * 4.向外围设备的特征对象写操作完成后调用 * 特别:当写操作为CBCharacteristicWriteWithoutResponse时不会调用 * @param peripheral 连接的设备 * @param characteristic 要写入的特征 * @param error 错误信息 */ - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
/**
 *  1.成功订阅外围设备的服务后调用,在该代理方法中寻找服务的特征
 *  @param peripheral 连接到的设备
 *  @param error       错误信息
 */
- (void)peripheral:(CBPeripheral *)peripheral 
        didDiscoverServices:(NSError *)error;
/** * 2.成功找到外围设备服务的特征后调用,在该代理方法中设置订阅方式 * @param peripheral 连接的设备 * @param service 拥有的服务 * @param error 错误信息 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error; /** * 3.外围设备读取到特征值就会调用 * @param peripheral 连接的设备 * @param characteristic 改变的特征 * @param error 错误信息 */ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error; /** * 4.向外围设备的特征对象写操作完成后调用 * 特别:当写操作为CBCharacteristicWriteWithoutResponse时不会调用 * @param peripheral 连接的设备 * @param characteristic 要写入的特征 * @param error 错误信息 */ - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
外围设备CBPeripheral的常用对象方法:
/* 寻找服务,传入的是服务的唯一标识CBUUID */
- (void)discoverServices:(NSArray<CBUUID *> *)services; /* 寻找指定服务下的特征,特征数组也是传入特征的唯一标识CBUUID */ - (void)discoverCharacteristics:(NSArray<CBUUID *> *)characteristics forService:(CBService *)service;/* 服务 */ /* 设置是否向特征订阅数据实时通知,YES表示会实时多次会调用代理方法读取数据 */ - (void)setNotifyValue:(BOOL)value forCharacteristic:(CBCharacteristic *)characteristic; /* 读取特征的数据,调用此方法后会调用一次代理方法读取数据 */ - (void)readValueForCharacteristic:(CBCharacteristic *)characteristic; /* 向特征写入数据,看type类型,决定调不调用写入数据后回调的代理方法 */ - (void)writeValue:(NSData *)value /* 写入数据 */ forCharacteristic:(CBCharacteristic *)characteristic /* 特征 */ type:(CBCharacteristicWriteType)type;/* 写入类型 */
/* 寻找服务,传入的是服务的唯一标识CBUUID */
- (void)discoverServices:(NSArray<CBUUID *> *)services; /* 寻找指定服务下的特征,特征数组也是传入特征的唯一标识CBUUID */ - (void)discoverCharacteristics:(NSArray<CBUUID *> *)characteristics forService:(CBService *)service;/* 服务 */ /* 设置是否向特征订阅数据实时通知,YES表示会实时多次会调用代理方法读取数据 */ - (void)setNotifyValue:(BOOL)value forCharacteristic:(CBCharacteristic *)characteristic; /* 读取特征的数据,调用此方法后会调用一次代理方法读取数据 */ - (void)readValueForCharacteristic:(CBCharacteristic *)characteristic; /* 向特征写入数据,看type类型,决定调不调用写入数据后回调的代理方法 */ - (void)writeValue:(NSData *)value /* 写入数据 */ forCharacteristic:(CBCharacteristic *)characteristic /* 特征 */ type:(CBCharacteristicWriteType)type;/* 写入类型 */
写入类型目前只有2种方式:
/* 写入类型,决定要不要调用代理方法 */
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) { CBCharacteristicWriteWithResponse = 0, //有回调的写入 CBCharacteristicWriteWithoutResponse //没回调的写入 };
/* 写入类型,决定要不要调用代理方法 */
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) { CBCharacteristicWriteWithResponse = 0, //有回调的写入 CBCharacteristicWriteWithoutResponse //没回调的写入 };
下面是设备作为中央设备的实例:
#import "CentralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h> #define kPeripheralName @"Liuting's Device" //外围设备名称 #define kServiceUUID @"FFA0-FFB0" //服务的UUID #define kCharacteristicUUID @"FFCC-FFDD" //特征的UUID @interface CentralViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate> @property (strong, nonatomic) CBCentralManager *centralManager;/* 中央设备管理器 */ @property (strong, nonatomic) NSMutableArray *peripherals;/* 连接的外围设备 */ @end @implementation CentralViewController #pragma mark - UI事件 - (void)viewDidLoad{ [super viewDidLoad]; self.peripherals = [NSMutableArray array]; //创建中心设备管理器并设置当前控制器视图为代理 self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; } #pragma mark - CBCentralManager代理方法 /* 中央设备管理器状态更新后调用 */ - (void)centralManagerDidUpdateState:(CBCentralManager *)central{ switch (central.state) { case CBPeripheralManagerStatePoweredOn: NSLog(@"BLE已打开."); //扫描外围设备 [central scanForPeripheralsWithServices:nil options:nil]; break; default: NSLog(@"此设备不支持BLE或未打开蓝牙功能,无法作为中央设备."); break; } } /* * 发现外围设备调用 * @param central 中央设备管理器 * @param peripheral 外围设备 * @param advertisementData 设备信息 * @param RSSI 信号质量(信号强度) */ - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { NSLog(@"发现外围设备..."); //连接指定的外围设备,匹配设备名 if ([peripheral.name isEqualToString:kPeripheralName]) { //添加保存外围设备,因为在此方法调用完外围设备对象就会被销毁 if(![self.peripherals containsObject:peripheral]){ [self.peripherals addObject:peripheral]; } NSLog(@"开始连接外围设备..."); [self.centralManager connectPeripheral:peripheral options:nil]; } } /* 中央设备管理器成功连接到外围设备后调用 */ - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { NSLog(@"连接外围设备成功!"); //停止扫描 [self.centralManager stopScan]; //设置外围设备的代理为当前视图控制器 peripheral.delegate = self; //外围设备开始寻找服务 [peripheral discoverServices:@[[CBUUID UUIDWithString:kServiceUUID]]]; } /* 中央设备管理器连接外围设备失败后调用 */ - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"连接外围设备失败!"); } #pragma mark - CBPeripheral 代理方法 /* 外围设备寻找到服务后调用 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { NSLog(@"已发现可用服务..."); //遍历查找到的服务 CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID]; for (CBService *service in peripheral.services) { if([service.UUID isEqual:serviceUUID]){ //外围设备查找指定服务中的特征,characteristics为nil,表示寻找所有特征 [peripheral discoverCharacteristics:nil forService:service]; } } } /* 外围设备寻找到特征后调用 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { NSLog(@"已发现可用特征..."); //遍历服务中的特征 CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID]; if ([service.UUID isEqual:serviceUUID]) { for (CBCharacteristic *characteristic in service.characteristics) { if ([characteristic.UUID isEqual:characteristicUUID]) { //情景一:通知 /* 找到特征后设置外围设备为已通知状态(订阅特征): * 调用此方法会触发代理方法peripheral:didUpdateValueForCharacteristic:error: * 调用此方法会触发外围设备管理器的订阅代理方法 */ [peripheral setNotifyValue:YES forCharacteristic:characteristic]; //情景二:读取 //调用此方法会触发代理方法peripheral:didUpdateValueForCharacteristic:error: //[peripheral readValueForCharacteristic:characteristic]; } } } } /* 外围设备读取到特征值后调用 */ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (characteristic.value) { NSString *value = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]; NSLog(@"读取到特征值:%@",value); }else{ NSLog(@"未发现特征值."); } } @end
#import "CentralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h> #define kPeripheralName @"Liuting's Device" //外围设备名称 #define kServiceUUID @"FFA0-FFB0" //服务的UUID #define kCharacteristicUUID @"FFCC-FFDD" //特征的UUID @interface CentralViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate> @property (strong, nonatomic) CBCentralManager *centralManager;/* 中央设备管理器 */ @property (strong, nonatomic) NSMutableArray *peripherals;/* 连接的外围设备 */ @end @implementation CentralViewController #pragma mark - UI事件 - (void)viewDidLoad{ [super viewDidLoad]; self.peripherals = [NSMutableArray array]; //创建中心设备管理器并设置当前控制器视图为代理 self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; } #pragma mark - CBCentralManager代理方法 /* 中央设备管理器状态更新后调用 */ - (void)centralManagerDidUpdateState:(CBCentralManager *)central{ switch (central.state) { case CBPeripheralManagerStatePoweredOn: NSLog(@"BLE已打开."); //扫描外围设备 [central scanForPeripheralsWithServices:nil options:nil]; break; default: NSLog(@"此设备不支持BLE或未打开蓝牙功能,无法作为中央设备."); break; } } /* * 发现外围设备调用 * @param central 中央设备管理器 * @param peripheral 外围设备 * @param advertisementData 设备信息 * @param RSSI 信号质量(信号强度) */ - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { NSLog(@"发现外围设备..."); //连接指定的外围设备,匹配设备名 if ([peripheral.name isEqualToString:kPeripheralName]) { //添加保存外围设备,因为在此方法调用完外围设备对象就会被销毁 if(![self.peripherals containsObject:peripheral]){ [self.peripherals addObject:peripheral]; } NSLog(@"开始连接外围设备..."); [self.centralManager connectPeripheral:peripheral options:nil]; } } /* 中央设备管理器成功连接到外围设备后调用 */ - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { NSLog(@"连接外围设备成功!"); //停止扫描 [self.centralManager stopScan]; //设置外围设备的代理为当前视图控制器 peripheral.delegate = self; //外围设备开始寻找服务 [peripheral discoverServices:@[[CBUUID UUIDWithString:kServiceUUID]]]; } /* 中央设备管理器连接外围设备失败后调用 */ - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"连接外围设备失败!"); } #pragma mark - CBPeripheral 代理方法 /* 外围设备寻找到服务后调用 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { NSLog(@"已发现可用服务..."); //遍历查找到的服务 CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID]; for (CBService *service in peripheral.services) { if([service.UUID isEqual:serviceUUID]){ //外围设备查找指定服务中的特征,characteristics为nil,表示寻找所有特征 [peripheral discoverCharacteristics:nil forService:service]; } } } /* 外围设备寻找到特征后调用 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { NSLog(@"已发现可用特征..."); //遍历服务中的特征 CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID]; if ([service.UUID isEqual:serviceUUID]) { for (CBCharacteristic *characteristic in service.characteristics) { if ([characteristic.UUID isEqual:characteristicUUID]) { //情景一:通知 /* 找到特征后设置外围设备为已通知状态(订阅特征): * 调用此方法会触发代理方法peripheral:didUpdateValueForCharacteristic:error: * 调用此方法会触发外围设备管理器的订阅代理方法 */ [peripheral setNotifyValue:YES forCharacteristic:characteristic]; //情景二:读取 //调用此方法会触发代理方法peripheral:didUpdateValueForCharacteristic:error: //[peripheral readValueForCharacteristic:characteristic]; } } } } /* 外围设备读取到特征值后调用 */ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (characteristic.value) { NSString *value = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]; NSLog(@"读取到特征值:%@",value); }else{ NSLog(@"未发现特征值."); } } @end

五、蓝牙后台运行

除非去申请后台权限,否则 app 都是只在前台运行的,程序在进入后台不久便会切换到挂起状态。挂起后,程序将无法再接收任何蓝牙事件。

中央设备管理器连接外围设备的方法中的options属性,可以设置如下字典值:
  • CBConnectPeripheralOptionNotifyOnConnectionKey
    在连接成功后,程序被挂起,给出系统提示。
  • CBConnectPeripheralOptionNotifyOnDisconnectionKey
    在程序挂起,蓝牙连接断开时,给出系统提示。
  • CBConnectPeripheralOptionNotifyOnNotificationKey
    在程序挂起后,收到 peripheral 数据时,给出系统提示。
设置蓝牙后台模式:
  • 添加info.plist字段Required background nodes
  • 在该字段下添加字符串值:
  • App communicates using CoreBluetooth:表示支持设备作为中央设备后台运行
  • App shares data using CoreBluetooth:表示支持设备作为外围设备后台运行

android手机蓝牙传文件给iphone手机失败_代理方法_05

 

设备作为中央设备的后台运行和前台运行区别:
  • 会将发现的多个外围设备的广播数据包合并为一个事件,然后每找到一个外围设备都会调用发现外围设备的代理方法
  • 如果全部的应用都在后台搜索外围设备,那么每次搜索的时间间隔会更大。这会导致搜索到外围设备的时间变长
设备作为外围设备的后台运行和前台运行区别:
  • 在广播时,广播数据将不再包含外围设备的名字
  • 外围设备只能被明确标识了搜索服务UUID的iOS设备找到
  • 如果所有应用都在后台发起广播,那么发起频率会降低