CoreBluetooth For Central (7)
Interact Data
從前面一系列的文章下來後對於掃描、停止掃描、連線、斷線、服務、屬性的使用都很清楚的了解,最後這篇文章要進入如何利用屬性與BLE週邊溝通獲取資料,藍牙 BLE CoreBluetooth 初探 中有張示意圖用來參考接下來要做的事情:
當我們尋訪完屬性之後,會得到屬性的UUID,從這邊我們要來利用CoreBluetooth讀取屬性設定以及設定屬性通知讓有資料更新時,CoreBluetooth Framework才會獲取UUID上的資料並通知對應的Delegate protocol使它即時得到資料,範例中需要即時更新的資料就是心跳測量的結果。
設定資料更新通知、讀取設定
這邊要將CoreBluetooth For Central (6)提到的peripheral:didDiscoverCharacteristicsForService
進行修改,並增加檢查UUID來做對應的動作:
//-----------start----------- // CBService *s = [peripheral.services objectAtIndex:(peripheral.services.count - 1)]; if(service.UUID == NULL || s.UUID == NULL) return; // zach ios6 added if ([c.UUID isEqual:[CBUUID UUIDWithString:HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID]]) { // 2 [peripheral setNotifyValue:YES forCharacteristic:c];//設定2A37為通知型,值有變化時會引發Delegate NSLog(@"找到 心跳測量屬性"); }else if ([c.UUID isEqual:[CBUUID UUIDWithString:HEART_RATE_BODY_LOCATION_CHARACTERISTIC_UUID]]) { // 3 //讀取Heart Rate Measurement 位置 [peripheral readValueForCharacteristic:c]; NSLog(@"找到 心跳測量位置屬性 "); } //------------end------------
CoreBluetooth For Central (6)了解到2A37
及2A38
,所以進行處理:
- 2A37
這為即時通知型的資料更新,所以進行setNotifyValue:forCharacteristic
的設定,如此有資料更新時會呼叫peripheral didUpdateValueForCharacteristic: error:
- 2A38
讀取Polar H7感測器所在位置,讀取後也是同樣會引發peripheral didUpdateValueForCharacteristic: error:
,之後要針對這個呼叫進行屬性UUID的判斷
完整修改完的程式:
//-----------start----------- -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { CBService *s = [peripheral.services objectAtIndex:(peripheral.services.count - 1)]; //NSLog(@"=========== Service UUID %s ===========\n",[NSUUID UUID] ini); if (!error) { NSLog(@"=========== %ld Characteristics of %@ service ",(long)service.characteristics.count,service.UUID); for(CBCharacteristic *c in service.characteristics){ NSLog(@" %@ \n",c.UUID); // CBService *s = [peripheral.services objectAtIndex:(peripheral.services.count - 1)]; if(service.UUID == NULL || s.UUID == NULL) return; // zach ios6 added if ([c.UUID isEqual:[CBUUID UUIDWithString:HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID]]) { [peripheral setNotifyValue:YES forCharacteristic:c];//設定2A37為通知型,值有變化時會引發Delegate NSLog(@"找到 心跳測量屬性"); }else if ([c.UUID isEqual:[CBUUID UUIDWithString:HEART_RATE_BODY_LOCATION_CHARACTERISTIC_UUID]]) { //讀取Heart Rate Measurement 位置 [peripheral readValueForCharacteristic:c]; NSLog(@"找到 心跳測量位置屬性 "); } } NSLog(@"=== Finished set notification ===\n"); } else { NSLog(@"Characteristic discorvery unsuccessfull !\n"); } } //------------end------------
資料更新通知處理
所有被設定為資料更新通知或是直接讀取屬性資料的都會被引發呼叫,因此要針對屬性UUID進行判斷是哪個屬性的資料更新,這裡只對心跳測量資料更新、心跳表感測器位置資料進行處理,程式內容如下:
//-----------start----------- //更新通知Delegate,有進行setNotifyValue:forCharacteristic設定的UUID一旦有更新都會呼叫此protocol - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { // Updated value for heart rate measurement received if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID]]) { // 1 //如果更新符合HEART RATE MEASUREMENT UUID進行心跳值的轉換 [self conveterBPMData:characteristic error:error]; //如果更新符合BODY SENSOR LOCATION UUID進行心跳值的轉換 } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:HEART_RATE_BODY_LOCATION_CHARACTERISTIC_UUID]]) { [self conveterLocation:characteristic]; } } //------------end------------
根據Bluetooth的Heart Rate Measurement規範中,必需要將取得的資料進行處理才能獲取正確的心跳測量值:
//-----------start----------- - (void) conveterBPMData:(CBCharacteristic *)characteristic error:(NSError *)error { // Get the Heart Rate Monitor BPM NSData *data = [characteristic value]; // 1 const uint8_t *reportData = [data bytes]; uint16_t bpm = 0; //判定第0個byte之第0個bit值為1的話要將取得的byte反轉 (LSB->MSB) if ((reportData[0] & 0x01) == 0) { // 2 // Retrieve the BPM value for the Heart Rate Monitor bpm = reportData[1]; } else { bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1])); // 3 } // Display the heart rate value to the UI if no error occurred if( (characteristic.value) || !error ) { // 4 NSLog(@"%@",[NSString stringWithFormat:@"%i bpm", bpm]); } return; } //------------end------------
同理,心跳表位感測器置資訊Body Sensor Location也是:
//-----------start----------- - (void) conveterLocation:(CBCharacteristic *)characteristic { NSData *sensorData = [characteristic value];//取得資料 uint8_t *locationData = (uint8_t *)[sensorData bytes];//轉換成byte來取得特定的byte if (locationData ) { uint8_t sensorLocation = locationData[0]; /* 0 Other 1 Chest 胸部 2 Wrist 3 Finger 4 Hand 5 Ear Lobe 6 Foot 7 ~ 255 Reserved for future use 因Porla H7為胸部位置,所以針對取值後判斷是不是為1 */ NSLog(@"%@",[NSString stringWithFormat:@"位置: %@", sensorLocation == 1 ? @"胸部" : @"非位於胸部"]); // 3 } else { NSLog(@"%@",[NSString stringWithFormat:@"位置: 未知"]); } return; } //------------end------------
如此,承之前的BluetoothLE-Explore範例程式進行修改後,您按下Scan and Connect
後就會看到心跳測量的值:
2015-01-27 14:42:15.272 BluetoothLE-Interact[8644:7087526] UpdateState:PoweredOn 2015-01-27 14:42:18.872 BluetoothLE-Interact[8644:7087526] Scan And Connect 2015-01-27 14:42:19.212 BluetoothLE-Interact[8644:7087526] peripheral <CBPeripheral: 0x165a7ed0, identifier = 8DC3DD90-0C17-1AB9-90FE-E9130DD07B81, name = Polar H7 000607, state = disconnected> 2015-01-27 14:42:19.213 BluetoothLE-Interact[8644:7087526] advertisementData { kCBAdvDataIsConnectable = 1; kCBAdvDataLocalName = "Polar H7 000607"; kCBAdvDataManufacturerData = <6b000303 58>; kCBAdvDataServiceUUIDs = ( "Heart Rate" ); kCBAdvDataTxPowerLevel = 0; } 2015-01-27 14:42:19.214 BluetoothLE-Interact[8644:7087526] RSSI -55 2015-01-27 14:42:19.214 BluetoothLE-Interact[8644:7087526] localName:Polar H7 000607 2015-01-27 14:42:19.215 BluetoothLE-Interact[8644:7087526] stopScan 2015-01-27 14:42:19.215 BluetoothLE-Interact[8644:7087526] connect to Polar H7 000607 2015-01-27 14:42:21.233 BluetoothLE-Interact[8644:7087526] connected 2015-01-27 14:42:21.233 BluetoothLE-Interact[8644:7087526] Connect To Peripheral with name: Polar H7 000607 with UUID:8DC3DD90-0C17-1AB9-90FE-E9130DD07B81 2015-01-27 14:42:21.617 BluetoothLE-Interact[8644:7087526] didDiscoverServices: 2015-01-27 14:42:21.619 BluetoothLE-Interact[8644:7087526] ====Polar H7 000607 2015-01-27 14:42:21.619 BluetoothLE-Interact[8644:7087526] =========== 4 of service for UUID 8DC3DD90-0C17-1AB9-90FE-E9130DD07B81 =========== 2015-01-27 14:42:21.620 BluetoothLE-Interact[8644:7087526] Service found with UUID: Heart Rate 2015-01-27 14:42:21.620 BluetoothLE-Interact[8644:7087526] Service found with UUID: Device Information 2015-01-27 14:42:21.621 BluetoothLE-Interact[8644:7087526] Service found with UUID: Battery 2015-01-27 14:42:21.621 BluetoothLE-Interact[8644:7087526] Service found with UUID: 6217FF49-AC7B-547E-EECF-016A06970BA9 2015-01-27 14:42:21.738 BluetoothLE-Interact[8644:7087526] =========== 2 Characteristics of Heart Rate service 2015-01-27 14:42:21.738 BluetoothLE-Interact[8644:7087526] 2A37 2015-01-27 14:42:21.739 BluetoothLE-Interact[8644:7087526] 找到 心跳測量屬性 2015-01-27 14:42:21.740 BluetoothLE-Interact[8644:7087526] 2A38 2015-01-27 14:42:21.740 BluetoothLE-Interact[8644:7087526] 找到 心跳測量位置屬性 2015-01-27 14:42:21.741 BluetoothLE-Interact[8644:7087526] === Finished set notification === 2015-01-27 14:42:22.008 BluetoothLE-Interact[8644:7087526] =========== 7 Characteristics of Device Information service 2015-01-27 14:42:22.009 BluetoothLE-Interact[8644:7087526] System ID 2015-01-27 14:42:22.009 BluetoothLE-Interact[8644:7087526] Model Number String 2015-01-27 14:42:22.010 BluetoothLE-Interact[8644:7087526] Serial Number String 2015-01-27 14:42:22.010 BluetoothLE-Interact[8644:7087526] Firmware Revision String 2015-01-27 14:42:22.011 BluetoothLE-Interact[8644:7087526] Hardware Revision String 2015-01-27 14:42:22.011 BluetoothLE-Interact[8644:7087526] Software Revision String 2015-01-27 14:42:22.012 BluetoothLE-Interact[8644:7087526] Manufacturer Name String 2015-01-27 14:42:22.012 BluetoothLE-Interact[8644:7087526] === Finished set notification === 2015-01-27 14:42:22.157 BluetoothLE-Interact[8644:7087526] =========== 1 Characteristics of Battery service 2015-01-27 14:42:22.158 BluetoothLE-Interact[8644:7087526] Battery Level 2015-01-27 14:42:22.159 BluetoothLE-Interact[8644:7087526] === Finished set notification === 2015-01-27 14:42:22.277 BluetoothLE-Interact[8644:7087526] =========== 1 Characteristics of 6217FF49-AC7B-547E-EECF-016A06970BA9 service 2015-01-27 14:42:22.277 BluetoothLE-Interact[8644:7087526] 6217FF4A-B07D-5DEB-261E-2586752D942E 2015-01-27 14:42:22.278 BluetoothLE-Interact[8644:7087526] === Finished set notification === 2015-01-27 14:42:22.396 BluetoothLE-Interact[8644:7087526] 97 bpm 2015-01-27 14:42:22.517 BluetoothLE-Interact[8644:7087526] 位置: 胸部 2015-01-27 14:42:23.386 BluetoothLE-Interact[8644:7087526] 92 bpm 2015-01-27 14:42:24.406 BluetoothLE-Interact[8644:7087526] 91 bpm 2015-01-27 14:42:25.426 BluetoothLE-Interact[8644:7087526] 93 bpm 2015-01-27 14:42:26.446 BluetoothLE-Interact[8644:7087526] 91 bpm 2015-01-27 14:42:27.466 BluetoothLE-Interact[8644:7087526] 86 bpm 2015-01-27 14:42:28.456 BluetoothLE-Interact[8644:7087526] 91 bpm
UI 增加顯示心跳資訊
NSLog對於心跳測量資訊能正確顯示後,我們要將範例增加顯示資訊,這裡利用UITextField
進行顯示,以下為Storyboard的配置:
將UITextField進行綁定:
再將更新通知進行修改,更新資料時也將資料更新至UI,這邊資料更新UI處理程序綁定在轉換資料時進行更新UI:
//-----------start----------- - (void) conveterBPMData:(CBCharacteristic *)characteristic error:(NSError *)error { // Get the Heart Rate Monitor BPM NSData *data = [characteristic value]; // 1 const uint8_t *reportData = [data bytes]; uint16_t bpm = 0; //判定第0個byte之第0個bit值為1的話要將取得的byte反轉 (LSB->MSB) if ((reportData[0] & 0x01) == 0) { // 2 // Retrieve the BPM value for the Heart Rate Monitor bpm = reportData[1]; } else { bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1])); // 3 } // Display the heart rate value to the UI if no error occurred if( (characteristic.value) || !error ) { // 4 NSLog(@"%@",[NSString stringWithFormat:@"%i bpm", bpm]); self.textHeartRate.text = [NSString stringWithFormat:@"%i", bpm];//更新UI資料 } return; } - (void) conveterLocation:(CBCharacteristic *)characteristic { NSData *sensorData = [characteristic value];//取得資料 uint8_t *locationData = (uint8_t *)[sensorData bytes];//轉換成byte來取得特定的byte if (locationData ) { uint8_t sensorLocation = locationData[0]; /* 0 Other 1 Chest 胸部 2 Wrist 3 Finger 4 Hand 5 Ear Lobe 6 Foot 7 ~ 255 Reserved for future use 因Porla H7為胸部位置,所以針對取值後判斷是不是為1 */ NSLog(@"%@",[NSString stringWithFormat:@"位置: %@", sensorLocation == 1 ? @"胸部" : @"非位於胸部"]); // 3 self.textHeartRateLocation.text = [NSString stringWithFormat:@"%@", sensorLocation == 1 ? @"胸部" : @"非位於胸部"];//更新UI資料 } else { NSLog(@"%@",[NSString stringWithFormat:@"位置: 未知"]); self.textHeartRateLocation.text = [NSString stringWithFormat:@"未知"];//更新UI資料 } return; } //------------end------------
修改完成後,UI上面就多了心跳測量的資料更新:
總結
如果你是一路從(1)~(7)都看完,這些累績的結果希望能讓你了解到如何讀取BLE週邊(心跳表的資料)的過程,最後的範例程式為這系列範例程式最終的範例程式,如有其他意見或錯誤的地方留言給我。