一.关键词
- 中央设备:中央设备用于管理外设,一个中央设备可以同时连接管理多个外设。
- 中心设备管理(CBCentralManager):管理外设的工具,提供扫描连接外设等方法。
- 外设(CBPeripheral):如蓝牙耳机,音响,打印机等,外设主动广播数据(advertisementData)描述其设备的基本信息,如此设备是什么,叫什么,提供什么样的服务(CBService)(类似于电台广播,不管是否有用户,都会广播信号以描述其电台信息)。
- 外设管理(CBPeripheralManager):管理外设,给外设添加服务
- 服务(CBService):一个外设至少有一个服务,一个服务包含多个特征,每个服务由唯一的一个UUID确定。
- 特征(CBCharacteristic):设备间通讯的最小单元或者说是一个通讯通道,每个特征由唯一的一个UUID确定。
- 特征值:特征存储的数据。
- 特征属性:用于描述特征的权限,如读,写等,以下是oc特征属性的枚举:
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(NA, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
};
- 广播数据(advertisementData):用于描述外设基本信息
二.工作流程
外设工作流程
- 建立外设管理(CBPeripheralManager):需要实现CBPeripheralManagerDelegate协议。
- 新建添加服务(CBMutableService)设置服务特征(CBCharacteristic),后续通过特征通讯。
- 广播服务。
中心设备工作流程
- 建立中心设备管理(CBCentralManager):需实现CBCentralManagerDelegate,CBPeripheralDelegate协议
- 扫描外设。
- 连接外设。
- 扫描外设服务。
- 选择使用外设服务。
- 获取服务特征(即获取了通讯通道)。
- 选择使用特征进行通讯。
通讯流程
三.上代码
外设
//
// BLECBPeripheral.h
// BLE
//
// Created by chenguibang on 2017/3/15.
// Copyright © 2017年 chenguibang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
@interface BLECBPeripheral : NSObject<CBPeripheralManagerDelegate>
@property(nonatomic,strong) CBPeripheralManager *peripheralManager;
+(id)shared;
-(void)setup;
-(void)addSe;
-(void)adv;
@end
//
// BLECBPeripheral.m
// BLE
//
// Created by chenguibang on 2017/3/15.
// Copyright © 2017年 chenguibang. All rights reserved.
//
#import "BLECBPeripheral.h"
#define kPeripheralName @"Kenshin Cui's Device" //外围设备名称
#define kServiceUUID @"C4FB2349-72FE-4CA2-94D6-1F3CB16331EE" //服务的UUID
#define kCharacteristicUUID @"6A3E4B28-522D-4B3B-82A9-D5E2004534FC" //特征的
@implementation BLECBPeripheral
+(id)shared{
static dispatch_once_t onceToken;
static BLECBPeripheral *per = nil;
dispatch_once(&onceToken, ^{
per = [[BLECBPeripheral alloc]init];
});
return per;
}
-(void)setup{
self.peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
}
-(void)addSe{
CBUUID *characteristicUUID=[CBUUID UUIDWithString:kCharacteristicUUID];
CBMutableCharacteristic *characteristicM=[[CBMutableCharacteristic alloc]initWithType:characteristicUUID properties:CBCharacteristicPropertyNotify|CBCharacteristicPropertyWriteWithoutResponse|CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable|CBAttributePermissionsWriteable|CBAttributePermissionsReadEncryptionRequired|CBAttributePermissionsWriteEncryptionRequired];
CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];
//创建服务
CBMutableService *serviceM=[[CBMutableService alloc]initWithType:serviceUUID primary:YES];
//设置服务的特征
[serviceM setCharacteristics:@[characteristicM]];
/*将服务添加到外围设备*/
[self.peripheralManager addService:serviceM];
}
-(void)adv{
//添加服务后开始广播
NSDictionary *dic=@{CBAdvertisementDataLocalNameKey:kPeripheralName};//广播设置
[self.peripheralManager startAdvertising:dic];//开始广播
}
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
switch (peripheral.state) {
case CBManagerStatePoweredOn:
NSLog(@"BLE已打开.");
break;
default:
NSLog(@"BLE已打开.异常");
break;
}
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
NSLog(@"添加服务成功");
}
-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
NSLog(@"广播成功");
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"有服务订阅特征值");
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"有服务取消订阅特征值");
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
NSLog(@"收到读请求");
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests{
NSLog(@"收到写请求");
}
@end
中央设备
//
// BLETool.h
// BLEOCChat
//
// Created by chenguibang on 2017/3/14.
// Copyright © 2017年 chenguibang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
typedef void(^ErrorBlcok)(NSError* error);
@interface BLETool : NSObject<CBCentralManagerDelegate,CBPeripheralDelegate>
@property(nonatomic,strong)CBCentralManager *centerManager;
@property(nonatomic,strong)NSMutableArray<CBPeripheral*> *peripherals;
@property(nonatomic,strong)NSMutableArray<CBService*> *sevices;
@property(nonatomic,strong)NSMutableArray<CBCharacteristic *>*characters;
@property(nonatomic,copy)ErrorBlcok errorBlcok;
+(id)shared;
-(void)setup;
-(void)scanfPer:(NSUInteger)timeout;
-(void)stopScanf;
-(void)connectTo:(CBPeripheral*)peripheral reuslt:(void(^)())result;
-(void)disconnect:(CBPeripheral*)peripheral;
-(void)discoverServicesFor:(CBPeripheral*)peripheral;
-(void)discoverCharactersFor:(CBService *)service In:(CBPeripheral*)peripheral;
-(void)write:(CBPeripheral*)peripheral forCharacteristic:(CBCharacteristic *)characteristic value:(NSData *)data;
@end
//
// BLETool.m
// BLEOCChat
//
// Created by chenguibang on 2017/3/14.
// Copyright © 2017年 chenguibang. All rights reserved.
//
#import "BLETool.h"
#define findMeServiceUUID [CBUUID UUIDWithString:@"1802"]
#define genericAccessServiceUUID [CBUUID UUIDWithString:@"1800"]
#define linkLossServiceUUID [CBUUID UUIDWithString:@"1803"]
#define deviceNameCharacteristicUUID [CBUUID UUIDWithString:@"2A00"]
#define alertLevelCharacteristicUUID [CBUUID UUIDWithString:@"2A06"]
#define BT_ALERT_LEVEL_NONE 0
#define BT_LEVEL_MILD 1
#define BT_ALERT_LEVEL_HIGH 2
#define BT_ALERT_LEVEL_RESERVED(LVL) LVL
@interface BLETool(){
// CBCentralManager *centerManager;
}
@end
@implementation BLETool
+(id)shared{
static dispatch_once_t onceToken;
static BLETool *tool;
dispatch_once(&onceToken, ^{
tool = [[BLETool alloc]init];
[tool setup];
});
return tool;
}
-(void)setup{
// 1.建立中心管理
self.centerManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
self.centerManager.delegate = self;//设置代理
self.peripherals = [NSMutableArray array];
self.sevices = [NSMutableArray array];
self.characters = [NSMutableArray array];
}
-(void)scanfPer:(NSUInteger)timeout{
if (self.centerManager.isScanning) {
[[self mutableArrayValueForKey:@"peripherals"]removeAllObjects];
[self.centerManager stopScan];
}
// 2.扫描外设
[self.centerManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:[NSNumber numberWithInt:1],CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithInt:1]}];
}
-(void)stopScanf{
[self.centerManager stopScan];
}
-(void)connectTo:(CBPeripheral*)peripheral reuslt:(void(^)())result{
if (self.centerManager.isScanning) {
[self.centerManager stopScan];
}
// 3.连接外设
if (peripheral.state != CBPeripheralStateDisconnected) {
NSLog(@"该设备暂时不能连接");
self.errorBlcok([NSError errorWithDomain:NSURLErrorDomain code:1 userInfo:@{@"msg":@"外设暂时状态不可连接"}]);
}else{
[self.centerManager connectPeripheral:peripheral options:nil];
}
}
-(void)disconnect:(CBPeripheral *)peripheral{
[self.centerManager cancelPeripheralConnection:peripheral];
}
-(void)discoverServicesFor:(CBPeripheral*)peripheral{
NSMutableArray *sevices = [self mutableArrayValueForKeyPath:@"sevices"];
[sevices removeAllObjects];
peripheral.delegate = self;
[peripheral discoverServices:nil];
}
-(void)write:(CBPeripheral*)peripheral forCharacteristic:(CBCharacteristic *)characteristic value:(NSData *)data{
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
}
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBManagerStateUnknown:
break;
case CBManagerStateResetting:
break;
case CBManagerStateUnsupported:
break;
case CBManagerStateUnauthorized:
break;
case CBManagerStatePoweredOff:
break;
case CBManagerStatePoweredOn:
// [central scanForPeripheralsWithServices:nil options:nil];
break;
default:
break;
}
NSLog(@"scanState : %ld \n" , (long)central.state);
}
-(void)discoverCharactersFor:(CBService *)service In:(CBPeripheral*)peripheral;{
NSMutableArray *characters = [self mutableArrayValueForKeyPath:@"characters"];
[characters removeAllObjects];
[peripheral discoverCharacteristics:nil forService:service];
}
//advertisementData 外设主动发送的广播数据 RSSI 信号强度单位是dbm
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
NSMutableArray *peripherals = [self mutableArrayValueForKeyPath:@"peripherals"];
if (![self.peripherals containsObject:peripheral]) {
[peripherals addObject:peripheral];
// [[self mutableArrayValueForKeyPath:@"peripherals"] addObject:peripheral];
}else{
NSUInteger index = [peripherals indexOfObject:peripheral];
[peripherals replaceObjectAtIndex:index withObject:peripheral];
// [[self mutableArrayValueForKeyPath:@"peripherals"] replaceObjectAtIndex:[self.peripherals indexOfObject:peripheral] withObject:peripheral];
}
NSLog(@"scan DisconnectPeripherals: %@ \n",self.peripherals);
// NSLog(@"uuid : %@\n",peripheral.identifier.UUIDString);
}
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"连接成功");
self.errorBlcok([NSError errorWithDomain:NSURLErrorDomain code:9999 userInfo:@{@"msg":@"连接成功"}]);
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
self.errorBlcok([NSError errorWithDomain:NSURLErrorDomain code:9999 userInfo:@{@"msg":@"连接失败"}]);
}
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
NSLog(@"发现服务");
if (!error) {
NSMutableArray *sevices = [self mutableArrayValueForKeyPath:@"sevices"];
[sevices addObjectsFromArray:peripheral.services];
for (CBService *service in peripheral.services) {
NSLog(@"%@",service.UUID);
[peripheral discoverCharacteristics:nil forService:service];
}
}else{
self.errorBlcok([NSError errorWithDomain:NSURLErrorDomain code:9999 userInfo:@{@"msg":@"查找失败"}]);
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error{
NSMutableArray *characters = [self mutableArrayValueForKeyPath:@"characters"];
[characters addObjectsFromArray:service.characteristics];
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
}
@end