在寫這個(gè)博客之前,空余時(shí)間抽看了近一個(gè)月的文檔和Demo,系統(tǒng)給的解釋很詳細(xì),接口也比較實(shí)用,唯獨(dú)有一點(diǎn),對于設(shè)備的唯一標(biāo)示,網(wǎng)上眾說
紛紜,在這里我目前也還沒有自己的見解,只是在不斷的測試各種情況,親測同一設(shè)備的UUID對于每臺(tái)iPhone設(shè)備都不一樣,只能盡量保證設(shè)備的唯一性,特別是自動(dòng)重連的過程,讓用戶沒有感知。我之前也找了很久,發(fā)現(xiàn)CBCentralManager和CBPeripheral里邊都找不到和Mac地址有關(guān)的東西,后來發(fā)現(xiàn)一般是外設(shè)在Device Information服務(wù)中的某個(gè)特征返回的。經(jīng)過與硬件工程師的協(xié)商,決定APP端將從這個(gè)服務(wù)中獲取到藍(lán)牙設(shè)備以及我的iPhone手機(jī)的藍(lán)牙Mac地址,為自動(dòng)連接的唯一性做準(zhǔn)備。
這里經(jīng)過和硬件工程師的測試,發(fā)現(xiàn)設(shè)備端在獲取手機(jī)藍(lán)牙MAC地址的時(shí)候,當(dāng)用戶手機(jī)重啟之后,這個(gè)地址也是會(huì)隨機(jī)變化的,也就是說,作為開發(fā)者,只有設(shè)備的MAC地址能夠保持唯一性不變化。
ble.png
有疑問的朋友可以先去這里瞅一瞅
一個(gè)關(guān)于藍(lán)牙4.0的智能硬件Demo詳解
下面是兩臺(tái)iPhone6連接同一臺(tái)藍(lán)牙設(shè)備的結(jié)果:
**成功連接**** peripheral:with UUID:<__NSConcreteUUID0x17003d980>50084F69-BA5A-34AC-8A6E-6F0CEADB21CD************成功連接**** peripheral:with UUID:<__NSConcreteUUID0x174036c00>55B7D759-0F1E-6271-EA14-BC5A9C9EEEEC**
進(jìn)入正題
iOS的藍(lán)牙開發(fā)很簡單,只要包含一個(gè)庫,創(chuàng)建CBCentralManager實(shí)例,實(shí)現(xiàn)代理方法,然后就可以直接和設(shè)備進(jìn)行通信。
發(fā)現(xiàn)附近的特定藍(lán)牙設(shè)備
#import
首先可以定義一些即將使用到的UUID的宏
#definekPeripheralName? ? @"360qws Electric Bike Service"http://外圍設(shè)備名稱#definekServiceUUID? ? ? ? @"7CACEB8B-DFC4-4A40-A942-AAD653D174DC"http://服務(wù)的UUID#definekCharacteristicUUID @"282A67B2-8DAB-4577-A42F-C4871A3EEC4F"http://特征的UUID
如果不是把手機(jī)作為中心設(shè)備的話,這些沒有必要設(shè)置。
這里我也沒有用到,僅僅是提了一下,具體操作后續(xù)添加。
對于生成UUID,大家可以谷歌一下,直接通過mac終端生成32位UUID。
1.聲明屬性
@property(weak,nonatomic)IBOutletUITableView*bluetoothTable;@property(weak,nonatomic)IBOutletUITextView*resultTextView;@propertyBOOLcbReady;@property(nonatomic)floatbatteryValue;@property(nonatomic,strong) CBCentralManager *manager;@property(nonatomic,strong) CBPeripheral *peripheral;@property(strong,nonatomic) CBCharacteristic *writeCharacteristic;@property(strong,nonatomic)NSMutableArray*nDevices;@property(strong,nonatomic)NSMutableArray*nServices;@property(strong,nonatomic)NSMutableArray*nCharacteristics;
2.遵守協(xié)議(這里我用到了table)
@interfaceViewController ()
3.初始化數(shù)據(jù)
- (void)viewDidLoad {? ? [superviewDidLoad];// Do any additional setup after loading the view, typically from a nib.self.manager = [[CBCentralManager alloc] initWithDelegate:selfqueue:nil];? ? _cbReady =false;? ? _nDevices = [[NSMutableArrayalloc]init];? ? _nServices = [[NSMutableArrayalloc]init];? ? _nCharacteristics = [[NSMutableArrayalloc]init];? ? _bluetoothTable.delegate =self;? ? _bluetoothTable.dataSource =self;? ? count =0;}
4.實(shí)現(xiàn)藍(lán)牙的協(xié)議方法
(1)檢測藍(lán)牙狀態(tài)
//開始查看服務(wù),藍(lán)牙開啟-(void)centralManagerDidUpdateState:(CBCentralManager *)central{switch(central.state) {caseCBCentralManagerStatePoweredOn:{? ? ? ? ? ? [selfupdateLog:@"藍(lán)牙已打開,請掃描外設(shè)"];? ? ? ? ? ? [_activity startAnimating];? ? ? ? ? ? [_managerscanForPeripheralsWithServices:@[[CBUUIDUUIDWithString:@"FF15"]]options:@{CBCentralManagerScanOptionAllowDuplicatesKey :@YES}];? ? ? ? }break;caseCBCentralManagerStatePoweredOff:[selfupdateLog:@"藍(lán)牙沒有打開,請先打開藍(lán)牙"];break;default:break;? ? }}
注:[_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]]? options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];中間的@[[CBUUID UUIDWithString:@"FF15"]]是為了過濾掉其他設(shè)備,可以搜索特定標(biāo)示的設(shè)備。
(2)檢測到外設(shè)后,停止掃描,連接設(shè)備
//查到外設(shè)后,停止掃描,連接設(shè)備-(void)centralManager:(CBCentralManager *)centraldidDiscoverPeripheral:(CBPeripheral *)peripheraladvertisementData:(NSDictionary *)advertisementDataRSSI:(NSNumber *)RSSI{? ? [selfupdateLog:[NSStringstringWithFormat:@"已發(fā)現(xiàn) peripheral: %@ rssi: %@, UUID: %@ advertisementData: %@ ", peripheral, RSSI, peripheral.identifier, advertisementData]];? ? _peripheral = peripheral;? ? [_managerconnectPeripheral:_peripheraloptions:nil];? ? [self.manager stopScan];? ? [_activity stopAnimating];? ? BOOL replace = NO;// Match if we have this device from beforefor(inti=0; i < _nDevices.count; i++) {? ? ? ? CBPeripheral *p = [_nDevicesobjectAtIndex:i];if([pisEqual:peripheral]) {? ? ? ? ? ? [_nDevicesreplaceObjectAtIndex:iwithObject:peripheral];? ? ? ? ? ? replace = YES;? ? ? ? }? ? }if(!replace) {? ? ? ? [_nDevicesaddObject:peripheral];? ? ? ? [_bluetoothTable reloadData];? ? }}
(3)連接外設(shè)后的處理
//連接外設(shè)成功,開始發(fā)現(xiàn)服務(wù)- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {NSLog(@"%@", [NSStringstringWithFormat:@"成功連接 peripheral: %@ with UUID: %@",peripheral,peripheral.identifier]);? ? [selfupdateLog:[NSStringstringWithFormat:@"成功連接 peripheral: %@ with UUID: %@",peripheral,peripheral.identifier]];? ? [self.peripheral setDelegate:self];? ? [self.peripheral discoverServices:nil];? ? [selfupdateLog:@"掃描服務(wù)"];}//連接外設(shè)失敗-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError*)error{NSLog(@"%@",error);}-(void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError*)error{NSLog(@"%s,%@",__PRETTY_FUNCTION__,peripheral);intrssi = abs([peripheral.RSSI intValue]);CGFloatci = (rssi -49) / (10*4.);NSString*length = [NSStringstringWithFormat:@"發(fā)現(xiàn)BLT4.0熱點(diǎn):%@,距離:%.1fm",_peripheral,pow(10,ci)];? ? [selfupdateLog:[NSStringstringWithFormat:@"距離:%@", length]];}
(4)發(fā)現(xiàn)服務(wù)和搜索到的Characteristice
//已發(fā)現(xiàn)服務(wù)-(void)peripheral:(CBPeripheral *)peripheraldidDiscoverServices:(NSError *)error{? ? [selfupdateLog:@"發(fā)現(xiàn)服務(wù)."];inti=0;for(CBService *sinperipheral.services) {? ? ? ? [self.nServicesaddObject:s];? ? }for(CBService *sinperipheral.services) {? ? ? ? [selfupdateLog:[NSStringstringWithFormat:@"%d :服務(wù) UUID: %@(%@)",i,s.UUID.data,s.UUID]];? ? ? ? i++;? ? ? ? [peripheraldiscoverCharacteristics:nilforService:s];if([s.UUIDisEqual:[CBUUIDUUIDWithString:@"FF15"]]) {? ? ? ? ? ? BOOL replace = NO;// Match if we have this device from beforefor(inti=0; i < _nDevices.count; i++) {? ? ? ? ? ? ? ? CBPeripheral *p = [_nDevicesobjectAtIndex:i];if([pisEqual:peripheral]) {? ? ? ? ? ? ? ? ? ? [_nDevicesreplaceObjectAtIndex:iwithObject:peripheral];? ? ? ? ? ? ? ? ? ? replace = YES;? ? ? ? ? ? ? ? }? ? ? ? ? ? }if(!replace) {? ? ? ? ? ? ? ? [_nDevicesaddObject:peripheral];? ? ? ? ? ? ? ? [_bluetoothTable reloadData];? ? ? ? ? ? }? ? ? ? }? ? }}//已搜索到Characteristics-(void)peripheral:(CBPeripheral *)peripheraldidDiscoverCharacteristicsForService:(CBService *)serviceerror:(NSError *)error{? ? [selfupdateLog:[NSStringstringWithFormat:@"發(fā)現(xiàn)特征的服務(wù):%@ (%@)",service.UUID.data ,service.UUID]];for(CBCharacteristic *cinservice.characteristics) {? ? ? ? [selfupdateLog:[NSStringstringWithFormat:@"特征 UUID: %@ (%@)",c.UUID.data,c.UUID]];if([c.UUIDisEqual:[CBUUIDUUIDWithString:@"FF01"]]) {? ? ? ? ? ? _writeCharacteristic = c;? ? ? ? }if([c.UUIDisEqual:[CBUUIDUUIDWithString:@"FF02"]]) {? ? ? ? ? ? [_peripheralreadValueForCharacteristic:c];? ? ? ? ? ? [_peripheralsetNotifyValue:YESforCharacteristic:c];? ? ? ? }if([c.UUIDisEqual:[CBUUIDUUIDWithString:@"FF04"]]) {? ? ? ? ? ? [_peripheralreadValueForCharacteristic:c];? ? ? ? }if([c.UUIDisEqual:[CBUUIDUUIDWithString:@"FF05"]]) {? ? ? ? ? ? [_peripheralreadValueForCharacteristic:c];? ? ? ? ? ? [_peripheralsetNotifyValue:YESforCharacteristic:c];? ? ? ? }if([c.UUIDisEqual:[CBUUIDUUIDWithString:@"FFA1"]]) {? ? ? ? ? ? [_peripheral readRSSI];? ? ? ? }? ? ? ? [_nCharacteristicsaddObject:c];? ? }}- (void)centralManager:(CBCentralManager *)centraldidDisconnectPeripheral:(CBPeripheral *)peripheralerror:(NSError *)error {? ? [selfupdateLog:[NSStringstringWithFormat:@"已斷開與設(shè)備:[%@]的連接", peripheral.name]];}
(5)獲取外設(shè)發(fā)來的數(shù)據(jù)
//獲取外設(shè)發(fā)來的數(shù)據(jù),不論是read和notify,獲取數(shù)據(jù)都是從這個(gè)方法中讀取。- (void)peripheral:(CBPeripheral *)peripheraldidUpdateValueForCharacteristic:(CBCharacteristic *)characteristicerror:(NSError *)error{if([characteristic.UUIDisEqual:[CBUUIDUUIDWithString:@"FF02"]]) {? ? ? ? NSData * data = characteristic.value;? ? ? ? Byte * resultByte = (Byte *)[data bytes];for(inti=0;i<[data length];i++)? ? ? ? ? ? printf("testByteFF02[%d] = %d\n",i,resultByte[i]);if(resultByte[1] ==0) {switch(resultByte[0]) {case3:// 加解鎖{if(resultByte[2] ==0) {? ? ? ? ? ? ? ? ? ? ? ? [selfupdateLog:@"撤防成功!!!"];? ? ? ? ? ? ? ? ? ? }elseif(resultByte[2] ==1) {? ? ? ? ? ? ? ? ? ? ? ? [selfupdateLog:@"設(shè)防成功!!!"];? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }break;case4:// 開坐桶{if(resultByte[2] ==0) {? ? ? ? ? ? ? ? ? ? ? ? [selfupdateLog:@"關(guān)坐桶成功!!!"];? ? ? ? ? ? ? ? ? ? }elseif(resultByte[2] ==1) {? ? ? ? ? ? ? ? ? ? ? ? [selfupdateLog:@"開坐桶成功!!!"];? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }break;case5:// 鎖定電機(jī){if(resultByte[2] ==0) {? ? ? ? ? ? ? ? ? ? ? ? [selfupdateLog:@"解鎖電機(jī)控制器成功!!!"];? ? ? ? ? ? ? ? ? ? }elseif(resultByte[2] ==1) {? ? ? ? ? ? ? ? ? ? ? ? [selfupdateLog:@"鎖定電機(jī)控制器成功!!!"];? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }break;default:break;? ? ? ? ? ? }? ? ? ? }elseif(resultByte[1] ==1) {? ? ? ? ? ? [selfupdateLog:@"未知錯(cuò)誤"];? ? ? ? }elseif(resultByte[1] ==2) {? ? ? ? ? ? [selfupdateLog:@"鑒權(quán)失敗"];? ? ? ? }? ? }if([characteristic.UUIDisEqual:[CBUUIDUUIDWithString:@"FF04"]]) {? ? ? ? NSData * data = characteristic.value;? ? ? ? Byte * resultByte = (Byte *)[data bytes];for(inti=0;i<[data length];i++)? ? ? ? ? ? printf("testByteFF04[%d] = %d\n",i,resultByte[i]);if(resultByte[0] ==0) {// 未綁定 -》寫鑒權(quán)碼[selfupdateLog:@"當(dāng)前車輛未綁定,請鑒權(quán)"];? ? ? ? ? ? [self authentication];// 鑒權(quán)[selfwritePassword:nilnewPw:nil];? ? ? ? }elseif(resultByte[0] ==1) {// 已綁定 -》鑒權(quán)[selfupdateLog:@"當(dāng)前車輛已經(jīng)綁定,請鑒權(quán)"];? ? ? ? ? ? [selfwritePassword:nilnewPw:nil];? ? ? ? }elseif(resultByte[0] ==2) {// 允許綁定[selfupdateLog:@"當(dāng)前車輛允許綁定"];? ? ? ? }? ? }if([characteristic.UUIDisEqual:[CBUUIDUUIDWithString:@"FF05"]]) {? ? ? ? NSData * data = characteristic.value;? ? ? ? Byte * resultByte = (Byte *)[data bytes];for(inti=0;i<[data length];i++)? ? ? ? ? ? printf("testByteFF05[%d] = %d\n",i,resultByte[i]);if(resultByte[0] ==0) {// 設(shè)備加解鎖狀態(tài) 0 撤防? ? 1 設(shè)防[selfupdateLog:@"當(dāng)前車輛撤防狀態(tài)"];? ? ? ? }elseif(resultByte[0] ==1) {// 設(shè)備加解鎖狀態(tài) 0 撤防? ? 1 設(shè)防[selfupdateLog:@"當(dāng)前車輛設(shè)防狀態(tài)"];? ? ? ? }? ? }}//中心讀取外設(shè)實(shí)時(shí)數(shù)據(jù)- (void)peripheral:(CBPeripheral *)peripheraldidUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristicerror:(NSError *)error {if(error) {? ? ? ? NSLog(@"Error changing notification state: %@", error.localizedDescription);? ? }// Notification has startedif(characteristic.isNotifying) {? ? ? ? [peripheralreadValueForCharacteristic:characteristic];? ? }else{// Notification has stopped// so disconnect from the peripheralNSLog(@"Notification stopped on %@.? Disconnecting", characteristic);? ? ? ? [selfupdateLog:[NSStringstringWithFormat:@"Notification stopped on %@.? Disconnecting", characteristic]];? ? ? ? [self.managercancelPeripheralConnection:self.peripheral];? ? }}//用于檢測中心向外設(shè)寫數(shù)據(jù)是否成功-(void)peripheral:(CBPeripheral *)peripheraldidWriteValueForCharacteristic:(CBCharacteristic *)characteristicerror:(NSError *)error{if(error) {? ? ? ? NSLog(@"=======%@",error.userInfo);? ? ? ? [selfupdateLog:[error.userInfo JSONString]];? ? }else{? ? ? ? NSLog(@"發(fā)送數(shù)據(jù)成功");? ? ? ? [selfupdateLog:@"發(fā)送數(shù)據(jù)成功"];? ? }/* When a write occurs, need to set off a re-read of the local CBCharacteristic to update its value */[peripheralreadValueForCharacteristic:characteristic];}
(6)其他輔助性的
#pragma mark - 藍(lán)牙的相關(guān)操作- (IBAction)bluetoothAction:(UIButton*)sender {switch(sender.tag) {case201:? ? ? ? {// 搜索設(shè)備[selfupdateLog:@"正在掃描外設(shè)..."];? ? ? ? ? ? [_activity startAnimating];? ? ? ? ? ? [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]]? options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];doubledelayInSeconds =30.0;? ? ? ? ? ? dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds *NSEC_PER_SEC));? ? ? ? ? ? dispatch_after(popTime, dispatch_get_main_queue(), ^(void){? ? ? ? ? ? ? ? [self.manager stopScan];? ? ? ? ? ? ? ? [_activity stopAnimating];? ? ? ? ? ? ? ? [selfupdateLog:@"掃描超時(shí),停止掃描"];? ? ? ? ? ? });? ? ? ? }break;case202:? ? ? ? {// 連接if(_peripheral && _cbReady) {? ? ? ? ? ? ? ? [_manager connectPeripheral:_peripheral options:nil];? ? ? ? ? ? ? ? _cbReady =NO;? ? ? ? ? ? }? ? ? ? }break;case203:? ? ? ? {// 斷開if(_peripheral && !_cbReady) {? ? ? ? ? ? ? ? [_manager cancelPeripheralConnection:_peripheral];? ? ? ? ? ? ? ? _cbReady =YES;? ? ? ? ? ? }? ? ? ? }break;case204:? ? ? ? {// 暫停搜索[self.manager stopScan];? ? ? ? }break;case211:? ? ? ? {// 車輛上鎖[selflock];? ? ? ? }break;case212:? ? ? ? {// 車輛解鎖[selfunLock];? ? ? ? }break;case213:? ? ? ? {// 開啟坐桶[selfopen];? ? ? ? }break;case214:? ? ? ? {// 立即尋車[selffind];? ? ? ? }break;default:break;? ? }}-(NSOperationQueue*)queue {if(!_queue) {// 請求隊(duì)列self.queue = [[NSOperationQueuealloc]init];? ? ? ? [self.queue setMaxConcurrentOperationCount:1];? ? }return_queue;}#pragma mark - 命令#pragma mark - 鑒權(quán)-(void)authentication {? ? Byte byte[] = {1,1,2,3,4,5,6,7,8};if(_peripheral.state == CBPeripheralStateConnected) {? ? ? ? [_peripheral writeValue:[NSDatadataWithBytes:byte length:9] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];? ? }}#pragma mark - 寫密碼-(void)writePassword:(NSString*)initialPw newPw:(NSString*)newPw {? ? Byte byte[] = {2,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8};if(_peripheral.state == CBPeripheralStateConnected) {? ? ? ? [_peripheral writeValue:[NSDatadataWithBytes:byte length:17] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];? ? }}#pragma mark - 車輛上鎖-(void)lock {? ? Byte byte[] = {3,1};if(_peripheral.state == CBPeripheralStateConnected) {? ? ? ? [_peripheral writeValue:[NSDatadataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];? ? }}#pragma mark - 車輛解鎖-(void)unLock {? ? Byte byte[] = {3,0};if(_peripheral.state == CBPeripheralStateConnected) {? ? ? ? [_peripheral writeValue:[NSDatadataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];? ? }}#pragma mark - 開啟坐桶-(void)open {? ? Byte byte[] = {4,1};if(_peripheral.state == CBPeripheralStateConnected) {? ? ? ? [_peripheral writeValue:[NSDatadataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];? ? }}#pragma mark - 立即尋車-(void)find {? ? Byte byte[] = {7};if(_peripheral.state == CBPeripheralStateConnected) {? ? ? ? [_peripheral writeValue:[NSDatadataWithBytes:byte length:1] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];? ? }}-(NSData*)hexString:(NSString*)hexString {intj=0;? ? Byte bytes[20];///3ds key的Byte 數(shù)組, 128位for(inti=0; i<[hexString length]; i++)? ? {intint_ch;/// 兩位16進(jìn)制數(shù)轉(zhuǎn)化后的10進(jìn)制數(shù)unicharhex_char1 = [hexString characterAtIndex:i];////兩位16進(jìn)制數(shù)中的第一位(高位*16)intint_ch1;if(hex_char1 >='0'&& hex_char1 <='9')? ? ? ? ? ? int_ch1 = (hex_char1-48)*16;//// 0 的Ascll - 48elseif(hex_char1 >='A'&& hex_char1 <='F')? ? ? ? ? ? int_ch1 = (hex_char1-55)*16;//// A 的Ascll - 65elseint_ch1 = (hex_char1-87)*16;//// a 的Ascll - 97i++;unicharhex_char2 = [hexString characterAtIndex:i];///兩位16進(jìn)制數(shù)中的第二位(低位)intint_ch2;if(hex_char2 >='0'&& hex_char2 <='9')? ? ? ? ? ? int_ch2 = (hex_char2-48);//// 0 的Ascll - 48elseif(hex_char1 >='A'&& hex_char1 <='F')? ? ? ? ? ? int_ch2 = hex_char2-55;//// A 的Ascll - 65elseint_ch2 = hex_char2-87;//// a 的Ascll - 97int_ch = int_ch1+int_ch2;NSLog(@"int_ch=%d",int_ch);? ? ? ? bytes[j] = int_ch;///將轉(zhuǎn)化后的數(shù)放入Byte數(shù)組里j++;? ? }NSData*newData = [[NSDataalloc] initWithBytes:bytes length:20];returnnewData;}
在和硬件之間的數(shù)據(jù)發(fā)送和接受,用的都是byte數(shù)組。最后,添加一個(gè)存儲(chǔ)已連接過得設(shè)備
- (void) addSavedDevice:(CFUUIDRef)uuid {NSArray*storedDevices = [[NSUserDefaultsstandardUserDefaults] arrayForKey:@"StoredDevices"];NSMutableArray*newDevices =nil;CFStringRefuuidString =NULL;if(![storedDevices isKindOfClass:[NSArrayclass]]) {NSLog(@"Can't find/create an array to store the uuid");return;? ? }? ? newDevices = [NSMutableArrayarrayWithArray:storedDevices];? ? uuidString =CFUUIDCreateString(NULL, uuid);if(uuidString) {? ? ? ? [newDevices addObject:(__bridgeNSString*)uuidString];CFRelease(uuidString);? ? }/* Store */[[NSUserDefaultsstandardUserDefaults] setObject:newDevices forKey:@"StoredDevices"];? ? [[NSUserDefaultsstandardUserDefaults] synchronize];}- (void) loadSavedDevices {NSArray*storedDevices = [[NSUserDefaultsstandardUserDefaults] arrayForKey:@"StoredDevices"];if(![storedDevices isKindOfClass:[NSArrayclass]]) {NSLog(@"No stored array to load");return;? ? }for(iddeviceUUIDStringinstoredDevices) {if(![deviceUUIDString isKindOfClass:[NSStringclass]])continue;CFUUIDRefuuid =CFUUIDCreateFromString(NULL, (CFStringRef)deviceUUIDString);if(!uuid)continue;? ? ? ? [CBCentralManager retrievePeripherals:[NSArrayarrayWithObject:(__bridgeid)uuid]];CFRelease(uuid);? ? }}//And the delegate function for the Retrieve Peripheral- (void) centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray*)peripherals {? ? CBPeripheral *peripheral;/* Add to list. */for(peripheralinperipherals) {? ? ? ? [central connectPeripheral:peripheral options:nil];? ? }} - (void) centralManager:(CBCentralManager *)central didRetrievePeripheral:(CBPeripheral *)peripheral {? ? [central connectPeripheral:peripheral options:nil];}
后記
最主要是用UUID來確定你要干的事情,特征和服務(wù)的UUID都是外設(shè)定義好的。我們只需要讀取,確定你要讀取什么的時(shí)候,就去判斷UUID是否相符。 一般來說我們使用的iPhone都是做centralManager的,藍(lán)牙模塊是peripheral的,所以我們是want datas,需要接受數(shù)據(jù)。
1.判斷狀態(tài)為powerOn,然后執(zhí)行掃描
2.停止掃描,連接外設(shè)
3.連接成功,尋找服務(wù)
4.在服務(wù)里尋找特征
5.為特征添加通知
5.通知添加成功,那么就可以實(shí)時(shí)的讀取value[也就是說只要外設(shè)發(fā)送數(shù)據(jù)[一般外設(shè)的頻率為10Hz],代理就會(huì)調(diào)用此方法]。
6.處理接收到的value,[hex值,得轉(zhuǎn)換] 之后就自由發(fā)揮了,在這期間都是通過代理來實(shí)現(xiàn)的,也就是說你只需要處理你想要做的事情,代理會(huì)幫你調(diào)用方法。[別忘了添加代理]
2015-07-28 更
關(guān)于write我這里還有些注意的地方要強(qiáng)調(diào)!!!!
并不是每一個(gè)Characteristic都可以通過回調(diào)函數(shù)來查看它寫入狀態(tài)的。就比如針對 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06),就是一個(gè)不能有response的Characteristic。剛開始我就一直用CBCharacteristicWriteType.WithResponse來進(jìn)行寫入始終不成功,郁悶壞了,最后看到每個(gè)Characteristic還有個(gè)屬性值是指示這個(gè)的,我將每個(gè)Characteristic打印出來有如下信息:
immediateAlertService Discover characteristiclinkLossAlertService Discover characteristic
這個(gè)的properties是什么剛開始不知道,覺得他沒意義,后面才注意到properties是Characteristic的一個(gè)參數(shù),具體解釋如下:
DeclarationSWIFTstruct CBCharacteristicProperties :RawOptionSetType{? ? init(_value: UInt)? ? var value: UInt? ? static var Broadcast: CBCharacteristicProperties { get }staticvarRead:CBCharacteristicProperties{ get }staticvarWriteWithoutResponse:CBCharacteristicProperties{ get }staticvarWrite:CBCharacteristicProperties{ get }staticvarNotify:CBCharacteristicProperties{ get }staticvarIndicate:CBCharacteristicProperties{ get }staticvarAuthenticatedSignedWrites:CBCharacteristicProperties{ get }staticvarExtendedProperties:CBCharacteristicProperties{ get }staticvarNotifyEncryptionRequired:CBCharacteristicProperties{ get }staticvarIndicateEncryptionRequired:CBCharacteristicProperties{ get }}OBJECTIVE-Ctypedefenum{? CBCharacteristicPropertyBroadcast =0x01,? CBCharacteristicPropertyRead =0x02,? CBCharacteristicPropertyWriteWithoutResponse =0x04,? CBCharacteristicPropertyWrite =0x08,? CBCharacteristicPropertyNotify =0x10,? CBCharacteristicPropertyIndicate =0x20,? CBCharacteristicPropertyAuthenticatedSignedWrites =0x40,? CBCharacteristicPropertyExtendedProperties =0x80,? CBCharacteristicPropertyNotifyEncryptionRequired =0x100,? CBCharacteristicPropertyIndicateEncryptionRequired =0x200,}CBCharacteristicProperties;
可以看到0x04對應(yīng)的是CBCharacteristicPropertyWriteWithoutResponse
0x0A對應(yīng)的是CBCharacteristicPropertyNotify
所以 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06)是不能用CBCharacteristicWriteType.WithRespons進(jìn)行寫入,只能用CBCharacteristicWriteType.WithOutRespons。這樣在以后的開發(fā)中可以對每個(gè)Characteristic的這個(gè)參數(shù)進(jìn)行檢查再進(jìn)行設(shè)置。
最后講一下關(guān)于藍(lán)牙綁定的過程,在iOS中,沒有講當(dāng)綁定的過程,直接就是掃描、連接、交互。從而很多人會(huì)認(rèn)為,連接就是綁定了,其實(shí)不然。在iOS開發(fā)中,連接并沒有完成綁定,在網(wǎng)上找到了個(gè)很好的解釋:
you cannot initiate pairing from the iOS central side. Instead, you have to read/write a characteristic value,
and then let your peripheral respond with an "Insufficient Authentication" error.
iOS will then initiate pairing, will store the keys for later use (bonding) and encrypts the link. As far as I know,
it also caches discovery information, so that future connections can be set up faster.
就是當(dāng)發(fā)生讀寫交互時(shí),系統(tǒng)在會(huì)和外設(shè)進(jìn)行綁定操作!!!
2016-02-20 更
如題,手機(jī)作為主設(shè)備,在使用CoreBluetooth時(shí)候,想獲取藍(lán)牙的數(shù)據(jù)廣播包。在使用
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)aPeripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
方法時(shí)候獲取的advertisementData打印出來只有
******kCBAdvDataIsConnectable****kCBAdvDataLocalName****kCBAdvDataManufacturerData**
三個(gè)屬性對應(yīng)的值,但這并非廣播包的數(shù)據(jù)。例如安卓可以通過scandata來獲取到廣播包的值,那么iOS這邊我應(yīng)該怎么做呢?
好像蘋果這邊禁止讀取這種廣播內(nèi)容的的,真要的話你可以讓硬件那邊把數(shù)據(jù)做到kCBAdvDataManufacturerData這個(gè)字段里面。
Demo地址:一個(gè)藍(lán)牙4.0的智能硬件Demo