最近在做一個(gè)項(xiàng)目,是關(guān)于運(yùn)動手表的,APP獲取手表傳來的步數(shù)繼而進(jìn)行接下來的一些操作枫笛。也屬于第一次接觸藍(lán)牙相關(guān)的項(xiàng)目,就開始猛啃藍(lán)牙的相關(guān)信息堡纬。我沒有去深入研究BabyTooth庫,總感覺CoreBluetooth能讓我更好的理解整個(gè)流程...當(dāng)然大家可以把這認(rèn)為是懶..,好了言歸正傳斥难。
BLE屬于低功耗藍(lán)牙,但傳輸速率在我感覺有點(diǎn)慢帘饶。
外圍設(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)颗搂,二者都有一個(gè)唯一的標(biāo)識UUID(CBUUID類型)來唯一確定一個(gè)服務(wù)或者特征担猛,每個(gè)服務(wù)可以擁有多個(gè)特征,UUID這個(gè)坑我已深陷多回..這后面再詳說丢氢。下面是他們之間的關(guān)系:
一些UUID的宏設(shè)置
#define kSeverviceUUID @"3D9E5046-325A-A248-F1FE-15380D827D21"http://獲取到的設(shè)備的UUID傅联,測試綁定的時(shí)候用的
#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 -掃描藍(lán)牙外設(shè)
- (void)scanBtnClick
{
//創(chuàng)建中心設(shè)備管理器并設(shè)置當(dāng)前控制器試圖為代理
_centralManage= [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
接下來進(jìn)入CBCentralManagerDelegate代理方法
2.中心服務(wù)器狀態(tài)
#pragma mark - CBCentralManagerDelegate代理方法
//中心服務(wù)器狀態(tài)更新后
- (void)centralManagerDidUpdateState:(CBCentralManager*)central
{
switch(central.state)
{
//藍(lán)牙為開啟狀態(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)我寫入一個(gè)指定的外設(shè)UUID的時(shí)候稍浆,卻掃不到任何設(shè)備,目前不清楚是什么原因猜嘱,希望大家告知衅枫。
[central scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];
}
break;
default:
NSLog(@"此設(shè)備不支持BLE或未打開藍(lán)牙功能,無法作為外圍設(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è)的名字來綁定設(shè)備的朗伶,但是發(fā)現(xiàn)行不通弦撩,當(dāng)時(shí)就這個(gè)綁定問題很是苦惱
if([peripheral.name hasPrefix:@"PD"])
{
_hud.labelText= [NSString stringWithFormat:@"掃描到外設(shè)%@,開始連接...",peripheral.name];
//坑來啦!就是這個(gè)坑啊,在這里可以獲取到外設(shè)的 identifier论皆,name 和 RSSI(信號強(qiáng)弱)益楼,我一直以為identifier就是外設(shè)的UUID,就一直用peripheral.identifier來做判斷点晴,一直就不走方法感凤,打印出來的identifier你會發(fā)現(xiàn)是這樣一串字符"<__NSConcreteUUID 0x15106e470> 3D9E5046-325A-A248-F1FE-15380D827D21",UUID前面還有一串字符粒督,所以說identifier并不是真正的UUID陪竿。最后發(fā)現(xiàn)identifier還有個(gè)UUIDString的方法,接下來就可以通過判斷UUIDString和當(dāng)前設(shè)備的UUID是否一致來選擇是否連接該設(shè)備來實(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è)成功";
//連接成功后建立一個(gè)通知,通知相機(jī)連接已完成(這個(gè)是寫的自定義相機(jī)來實(shí)現(xiàn)藍(lá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ā)生錯(cuò)誤,錯(cuò)誤信息:%@",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í)我拿到的硬件文檔純英文的不說礁哄,而且給的東西很少长酗,很不清晰,你所寫入的數(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)時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",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í)間和步數(shù):%@,%@",newText,stepCount);
//其他的卡路里什么的計(jì)算就不寫了花枫,太復(fù)雜..算法是客戶那邊提供的..看著都惡心..
}
最后再說下,我發(fā)現(xiàn)其實(shí)BLE在讀寫數(shù)據(jù)的時(shí)候已經(jīng)實(shí)現(xiàn)了綁定了掏膏,因?yàn)樵趯戇^斷開重連后劳翰,重連的只會是之前連接的那個(gè)設(shè)備,但重新掃描就不一定掃描那個(gè)設(shè)備了馒疹,所以在鏈接外設(shè)的代理方法里判斷identifier.UUIDString佳簸。
好啦,BLE的開發(fā)大概就是這樣的一個(gè)流程了颖变,其實(shí)并不是說很難生均,只要理清這個(gè)邏輯就行。第一次寫腥刹,也懶得用MarkDown來排版马胧,關(guān)于其他相關(guān)的我也會繼續(xù)研究并更新,忘大家共同學(xué)習(xí)衔峰,共同進(jìn)步佩脊。?