基礎(chǔ)用法先說明一下,自己創(chuàng)建一個(gè)叫BluetoothManager的單例,在頭文件BluetoothManager.h
導(dǎo)入系統(tǒng)藍(lán)牙框架,分別創(chuàng)建幾個(gè)數(shù)組,在使用時(shí)調(diào)用:
#import <CoreBluetooth/CoreBluetooth.h>
@interface BluetoothManager : NSObject
@property (nonatomic, strong) NSMutableArray *findPeripherals; //查找到設(shè)備(不包含用戶列表里的設(shè)備)
@property (nonatomic, strong) NSMutableArray *connectPeripherals; //連接上的設(shè)備
@property (nonatomic, strong) CBCentralManager *cbCentralManager;
/**
單例實(shí)現(xiàn)方法
*/
+ (BluetoothManager *)share;
@end
在.m文件里面創(chuàng)建單例,并且添加藍(lán)牙模塊的協(xié)議,并為簽訂代理:
#import "BluetoothManager.h"
@interface BluetoothManager()<CBCentralManagerDelegate,CBPeripheralDelegate,CBPeripheralManagerDelegate>
@property (nonatomic, strong) NSMutableArray *offlineperipherals;
@property (nonatomic, strong) NSMutableArray *tempLists;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBPeripheralManager *peripheralmanager;
@implementation BlueInfo
@end
@implementation BluetoothManager
//單例生成
+(BluetoothManager *)share {
static BluetoothManager *shareInstance_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance_ = [[self alloc] init];
});
return shareInstance_;
}
//初始化中心設(shè)備CBCentraManager(管理者)和CBPeripheralManager(外設(shè)管理者)
-(id) init {
self = [super init];
if (self) {
_cbCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
_peripheralmanager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
}
return self;
}
-(void) setDelegate:(id<BluetoothManagerDelegate>)delegate
{
if (!_delegate) {
_delegate = delegate;
}
}
下面是中心設(shè)備的代理方法,步驟基本要按照下面的走:
1.檢查設(shè)備藍(lán)牙服務(wù)是否打開(可用),關(guān)于中心設(shè)備的狀態(tài)central.state
有以下6個(gè)枚舉
- (void) centralManagerDidUpdateState:(CBCentralManager *)central{
BOOL state = false;
NSString *msg = @"";
switch (central.state) {
case CBCentralManagerStatePoweredOn:{
msg = @"Bluetooth is currently powered on";
state = YES;
}
break;
case CBCentralManagerStatePoweredOff:{
msg = @"Bluetooth is currently powered off.";
}
break;
case CBCentralManagerStateUnauthorized:{
msg = @"The application is not authorized to use the Bluetooth Low Energy Central/Client role.";
}
break;
case CBCentralManagerStateUnsupported:{
msg = @"The platform doesn't support the Bluetooth Low Energy Central/Client role.";
}
break;
case CBCentralManagerStateResetting:{
msg = @"The connection with the system service was momentarily lost, update imminent.";
}
break;
case CBCentralManagerStateUnknown:{
msg = @"State unknown, update imminent.";
}
break;
}
}
NSLog(@"%@",msg);
只有在CBCentralManagerStatePoweredOn
的狀態(tài)下,才可以進(jìn)行下步操作——其他狀態(tài),可以根據(jù)自身情況做相應(yīng)的提示高告訴用戶溉旋;
2.搜索中心設(shè)備周邊的外設(shè):
在上面代理方法的CBCentralManagerStatePoweredOn
的case
下寫搜索方法,可以直接把系統(tǒng)方法寫到該case
,此處把系統(tǒng)的搜索方法封裝到一個(gè)自定義方法,代碼更整潔(創(chuàng)建布爾值state
,用于標(biāo)記設(shè)備藍(lán)牙成功與否)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
BOOL state = false;
NSString *msg = @"";
switch (central.state) {
case CBCentralManagerStatePoweredOn:{
msg = @"Bluetooth is currently powered on";
[self scanDevice];//搜索設(shè)備方法
state = YES;
}
break;
// ...... 下面的狀態(tài)省略......
}
}
下面是搜索設(shè)備封裝方法:
-(void) scanBlueServes {
NSDictionary *optionsDict = @{CBCentralManagerScanOptionAllowDuplicatesKey:@YES};
[self.cbCentralManager scanForPeripheralsWithServices:nil options:optionsDict];
//CBUUID *uuid = [CBUUID UUIDWithString:@"FFF0"];
//[self.cbCentralManager scanForPeripheralsWithServices:@[uuid] options:optionsDict];
}
方法中,需要注意的是:
- 注釋的那部分代碼是用于指定搜索設(shè)備特定服務(wù)的uuid,nil則無差別搜索;
- 如果想要改變默認(rèn)行為矛纹,可以指定
CBCentralManagerScanOptionAllowDuplicatesKey
作為掃描選項(xiàng)盖淡。此時(shí)愉粤,central管理器會(huì)在每次收到peripheral端的廣告包時(shí)都觸發(fā)一個(gè)事件遣蚀。在某些情況下關(guān)閉默認(rèn)行為很有用處孤紧,但記住指定CBCentralManagerScanOptionAllowDuplicatesKey
掃描選項(xiàng)不利于電池的壽命及程序性能匣屡。因此潘飘,只在需要的時(shí)候使用這個(gè)選項(xiàng)以完成特定的任務(wù)。
原文如下:
- A Boolean value that specifies whether the scan should run without duplicate filtering.
The value for this key is an NSNumber object. If true, filtering is disabled and a discovery event is generated each time the central receives an advertising packet from the peripheral. Disabling this filtering can have an adverse effect on battery life and should be used only if necessary. If false, multiple discoveries of the same peripheral are coalesced into a single discovery event. If the key is not specified, the default value is false.
3.檢測(cè)到外設(shè)后,進(jìn)入下面的代理方法,連接設(shè)備(這里開始加入了一些個(gè)人的方法)
- 當(dāng)搜索到設(shè)備進(jìn)入這個(gè)代理方法:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
,搜索到多個(gè)設(shè)備,則此方法多次調(diào)用;
由于iOS端禁止獲取藍(lán)牙設(shè)備Mac地址,所以和硬件工程師協(xié)商好,將Mac地址放到指定廣播包里,從此方法中advertisementData
以NSData傳過來,而這個(gè)key是蘋果指定的,不能亂寫;
(余下的除了'central'分別是搜索到的設(shè)備:peripheral
瘤袖,設(shè)備信號(hào):RSSI
(數(shù)據(jù)類型參考方法)). - 我們可通過外設(shè)的名字
peripheral.name
或者剛剛說的藍(lán)牙Mac地址來過濾設(shè)備,這就需要和硬件開發(fā)協(xié)商了,這里還做了一步處理,將已經(jīng)被其他設(shè)備連接的外設(shè)通過連接狀態(tài)的判斷也過濾掉
if (peripheral.state == CBPeripheralStateConnected) {
return;
}
- 過濾之后,將設(shè)備添加到
self.findPeripherals
數(shù)組中傳到界面(通過UITableView
)展示出來:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
if (peripheral.state == CBPeripheralStateConnected) {
return;
}
NSData *data = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
if ((peripheral.name && ([peripheral.name hasPrefix:@"BBCare"]) || [self checkMacAddress]){
[self.findPeripherals addObject:peripheral];
}
4.通過界面方法,實(shí)現(xiàn)藍(lán)牙類方法;
一般情況下,我們(其實(shí)是我)會(huì)在tableView通過BluetoothManager
單例獲取到findPeripherals數(shù)組,展示到界面,然后在tableview的協(xié)議方法:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
里面獲取要連接的設(shè)備peripheral
,傳入如下方法實(shí)現(xiàn)連接設(shè)備:
[[BluetoothManager share].cbCentralManager connectPeripheral:peripheral options:nil];
5.連接后的處理(劃重點(diǎn)了!)
- 連接失敗:
有時(shí)候,藍(lán)牙搜索到設(shè)備時(shí)設(shè)備還在,點(diǎn)擊連接時(shí)設(shè)備不在附近或沒電(就不詳細(xì)說了),類似這些情況,連接會(huì)失敗,會(huì)進(jìn)入如下方法,NSLog一下,會(huì)打印出具體的失敗原因:
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@">>>連接到名稱為(%@)的設(shè)備-失敗,原因:%@",[peripheral name],[error localizedDescription]);
}
- 連接成功后,簽訂外設(shè)代理方法
別以為連接成功了數(shù)據(jù)就會(huì)自己找你了,并不是,后面還有不少的坑呢.
首先,連接成功后,會(huì)進(jìn)入另外一個(gè)代理方法",這里我把連接的設(shè)備添加進(jìn)self.connectperipherals
數(shù)組以備不時(shí)之需,然后該設(shè)備需簽訂peripheral的代理,然后開始一起去了解外設(shè)的代理方法吧
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@">>>連接到設(shè)備(%@ >> %@)-- 成功",peripheral.name,peripheral.identifier.UUIDString);
if (self.connectperipherals == nil) {
self.connectperipherals = [NSMutableArray array];
}
if (![self.connectperipherals containsObject:peripheral]) {
[self.connectperipherals addObject:peripheral];
}
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
}
- 發(fā)現(xiàn)外設(shè)服務(wù)
CBService
來到這里,就要打開硬件工程師寫給你的硬件對(duì)接的文檔,看看他們給你的設(shè)備里面有什么服務(wù)了
外設(shè)一般都會(huì)有幾個(gè)服務(wù)衣摩,每個(gè)服務(wù)都會(huì)有幾個(gè)特征,服務(wù)和特征都是用不同的 UUID 來標(biāo)識(shí),每個(gè)特征的properties
是不同的,就是說有不同的功能屬性后面會(huì)說到,不清楚的話,可以谷歌一下 "藍(lán)牙","服務(wù)","特征碼"等關(guān)鍵字;
而peripheral.services
就是一個(gè)存有所有服務(wù)碼的數(shù)組,forin一下,看看里面有沒有你需要的?當(dāng)然,代碼里面的那個(gè)特征碼的UUID@"FFF1"
是我亂寫的,大家可以根據(jù)自己的硬件去搜索特征碼,用到的方法是:
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"FFF1"]] forService:service];
//完整代碼
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
NSLog(@">>>掃描到服務(wù):%@",peripheral.services);
if (error){
NSLog(@">>>發(fā)現(xiàn)服務(wù) %@ 錯(cuò)誤: %@", peripheral.name, [error localizedDescription]);
return;
}
for (CBService *service in peripheral.services) {
NSLog(@">>>掃描到服務(wù) = %@",service.UUID);
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"FFF1"]] forService:service];
}
}
- 發(fā)現(xiàn)外設(shè)服務(wù)中的特征碼
CBCharacteristic
剛也說到,和服務(wù)類似,特征碼也是以不同的 UUID 來標(biāo)識(shí),我們要對(duì)藍(lán)牙的操作,其實(shí)就是對(duì)這些特征碼搞事情了特征碼的屬性是一個(gè)枚舉類型:
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
};
在我的項(xiàng)目中用到的分別是:
CBCharacteristicPropertyWrite, //可寫入
CBCharacteristicPropertyWriteWithoutResponse, //可寫入并帶回執(zhí)
CBCharacteristicPropertyRead, //可讀
CBCharacteristicPropertyNotify, //可訂閱1
CBCharacteristicPropertyIndicate //可訂閱2
會(huì)一點(diǎn)英文的同學(xué)看看類型后面大概能猜到,第一二個(gè)是寫入,一個(gè)帶回執(zhí),一個(gè)不帶;第三個(gè)是可讀;第四第五個(gè)是騷騷不同的兩種訂閱,具體根據(jù)不同的情況使用,其他的好像我也沒用到過...不過看后綴的話好像能發(fā)現(xiàn)些什么吧?
- 發(fā)現(xiàn)外設(shè)的特征碼
CBCharacteristic
代理方法
這也是通過forinservice.characteristics
,判斷特征碼的UUID,對(duì)應(yīng)不同的特征碼做不同的操作,比如:
setNotifyValue forCharacteristic //訂閱方法`;
writeValue: forCharacteristic: type: //寫入方法,其中type類型有無回執(zhí)
peripheral readValueForCharacteristic: //讀取方法
整段的代碼如下:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error) {
NSLog(@"%s,%@",__PRETTY_FUNCTION__,error);
}
else{
for (CBCharacteristic *characteristic in service.characteristics{
NSLog(@"特征碼 == %@",characteristic.UUID);
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFF4"]]){
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFF3"]]){
[peripheral writeValue:[NSData data] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
}
}
不知不覺寫了這么多,還是分開上下部說吧...
iOS關(guān)于藍(lán)牙框架BLE的開發(fā)--基礎(chǔ)用法(下)
- 操作特征碼后的回調(diào)——訂閱/讀取回調(diào)與失敗回調(diào)
- 操作特征碼后的回調(diào)——寫入回調(diào)與失敗回調(diào))