CoreBluetooth Framework
本文主要是介绍iphone、ipad设备之间相互通信的蓝牙知识。
1.相关的类
在CoreBluetooth框架中,有两个主要的角色:周边和中央(Peripheral and Central),整个框架都是围绕这两个主要角色设计的,他两之间有一系列的回调交换数据。
周边(Peripheral)是生成或者保存了数据的设备,中央(Central)是使用这些数据的设备。所有可用的iOS设备可以作为周边(Peripheral),也可以作为中央(Central),但不可以同时既是周边也是中央.
周边和中央这两个角色在CoreBluetooth框架中是用两个类来表示的,CBPeripheralManager这个类代表周边,CBCentralManager 这个类代表中央。
在中央这边,一个CBPeripheral 对象代表着相应的和中央连接着的周边;同样的,在周边这边,一个CBCentral 对象代表着相应的和周边连接着的中央。
你可以认为周边是一个广播数据的设备,他广播到外部世界说他这儿有数据,并且也说明了能提供的服务。另一边,中央开始扫描附近有没有服务,如果中央发现了想要的服务,然后中央就会请求连接周边,一旦连接建立成功,两个设备之间就开始交换传输数据了。
除了中央和周边,我们还要考虑他俩交换的数据结构。这些数据在服务中被结构化,每个服务由不同的特征(Characteristics)组成,特征是包含一个单一逻辑值的属性类型。如果你去http://developer.bluetooth.org链接,你可以找到标准服务和特征的列表。
在中央这边,CBService 类代表服务,CBCharacteristic 类代表特征。在周边这边,CBMutableService 类代表服务,CBMutableCharacteristic 类代表特征
CBUUID 和CBATTRequest 是两个苹果公司给我们提供的帮助类,以便于开发者更简单地操作数据.
2.周边相关
1>创建周边
2>设置这个周边所提供的服务
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
第一个参数是设置代理,这儿就是view controller,第二个参数设置为nil,因为Peripheral Manager将Run在主线程中。如果你想用不同的线程做更加复杂的事情,你需要创建一个队列(queue)并将它放在这儿。
一旦Peripheral Manager被创建,我们应该及时地检查它的状态,看正在运行App的这个设备是不是支持BLE标准。所以要实现以下这个代理方法,在这儿你可以做更复杂的一些事情和友好地提醒用户,如果设备不支持BLE。
实现代理方法
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
if(peripheral.state != CBPeripheralManagerStatePoweredOn){
NSLog(@“不支持BLE”);
return ;
}
NSLog(@“peripheral manager powered on”);
//服务和特征
//每一个服务和特征都需要一个UUID去标识,UUID可以用uuidgen命令生成。
//使用该命令两次,一个给服务用的,一个给特征用的
static NSString * const kServiceUUID = @"312700E2-E798-4D5C-8DCF-49908332DF9F";
static NSString * const kCharacteristicUUID = @"FFA28CDE-6525-4489-801C-1C060CAC9767";
//特征
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:kCharacteristicUUID
properties:CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
//服务
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:kServiceUUID primary:YES];
//每一个服务可以多个特征
transferService.characteristics = @[self.transferCharacteristic];
//把服务添加到周边管理者(Peripheral Manager)是用于发布服务
[self.peripheralManager addService:transferService];
}
3>广播这个服务
[self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
//可以编辑好要发送的内容,再调用这个方法。
//也可以在下面的代理方法中调用
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *){
if(error != nil){
NSLog(@“fail to start add service, reason %@”, error.localizedDescription);
return ;
}
[peripheral startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
}
当周边管理者开始广播服务,他的代理接收-peripheralManagerDidStartAdvertising:error: 消息,并且当中央预定了这个服务,他的代理接收 -peripheralManager:central:didSubscribeToCharacteristic:消息,这儿是你给中央生成动态数据的地方。
4>向中央发送数据
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
NSData *dataToSend = [@“Hello” dataUsingEncoding:NSUTF8EncodingString];
NSData *chunk = [NSData dataWithBytes:dataToSend.bytes length:dataToSend.length];
BOOL didSend = [peripheral updateValue:chunk forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
if(!didSend){
NSLog(“fail to update value”);
return ;
}
//make sure what data you have updated
NSString *stringFromData = [[NSString alloc] initWithData:chunk encoding:NSUTF8StringEncoding];
NSLog(@"Sent: %@", stringFromData);
}
5>停止广播
[self.peripheralManager stopAdvertising];
一般在- (void)viewWillDisappear:(BOOL)animated中调用
3.中央相关
1>创建中央
2>寻找指定服务,找到后进行连接.
3>连接成功后,停止寻找
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
和创建周边时一样,第一个参数代表CBCentralManager 代理,在这个例子中就是这个view controller;第二个参数设置为nil,因为Peripheral Manager将Run在主线程中。如果你想用不同的线程做更加复杂的事情,你需要创建一个队列(queue)并将它放在这儿。
当Central Manager被初始化,我们要检查它的状态,以检查运行这个App的设备是不是支持BLE。实现以下的代理方法:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
if(central.state != CBCentralStatePowedOn){
NSLog(@“不支持BLE”);
return ;
}
NSLog(@"Scanning started");
//如果将第一次参数设置为nil,Central Manager就会开始找寻所有的服务
[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
}
//一旦一个周边在寻找的时候被发现,中央的代理会收到以下回调:
//这个调用通知Central Manager代理(在这个例子中就是view controller),一个附带着广播数据和信号质量(RSSI-Received Signal Strength Indicator)的周边被发现。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@“Discovered %@ at %@”, peripheral.name, RSSI);
if(self.peripheral != peripheral){
//连接
[central connectPeripheral:peripheral options:nil];
}
}
//基于连接的结果,代理(这个例子中是view controller)会接收centralManager:didFailToConnectPeripheral:error:或者centralManager:didConnectPeripheral:。
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@“fail to connect to %@, reason %@”, peripheral, error.localizedDescription);
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"Peripheral Connected");
// Stop scanning
[self.centralManager stopScan];
NSLog(@"Scanning stopped");
//清空数据
[self.data setLength:0];
peripheral.delegate = self;
// Search only for services that match our UUID
[peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];
}
上面周边去寻找服务,如果找到调用下面的这个方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error) {
NSLog(@"Error discovering services: %@", [error localizedDescription]);
return;
}
// Loop through the newly filled peripheral.services array, just in case there's more than one.
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service];
}
}
//现在,如果一个特征被发现,周边代理会接收-peripheral:didDiscoverCharacteristicsForService:error:。现在,一旦特征的值被更新,用-setNotifyValue:forCharacteristic:,周边被请求通知它的代理。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
// Deal with errors (if any)
if (error) {
NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
// 检查特征
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
//打开通知
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
}
//周边更改特征的通知状态
//YES的话,就会发通知
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
NSLog(@"Error changing notification state: %@", error.localizedDescription);
}
//如果特征不对,退出
if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]){
return;
}
//通知开始
if (characteristic.isNotifying) {
NSLog(@"Notification began on %@", characteristic);
}
// 通知失败
else {
//断开连接周边
NSLog(@"Notification stopped on %@. Disconnecting", characteristic);
[self.centralManager cancelPeripheralConnection:peripheral];
}
}
//周边发数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
return;
}
//characteristic.value存放带过来的数据
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
[self.data appendData:characteristic.value];
//停止发送数据
//如果不停止,会一直发回调这个方法,发数据
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
//断开连接
[self.centralManager cancelPeripheralConnection:peripheral]
}
//去掉连接调用这个方法
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"Peripheral Disconnected");
self.discoveredPeripheral = nil;
//重新寻找
[self scan];
}