IOS9 BLE 與外設(shè)交互及設(shè)備綁定

最近在做一個項目,是關(guān)于運動手表的,APP獲取手表傳來的步數(shù)繼而進行接下來的一些操作。也屬于第一次接觸藍牙相關(guān)的項目,就開始猛啃藍牙的相關(guān)信息。我沒有去深入研究BabyTooth庫,總感覺CoreBluetooth能讓我更好的理解整個流程...當(dāng)然大家可以把這認(rèn)為是懶..,好了言歸正傳。

BLE屬于低功耗藍牙,但傳輸速率在我感覺有點慢。

外圍設(shè)備和中央設(shè)備在CoreBluetooth中使用CBPeripheralManager和CBCentralManager表示。

CBPeripheralManager:外圍設(shè)備通常用于發(fā)布服務(wù)、生成數(shù)據(jù)、保存數(shù)據(jù)。外圍設(shè)備發(fā)布并廣播服務(wù),告訴周圍的中央設(shè)備它的可用服務(wù)和特征。

CBCentralManager:中央設(shè)備使用外圍設(shè)備的數(shù)據(jù).中央設(shè)備掃描到外圍設(shè)備后會就會試圖建立連接,一旦連接成功就可以使用這些服務(wù)和特征。

外圍設(shè)備和中央設(shè)備之間交互的橋梁是服務(wù)(CBService)和特征(CBCharacteristic),二者都有一個唯一的標(biāo)識UUID(CBUUID類型)來唯一確定一個服務(wù)或者特征,每個服務(wù)可以擁有多個特征,UUID這個坑我已深陷多回..這后面再詳說。下面是他們之間的關(guān)系:

一些UUID的宏設(shè)置

#define kSeverviceUUID @"3D9E5046-325A-A248-F1FE-15380D827D21"http://獲取到的設(shè)備的UUID,測試綁定的時候用的

#define kStepServiceUUID @"0C301900-BEB8-5C69-8714-099C77103418"http://服務(wù)的UUID

#define kStepRecordsUUID @"0C302ABC-BEB8-5C69-8714-099C77103418"http://特征的UUID

#define kStepControl @"0C302ABE-BEB8-5C69-8714-099C77103418"

1.創(chuàng)建中心設(shè)備

#pragma mark -掃描藍牙外設(shè)

- (void)scanBtnClick

{

//創(chuàng)建中心設(shè)備管理器并設(shè)置當(dāng)前控制器試圖為代理

_centralManage= [[CBCentralManager alloc] initWithDelegate:self queue:nil];

}

接下來進入CBCentralManagerDelegate代理方法

2.中心服務(wù)器狀態(tài)

#pragma mark - CBCentralManagerDelegate代理方法

//中心服務(wù)器狀態(tài)更新后

- (void)centralManagerDidUpdateState:(CBCentralManager*)central

{

switch(central.state)

{

//藍牙為開啟狀態(tài),iOS應(yīng)該是8以后吧,不加狀態(tài)判斷崩潰

case CBCentralManagerStatePoweredOn:

NSLog(@"BLE已打開");

{

_hud= [MBProgressHUD showHUDAddedTo:self.view.window animated:YES];

_hud.delegate=self;

_hud.labelText=@"掃描外設(shè)中..";

self.centralManage= central;

//這里填寫nil,會掃描所有外設(shè),但是當(dāng)我寫入一個指定的外設(shè)UUID的時候,卻掃不到任何設(shè)備,目前不清楚是什么原因,希望大家告知。

[central scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];

}

break;

default:

NSLog(@"此設(shè)備不支持BLE或未打開藍牙功能,無法作為外圍設(shè)備");

break;

}

}

#pragma mark - 發(fā)現(xiàn)外設(shè)

- (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber*)RSSI

