天天看點

coreBlueTooth(續)

概述 公司的項目是醫療類的項目,是以這段一直在和藍牙打交道。我使用的是蘋果原生的架構CoreBluetooth。在對接幾個藍牙裝置的過程中,也遇到一些坑,下文我會一一列舉。 git上有個庫BabyBluetooth 基于原生CoreBluetooth架構進行了封裝,使用起來也很友善,大家可以嘗試一下。 那麼我們開始吧!

正文 在了解下文内容之前,我已預設你已經了解一些基本概念: 什麼是中心裝置 什麼是外圍裝置 什麼是服務(service) 什麼是特性(characteristic) 什麼是訂閱(notify) 什麼是UUID ... 基本了解了以上一些概念,下面的内容将比較好了解。

需要注明,下面的UUID是我的藍牙裝置中的Service和Characteristic的UUID,要注意根據自己的藍牙裝置提供的Service和Characteristic的UUID來替換

// 藍牙裝置提供的服務的UUID #define kCGMServiceTwoUUID @"0000FFF0-0000-1000-8000-00805F9B34FB" // 藍牙裝置提供的寫入特性 #define kCGMCharacteristicOneUUID @"0000FFF1-0000-1000-8000-00805F9B34FB" // 藍牙裝置提供的notify特性 #define kCGMCharacteristicTwoUUID @"0000FFF2-0000-1000-8000-00805F9B34FB"

