CoreBluetooth For Central (7)

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)了解到2A372A38,所以进行处理:

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