{

//根據(jù)外設(shè)名的前綴來查找相應(yīng)的外設(shè),本來在綁定的時候我打算通過判斷外設(shè)的名字來綁定設(shè)備的,但是發(fā)現(xiàn)行不通,當(dāng)時就這個綁定問題很是苦惱

if([peripheral.name hasPrefix:@"PD"])

{

_hud.labelText= [NSString stringWithFormat:@"掃描到外設(shè)%@,開始連接...",peripheral.name];

//坑來啦!就是這個坑啊,在這里可以獲取到外設(shè)的 identifier,name 和 RSSI(信號強弱),我一直以為identifier就是外設(shè)的UUID,就一直用peripheral.identifier來做判斷,一直就不走方法,打印出來的identifier你會發(fā)現(xiàn)是這樣一串字符"<__NSConcreteUUID 0x15106e470> 3D9E5046-325A-A248-F1FE-15380D827D21",UUID前面還有一串字符,所以說identifier并不是真正的UUID。最后發(fā)現(xiàn)identifier還有個UUIDString的方法,接下來就可以通過判斷UUIDString和當(dāng)前設(shè)備的UUID是否一致來選擇是否連接該設(shè)備來實現(xiàn)綁定。

[USER_D setObject:peripheral.identifier.UUIDString forKey:@"peripheralUUID"];

//停止掃描

[self.centralManage stopScan];

NSLog(@"獲得的外設(shè)的UUID---%@",peripheral.identifier.UUIDString);

NSLog(@"advertisementData--%@",advertisementData);

NSLog(@"*****沒有綁定*****");

self.peripheral= peripheral;

NSLog(@"開始連接外圍設(shè)備");

//[self.centralManage connectPeripheral:peripheral options:nil];

[self.centralManage connectPeripheral:peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];

}

}

#pragma mark -連接到外圍設(shè)備

- (void)centralManager:(CBCentralManager*)central didConnectPeripheral:(CBPeripheral*)peripheral

{

NSLog(@"連接外圍設(shè)備成功");

_hud.labelText=@"連接外設(shè)成功";

//連接成功后建立一個通知,通知相機連接已完成(這個是寫的自定義相機來實現(xiàn)藍牙控制拍照)

[[NSNotificationCenter defaultCenter] postNotificationName:@"connected" object:nil];

//設(shè)置外圍設(shè)備的代理為當(dāng)前控制器

peripheral.delegate=self;

//外圍設(shè)備開始尋找服務(wù)

[peripheral discoverServices:@[[CBUUID UUIDWithString:kStepServiceUUID]]];

}

#pragma mark - CBPeripheralDelegate代理方法

//外圍設(shè)備尋找到服務(wù)器后

- (void)peripheral:(CBPeripheral*)peripheral didDiscoverServices:(nullableNSError*)error

{

NSLog(@"已發(fā)現(xiàn)可用服務(wù)...");

if(error)

{

NSLog(@"外圍設(shè)備尋找服務(wù)過程中發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);

}

//遍歷查找到的服務(wù)

CBUUID*serviceUUID = [CBUUID UUIDWithString:kStepServiceUUID];

CBUUID*stepRecordsUUID = [CBUUID UUIDWithString:kStepRecordsUUID];

CBUUID*stepControl = [CBUUID UUIDWithString:kStepControl];

for(CBService*service in peripheral.services)

{

if([service.UUID isEqual:serviceUUID])

{

//外圍設(shè)備查找指定服務(wù)中的特征

[peripheral discoverCharacteristics:@[stepRecordsUUID,stepControl] forService:service];

}

}

}

#pragma mark - 外圍設(shè)備尋找到特征后

- (void)peripheral:(CBPeripheral*)peripheral didDiscoverCharacteristicsForService:(CBService*)service error:(nullableNSError*)error

{

? ? ?//在這里向外設(shè)寫入內(nèi)容,先遍歷特征,遍歷到可寫入的特征

? ? //寫入特征也比較坑,當(dāng)時我拿到的硬件文檔純英文的不說,而且給的東西很少,很不清晰,你所寫入的數(shù)據(jù)應(yīng)該是硬件那邊提供的,寫入的分方法就是writeValue

//寫入目標(biāo)

[peripheral writeValue:[NSDatadataWithBytes:(uint8_t[]){43, goal, goal>>8,0,2} length:5] forCharacteristic:characterstic type:CBCharacteristicWriteWithResponse];//這是我寫入目標(biāo)的一種數(shù)據(jù)格式

}

#pragma mark - 特征值被更新后

- (void)peripheral:(CBPeripheral*)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic*)characteristic error:(nullableNSError*)error

