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周边(心跳表的资料)的过程,最后的范例程式为这系列范例程式最终的范例程式,如有其他意见或错误的地方留言给我。