那麼,先讓我們了解下藍牙互動流程中幾個常用的回調。 中心裝置CBCentralManager更新裝置藍牙狀态的回調 - (void)centralManagerDidUpdateState:(CBCentralManager *)central{   switch (central.state)  {   case CBCentralManagerStatePoweredOn:    {   // 掃描外圍裝置    [self.centeralManager scanForPeripheralsWithServices:nil options:nil];    }   break;  default:  NSLog(@"裝置藍牙未開啟");   break;   } } 中心裝置已經發現外圍裝置回調

這裡有幾個問題值得注意: 1. 在ios中藍牙廣播資訊中通常會包含以下4種類型的資訊。ios的藍牙通信協定中不接受其他類型的廣播資訊。是以需要注意的是,如果需要在掃描裝置時,通過藍牙裝置的Mac位址來唯一辨識裝置,那麼需要與藍牙裝置的硬體工程師溝通好:将所需要的Mac位址放到一下幾種類型的廣播資訊中。通常放到kCBAdvDataManufacturerData這個字段中。kCBAdvDataIsConnectable = 1; kCBAdvDataLocalName = XXXXXX; kCBAdvDataManufacturerData =XXXX; kCBAdvDataTxPowerLevel = 0; 2. 裝置的UUID(peripheral.identifier)是由兩個裝置的mac通過算法得到的,是以不同的手機連接配接相同的裝置,它的UUID都是不同的,無法辨別裝置。 3. 蘋果與藍牙裝置連接配接通信時,使用的并不是蘋果藍牙子產品的Mac位址,使用的是蘋果随機生成的十六進制碼作為手機藍牙的Mac與外圍藍牙裝置進行互動。如果藍牙裝置與手機在一定時間内多次通信,那麼使用的是首次連接配接時随機生成的十六進制碼作為Mac位址,超過這個固定的時間段,手機會清空已随機生成的Mac位址,重新生成。也就是說外圍裝置是不能通過與蘋果手機的互動時所擷取的藍牙Mac位址作為手機的唯一辨別的。(這是在與寫藍牙裝置的固件工程師聯調時根據問題的現象推測的。至于蘋果藍牙通訊協定的底層是否确實完全像我所說的這樣,希望了解的讀者能提供幫助。在此先謝過。)

 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { NSLog(@"advertisementData.kCBAdvDataManufacturerData = %@", advertisementData[@"kCBAdvDataManufacturerData"]);   _connectPeripheral = peripheral; // [self.centeralManager connectPeripheral:peripheral options:nil];   if ([advertisementData[@"kCBAdvDataLocalName"] hasPrefix:@"SN"]){  NSLog(@"已搜尋到裝置");  NSLog(@"peripheral.identifier = %@ peripheral.name = %@", peripheral.identifier, peripheral.name);  [_delegate getAdvertisementData:advertisementData andPeripheral:peripheral];  [_peripheralArray addObject:peripheral];   } }

中心裝置裝置連接配接成功回調  - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {   // 裝置停止掃描  [self.centeralManager stopScan];  peripheral.delegate = self;   dispatch_after(2, dispatch_get_main_queue(), ^{   // 查找服務  [_connectPeripheral discoverServices:@[[CBUUID UUIDWithString:kCGMServiceTwoUUID]]];   }); }

中心裝置裝置連接配接失敗回調  - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{  [_operationDelegate failToConnect]; }

中心裝置裝置連接配接中斷回調  - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{

 NSLog(@"連接配接斷開 %@", [error localizedDescription]);  [_operationDelegate disconnected]; }

外圍裝置(CBPeripheral)發現服務(service)回調 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {   if (error)  { // 輸出錯誤資訊   NSLog(@"discoverServices.error============ %@", [error localizedDescription]);   return;  }   // 周遊裝置提供的服務   for (CBService *service in peripheral.services)  {   NSLog(@"service.UUID = ------------- = %@", service.UUID.UUIDString);  // 找到需要的服務,并擷取該服務響應的特性   if([service.UUID isEqual:[CBUUID UUIDWithString:kCGMServiceTwoUUID]])  {   [service.peripheral discoverCharacteristics:nil forService:service];  NSLog(@"開始查找cgm的characteristic");  }   } }

外圍裝置發現特性(characteristic)回調   - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {  if (error) {   // 輸出錯誤資訊   NSLog(@"discoverCharacteristics.error=========== %@", [error localizedDescription]);  return;  }   // 周遊服務中的所有特性   for (CBCharacteristic *characteristic in service.characteristics)  {   if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCGMCharacteristicOneUUID]])  {  // 設定讀寫的特性

 _readAndWriteCharacteristic = characteristic;  } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCGMCharacteristicTwoUUID]])  {   // 設定需要訂閱的特性 _notifyCharacteristic = characteristic;   [_connectPeripheral setNotifyValue:YES forCharacteristic:_notifyCharacteristic];   } } }

外圍裝置資料更新回調, 可以在此回調方法中讀取資訊(無論是read的回調,還是notify(訂閱)的回調都是此方法)  - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{   if (error) {   // 輸出錯誤資訊   NSLog(@"didupadteValueForCharacteristic error ============ %@", [error localizedDescription]);   return;   }   NSLog(@"value ============= %@", characteristic.value);   // 解析資料   NSData *data = characteristic.value;   // 将NSData轉Byte數組   NSUInteger len = [data length];   Byte *byteData = (Byte *)malloc(len);  memcpy(byteData, [data bytes], len);   NSMutableArray *commandArray = [NSMutableArray arrayWithCapacity:0];  // Byte數組轉字元串  for (int i = 0; i < len; i++) {   NSString *str = [NSString stringWithFormat:@"%02x", byteData[i]];  [commandArray addObject:str];   NSLog(@"byteData = %@", str);   }   // 輸出資料   [_operationDelegate dataWithCharacteristic:commandArray];  }

特性已寫入外圍裝置的回調(如果寫入類型為CBCharacteristicWriteWithResponse 回調此方法,如果寫入類型為CBCharacteristicWriteWithoutResponse不回調此方法)  - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{   if (error) {  NSLog(@"write.error=======%@",error.userInfo);   }  // 讀資料   if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCGMCharacteristicOneUUID]]) {   [self readCharacter];   } }

外圍裝置訂閱特征值狀态改變成功的回調需要注意的是這裡是對kCGMCharacteristicOneUUID這個特性進行寫入,這裡之是以這樣操作是因為我的藍牙裝置的藍牙協定是這樣定義的,是以這裡不要照抄照搬,要按照你的藍牙裝置的通訊協定來确定,對哪一個特性進行read,對哪個特性進行write,以及對哪個特性進行設定Notify 

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {  if (error) {   NSLog(@"error = %@", [error localizedDescription]);  }   // 對特性kCGMCharacteristicTwoUUID設定notify(訂閱),成功以後回      if ([characteristic.UUID.UUIDString isEqualToString:kCGMCharacteristicTwoUUID] && characteristic.isNotifying) {  // 寫資料 回調-didWriteValueForCharacteristic  NSLog(@"寫資料到cgm裝置的characteristic = %@", _readAndWriteCharacteristic.UUID.UUIDString);  [_operationDelegate writeCharacteristic];   } }

另外,除了回調以外,還有幾個點需要注意: 搜尋外圍裝置 - (void)searchlinkDevice{   // 實作代理 // 掃描裝置//  _centeralManager = [[CBCentralManager alloc] initWithDelegate:self// queue:nil];   if(self.centeralManager.state == CBCentralManagerStatePoweredOff) {   // 藍牙關閉的   } else if(self.centeralManager.state == CBCentralManagerStateUnsupported) {   // 裝置不支援藍牙  } else if(self.centeralManager.state == CBCentralManagerStatePoweredOn || self.centeralManager.state == CBCentralManagerStateUnknown) {  // 開啟的話開始掃描藍牙裝置  [self.centeralManager scanForPeripheralsWithServices:nil options:nil];   double delayInSeconds = 20.0;  // 掃描20s後未掃描到裝置停止掃描   dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));   dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { [self stopScan]; });  } }

對某個特性(characteristic)寫入資料 - (void)writeCharacter:(NSData *)data{  NSLog(@" characteristic.uuid = %@ data ==== %@", _readAndWriteCharacteristic.UUID.UUIDString, data);  if ([_readAndWriteCharacteristic.UUID isEqual:[CBUUID UUIDWithString:kCGMCharacteristicOneUUID]]) {   [_connectPeripheral writeValue:data forCharacteristic:_readAndWriteCharacteristic type:CBCharacteristicWriteWithResponse];  } else {   [_connectPeripheral writeValue:data forCharacteristic:_readAndWriteCharacteristic type:CBCharacteristicWriteWithoutResponse];  } }

讀資料 需要注意的是這裡讀取藍牙資訊 (但并不是在傳回值中接收,要在  - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;這個回調方法中接收)  - (void)readCharacter{  [_connectPeripheral readValueForCharacteristic:_readAndWriteCharacteristic]; }

另外完整的源碼請到這裡https://github.com/xuzhengquan/BlueToothDemo/檢視,demo中提供了藍牙操作的工具類,由于藍牙通訊協定的不同,是以在UI上沒有做更多的工作,隻是提供了搜尋裝置時展示了一下外圍裝置資訊,還請諒解。

連結:http://www.jianshu.com/p/3b404bd672a8