{

NSLog(@"收到特征更新通知...");

if(error)

{

NSLog(@"更新通知狀態(tài)時發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);

}

//給特征值設(shè)置新的值

CBUUID *stepRecordsUUID = [CBUUID UUIDWithString:kStepRecordsUUID];

if([characteristic.UUID isEqual:stepRecordsUUID])

{

if(characteristic.isNotifying)

{

if(characteristic.properties == CBCharacteristicPropertyNotify)

{

NSLog(@"已訂閱特征通知");

[peripheral readValueForCharacteristic:characteristic];

return;

}

elseif(characteristic.properties == CBCharacteristicPropertyRead)

{

//從外圍設(shè)備讀取新值,調(diào)用此方法會觸發(fā)代理方法:- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error

[peripheral readValueForCharacteristic:characteristic];

}

else

{

NSLog(@"停止已停止");

//取消連接

[self.centralManage cancelPeripheralConnection:peripheral];

}

}

}

}

#pragma mark -?更新特征后(調(diào)用readValueForCharactrtistic:方法或者外圍設(shè)備在訂閱后更新特征值都會調(diào)用此方法)

- (void)peripheral:(CBPeripheral*)peripheral didUpdateValueForCharacteristic:(CBCharacteristic*)characteristic error:(nullableNSError*)error

{

//在這里讀取外設(shè)傳來的數(shù)據(jù)

//這只是一部分代碼

NSData*data = characteristic.value;

uint32_t minuteTimestamp =0;

[data getBytes:&minuteTimestamp range:NSMakeRange(1,3)];

const uint8_t*bytes = data.bytes;

if(minuteTimestamp !=0)

{

NSDate*time = [NSDate dateWithTimeIntervalSince1970:SEC_FROM_1970_TO_2012 + minuteTimestamp *60];

NSTimeZone*zone = [NSTimeZone systemTimeZone];

NSInteger interval = [zone secondsFromGMTForDate:time];

NSDate*localDate = [time dateByAddingTimeInterval:interval];

[newText appendFormat:@"%@ %u, %u, %u, %u, %u\n",

localDate, bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]];

NSString*dateString = [NSString stringWithFormat:@"%@",localDate];

NSLog(@"======dateString====%@",dateString);

NSString*stepCount = [NSString stringWithFormat:@"%u",bytes[4]+bytes[5]+bytes[6]+bytes[7]+bytes[8]];

//把數(shù)據(jù)存入數(shù)據(jù)庫中

[StepDAO insertData:dateString AndSteps:stepCount];

NSLog(@"讀取到的特征時間和步數(shù):%@,%@",newText,stepCount);

//其他的卡路里什么的計算就不寫了,太復(fù)雜..算法是客戶那邊提供的..看著都惡心..

}

最后再說下,我發(fā)現(xiàn)其實BLE在讀寫數(shù)據(jù)的時候已經(jīng)實現(xiàn)了綁定了,因為在寫過斷開重連后,重連的只會是之前連接的那個設(shè)備,但重新掃描就不一定掃描那個設(shè)備了,所以在鏈接外設(shè)的代理方法里判斷identifier.UUIDString。

好啦,BLE的開發(fā)大概就是這樣的一個流程了,其實并不是說很難,只要理清這個邏輯就行。第一次寫,也懶得用MarkDown來排版,關(guān)于其他相關(guān)的我也會繼續(xù)研究并更新,忘大家共同學(xué)習(xí),共同進步。?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容