iPhone - Polar H7_heart_rate_sensor connectivity u

2019-06-09 03:40发布

问题:

Please suggest me, whether an iPhone application can connect with external bluetooth devices like polar h7/h6 heart rate sensor fitness belt (http://www.polar.com/en/products/accessories/H7_heart_rate_sensor)?

I am following these links: http://www.tekritisoftware.com/scan-ble-devices-using-ios-core-bluetooth-framework https://github.com/sergiomtzlosa/CoreBluetooth-Demo/

But i am not sure, whether iPhone app will connect to this external fitness belt. Please guide.

Thanks.

回答1:

So: Bluetooth Smart is the branded name of Bluetooth Low Energy.
Source: Bluetooth.com and WikiPedia.
The Apple Sample: CoreBluetooth Temperature Sensor and the iOS app LightBlue (to check/test) are great starts.



回答2:

Below is the logic for a fully working example to scan for bluetooth HRM devices, discover their services and their characteristics, followed by logic to extract data from notifications. Hopefully the comments within the code are self explaining. Within the code I have added some url's to the related bluetooth specs.

The ViewController.h file:

#import <UIKit/UIKit.h>
@import CoreBluetooth;


#define HRM_HEART_RATE_SERVICE_UUID @"180D"
#define DEVICE_INFO_SERVICE_UUID @"180A"

#define HRM_MEASUREMENT_CHARACTERISTIC_UUID @"2A37"
#define HRM_BODY_LOCATION_CHARACTERISTIC_UUID @"2A38"
#define DEVICE_MANUFACTURER_NAME_CHARACTERISTIC_UUID @"2A29"

@interface TestHRMViewController : UIViewController <CBCentralManagerDelegate, CBPeripheralDelegate>

@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheral *hrmPeripheral;


// Instance method to get the heart rate BPM information
- (void) getHeartBPMData:(CBCharacteristic *)characteristic error:(NSError *)error;
- (void) getBodyLocation:(CBCharacteristic *)characteristic;

// Instance methods to grab device Manufacturer Name, Body Location
- (void) getManufacturerName:(CBCharacteristic *)characteristic;

@end

The ViewController.m file:

#import "TestHRMViewController.h"

@interface TestHRMViewController ()

@end

@implementation TestHRMViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // Create the CoreBluetooth CentralManager //
    CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    self.centralManager = centralManager;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


#pragma mark - CBCentralManagerDelegate

// Method called whenever you have successfully connected to the BLE peripheral //
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    // Set the delegate of the peripheral //
    [peripheral setDelegate:self];

    // Tell the peripheral to discover services //
    // When the peripheral discovers one or more services, it calls the peripheral:didDiscoverServices: method //
    [peripheral discoverServices:nil];

    NSString *connected = [NSString stringWithFormat:@"Connected: %@", peripheral.state == CBPeripheralStateConnected ? @"YES" : @"NO"];

    NSLog(@"%@", connected);

}

// Method called when an existing connection with a peripheral is disconnected //
// If the disconnection was not initiated by cancelPeripheralConnection: the cause is detailed in error //
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSString *connected = [NSString stringWithFormat:@"Connected: %@", peripheral.state == CBPeripheralStateConnected ? @"YES" : @"NO"];

    NSLog(@"%@", connected);
}

// Method called with the CBPeripheral class as its main input parameter //
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    // Check to make sure that the device has a non-empty local name //
    NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
    if ([localName length] > 0){
        NSLog(@"Found the HeartRate monitor: %@", localName);

        // Stop scanning //
        [self.centralManager stopScan];

        // Store peripheral //
        self.hrmPeripheral = peripheral;
        peripheral.delegate = self;

        // Connect to peripheral //
        [self.centralManager connectPeripheral:peripheral options:nil];

    }
    else{
        NSLog(@"Device with no localName");
    }
}

// Method called whenever the device state changes //
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    // Determine the state of the CentralManager //
    // (To make sure this iOS device is Bluetooth low energy compliant and it can be used as the CentralManager) //
    if ([central state] == CBCentralManagerStatePoweredOff) {
        NSLog(@"CoreBluetooth BLE hardware is powered off");
    }
    else if ([central state] == CBCentralManagerStatePoweredOn) {
        NSLog(@"CoreBluetooth BLE hardware is powered on and ready");

        // Create an array with Bluetooth-services you wish to detect //
        NSArray *services = @[[CBUUID UUIDWithString:HRM_HEART_RATE_SERVICE_UUID], [CBUUID UUIDWithString:DEVICE_INFO_SERVICE_UUID]];
        // Start scanning for services //
        [self.centralManager scanForPeripheralsWithServices:services options:nil];

    }
    else if ([central state] == CBCentralManagerStateUnauthorized) {
        NSLog(@"CoreBluetooth BLE state is unauthorized");
    }
    else if ([central state] == CBCentralManagerStateUnknown) {
        NSLog(@"CoreBluetooth BLE state is unknown");
    }
    else if ([central state] == CBCentralManagerStateUnsupported) {
        NSLog(@"CoreBluetooth BLE hardware is unsupported on this platform");
    }
}


#pragma mark - CBPeripheralDelegate

// Method called when the peripheral's available services are discovered //
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    // Walk through all services //
    for (CBService *service in peripheral.services){
        NSLog(@"Discovered service: %@", service.UUID);

        // Ask to discover characteristics for service //
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

// Method called when the characteristics of a specified service are discovered //
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    // Check if service is HeartRate service //
    if ([service.UUID isEqual:[CBUUID UUIDWithString:HRM_HEART_RATE_SERVICE_UUID]]){

        // If so, iterate through the characteristics array and determine if the characteristic is a HeartRateMeasurement characteristic //
        // If so, you subscribe to this characteristic //

        for (CBCharacteristic *aChar in service.characteristics){

            // Request HeartRateMeasurement notifications //
            if ([aChar.UUID isEqual:[CBUUID UUIDWithString:HRM_MEASUREMENT_CHARACTERISTIC_UUID]]){

                [self.hrmPeripheral setNotifyValue:YES forCharacteristic:aChar];
                NSLog(@"Found heart rate measurement characteristic");
            }

            // Read BodySensorLocation once instead of subscribing to notifications //
            else if ([aChar.UUID isEqual:[CBUUID UUIDWithString:HRM_BODY_LOCATION_CHARACTERISTIC_UUID]]){

                [self.hrmPeripheral readValueForCharacteristic:aChar];
                NSLog(@"Found body sensor location characteristic");
            }

        }

    }

    // Check if service is DeviceInformation service for Manufacturer name //
    if ([service.UUID isEqual:[CBUUID UUIDWithString:DEVICE_INFO_SERVICE_UUID]]){
        for (CBCharacteristic *aChar in service.characteristics){

            // Read Manufacturer name once instead of subscribing to notifications //
            if ([aChar.UUID isEqual:[CBUUID UUIDWithString:DEVICE_MANUFACTURER_NAME_CHARACTERISTIC_UUID]]){

                [self.hrmPeripheral readValueForCharacteristic:aChar];
                NSLog(@"Found a device manufacturer name characteristic");
            }
        }
    }

}

// Method called when you retrieve a specified characteristic's value //
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    // New value for HeartRateMeasurement received //
    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:HRM_MEASUREMENT_CHARACTERISTIC_UUID]]){
        // Get HeartRate data //
        [self getHeartBPMData:characteristic error:error];
    }

    // Characteristic value for ManufacturerName received //
    else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:DEVICE_MANUFACTURER_NAME_CHARACTERISTIC_UUID]]){
        // Get ManufacturerName //
        [self getManufacturerName:characteristic];
    }

    // Characteristic value for BodySensorLocation received //
    else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:HRM_BODY_LOCATION_CHARACTERISTIC_UUID]]){
        // Get BodySensorLocation //
        [self getBodyLocation:characteristic];
    }

}


#pragma mark - CBCharacteristic helpers

// Method to get the heart rate BPM data //
- (void) getHeartBPMData:(CBCharacteristic *)characteristic error:(NSError *)error
{
    // Get the BPM //
    // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml //

    // Convert the contents of the characteristic value to a data-object //
    NSData *data = [characteristic value];

    // Get the byte sequence of the data-object //
    const uint8_t *reportData = [data bytes];

    // Initialise the offset variable //
    NSUInteger offset = 1;
    // Initialise the bpm variable //
    uint16_t bpm = 0;


    // Next, obtain the first byte at index 0 in the array as defined by reportData[0] and mask out all but the 1st bit //
    // The result returned will either be 0, which means that the 2nd bit is not set, or 1 if it is set //
    // If the 2nd bit is not set, retrieve the BPM value at the second byte location at index 1 in the array //
    if ((reportData[0] & 0x01) == 0) {
        // Retrieve the BPM value for the Heart Rate Monitor
        bpm = reportData[1];

        offset = offset + 1; // Plus 1 byte //
    }
    else {
        // If the second bit is set, retrieve the BPM value at second byte location at index 1 in the array and //
        // convert this to a 16-bit value based on the host’s native byte order //
        bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1]));

        offset =  offset + 2; // Plus 2 bytes //
    }
    NSLog(@"bpm: %i", bpm);



    // Determine if EE data is present //
    // If the 3rd bit of the first byte is 1 this means there is EE data //
    // If so, increase offset with 2 bytes //
    if ((reportData[0] & 0x03) == 1) {
        offset =  offset + 2; // Plus 2 bytes //
    }



    // Determine if RR-interval data is present //
    // If the 4th bit of the first byte is 1 this means there is RR data //
    if ((reportData[0] & 0x04) == 0)
    {
        NSLog(@"%@", @"Data are not present");
    }
    else
    {
        // The number of RR-interval values is total bytes left / 2 (size of uint16) //

        NSUInteger length = [data length];
        NSUInteger count = (length - offset)/2;
        NSLog(@"RR count: %lu", (unsigned long)count);

        for (int i = 0; i < count; i++) {

            // The unit for RR interval is 1/1024 seconds //
            uint16_t value = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[offset]));
            value = ((double)value / 1024.0 ) * 1000.0;


            offset = offset + 2; // Plus 2 bytes //

            NSLog(@"RR value %lu: %u", (unsigned long)i, value);

        }

    }

}

// Instance method to get the body location of the device //
- (void) getBodyLocation:(CBCharacteristic *)characteristic
{
    // Get the BodySensorLocation //
    // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml //

    NSData *sensorData = [characteristic value];

    // Convert the characteristic value to a data object consisting of byte sequences //
    uint8_t *bodyData = (uint8_t *)[sensorData bytes];

    // Determine if you have device body location data //
    if (bodyData){
        // Access the first byte at index 0 in your array //
        uint8_t bodyLocation = bodyData[0];

        // Determine if it is 'Chest' or something else (other values mean other locations, see url) //
        NSString *sensorLocation = [NSString stringWithFormat:@"Body Location: %@", bodyLocation == 1 ? @"Chest" : @"Undefined"];
        NSLog(@"SensorLocation: %@", sensorLocation);

    }
    else {
        // If no data is available, log N/A as the body location //
        NSLog(@"SensorLocation: N?A");
    }

}

// Method to get the manufacturer name of the device //
- (void) getManufacturerName:(CBCharacteristic *)characteristic
{
    // Get the ManufacturerName //
    // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.manufacturer_name_string.xml //

    NSString *manufacturerName = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
    NSLog(@"ManufacturerName: %@", manufacturerName);

}


@end


回答3:

Get Swift implementation of POLAR https://github.com/msyshani/PolarDemo-for-iOS

import UIKit
import CoreBluetooth
import QuartzCore


protocol polarDeledate{

    func updateStatus(bpm:String)
    func updateBPM(status:String)
}


class MSYPolarH7: NSObject , CBCentralManagerDelegate, CBPeripheralDelegate {


    let POLARH7_HRM_DEVICE_INFO_SERVICE_UUID = "180A"
    let POLARH7_HRM_HEART_RATE_SERVICE_UUID = "180D"


    let POLARH7_HRM_MEASUREMENT_CHARACTERISTIC_UUID = "2A37"
    let POLARH7_HRM_BODY_LOCATION_CHARACTERISTIC_UUID = "2A38"
    let POLARH7_HRM_MANUFACTURER_NAME_CHARACTERISTIC_UUID = "2A29"


    var polarDel:polarDeledate?


    //MARK:- Var Init

    var centralManager:CBCentralManager?
    var polarH7HRMPeripheral:CBPeripheral?

    //MARK:- Make Singleton
    class var sharedInstance: MSYPolarH7 {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: MSYPolarH7? = nil
        }
        dispatch_once(&Static.onceToken) {

            Static.instance = MSYPolarH7()

        }
        return Static.instance!
    }


    func startScanningDevice(){
        let cManager=CBCentralManager(delegate: self, queue: dispatch_get_main_queue())
        //cManager.delegate=self

       // cManager.scanForPeripheralsWithServices([CBUUID(string: POLARH7_HRM_DEVICE_INFO_SERVICE_UUID),CBUUID(string: POLARH7_HRM_HEART_RATE_SERVICE_UUID)], options: nil)
        cManager.scanForPeripheralsWithServices(nil, options: nil)
        self.centralManager=cManager
    }


    //MARK:- CBCentralManager Delagates

    func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
        peripheral.delegate=self
        peripheral.discoverServices(nil)

        if peripheral.state == CBPeripheralState.Connected {
            print("Connected")
            if let msyPolar = polarDel {
                msyPolar.updateStatus("Connected")
            }
        }else{
            print("Not connected")
            if let msyPolar = polarDel {
                msyPolar.updateStatus("Not connected")
            }

        }
    }

    func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
        print("dis connected")
        if let msyPolar = polarDel {
            msyPolar.updateStatus("Disconnected")
        }
        self.centralManager?.connectPeripheral(peripheral, options: nil)
    }

    func centralManager(central: CBCentralManager, willRestoreState dict: [String : AnyObject]) {
        print("Restored....")
    }


    func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
        let localName=advertisementData[CBAdvertisementDataLocalNameKey]
        if localName?.length > 0 {
            print("Found device is \(localName)")
            self.centralManager?.stopScan()
            peripheral.delegate=self
            self.centralManager?.connectPeripheral(peripheral, options: nil)
            self.polarH7HRMPeripheral=peripheral
        }

    }

    func centralManagerDidUpdateState(central: CBCentralManager) {

        if(central.state == CBCentralManagerState.PoweredOff){
            print("CoreBluetooth BLE hardware is powered off")
        }else if(central.state == CBCentralManagerState.PoweredOn){
            print("CoreBluetooth BLE hardware is powered on and ready")
            self.centralManager?.scanForPeripheralsWithServices([CBUUID(string: POLARH7_HRM_DEVICE_INFO_SERVICE_UUID),CBUUID(string: POLARH7_HRM_HEART_RATE_SERVICE_UUID)], options: nil)
        }else if(central.state == CBCentralManagerState.Unauthorized){
            print("CoreBluetooth BLE state is unauthorized")
        }else if(central.state == CBCentralManagerState.Unknown){
            print("CoreBluetooth BLE state is unknown")
        }else if(central.state == CBCentralManagerState.Unsupported){
            print("CoreBluetooth BLE hardware is unsupported on this platform")
        }


    }



    //MARK:- CBPeripheralDelegate Delagates

    func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {

        for service:CBService in peripheral.services! {
            print("discover service \(service.UUID)")
            peripheral.discoverCharacteristics(nil, forService: service)
        }

    }


    func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {

        if service.UUID == CBUUID(string: POLARH7_HRM_HEART_RATE_SERVICE_UUID) {
            for aChar:CBCharacteristic in service.characteristics! {
                // Request heart rate notifications
                if aChar.UUID == CBUUID(string: POLARH7_HRM_MEASUREMENT_CHARACTERISTIC_UUID) {
                    self.polarH7HRMPeripheral?.setNotifyValue(true, forCharacteristic: aChar)
                    print("Found heart rate measurement characteristic")
                }else if aChar.UUID == CBUUID(string: POLARH7_HRM_BODY_LOCATION_CHARACTERISTIC_UUID){
                     self.polarH7HRMPeripheral?.readValueForCharacteristic(aChar)
                    print("Found body sensor location characteristic")
                }
            }
        }



        // Retrieve Device Information Services for the Manufacturer Name

        if service.UUID == CBUUID(string: POLARH7_HRM_DEVICE_INFO_SERVICE_UUID) {
            for aChar:CBCharacteristic in service.characteristics! {
                // Request heart rate notifications
                if aChar.UUID == CBUUID(string: POLARH7_HRM_MANUFACTURER_NAME_CHARACTERISTIC_UUID) {
                     self.polarH7HRMPeripheral?.readValueForCharacteristic(aChar)
                    print("Found a device manufacturer name characteristic")
                }
            }
        }

    }


    func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {

        // Updated value for heart rate measurement received
        if characteristic.UUID == CBUUID(string: POLARH7_HRM_MEASUREMENT_CHARACTERISTIC_UUID) {
            //print(characteristic)
            //self.getHeartBPMData(characteristic, error:error!)
            self.getHeartBPMData(characteristic)
        }


        // Retrieve the characteristic value for manufacturer name received
        if characteristic.UUID == CBUUID(string: POLARH7_HRM_MANUFACTURER_NAME_CHARACTERISTIC_UUID ) {
            getManufacturerName(characteristic)
        }


        // Retrieve the characteristic value for the body sensor location received
        else if characteristic.UUID == CBUUID(string: POLARH7_HRM_BODY_LOCATION_CHARACTERISTIC_UUID ) {
            getBodyLocation(characteristic)
        }


    }


    //MARK:- CBCharacteristic helpers

   // func getHeartBPMData(characteristic:CBCharacteristic,error:NSError)
    func getHeartBPMData(characteristic:CBCharacteristic){

       //print("Hello")
       // print("characteristic \(characteristic)")

        if characteristic.value == nil {
            return
        }


        let data = characteristic.value
        let reportData = UnsafePointer<UInt8>(data!.bytes)
        var bpm : UInt16
        if (reportData[0] & 0x01) == 0 {
            bpm = UInt16(reportData[1])
        } else {
            bpm = UnsafePointer<UInt16>(reportData + 1)[0]
            bpm = CFSwapInt16LittleToHost(bpm)
        }

        let outputString = String(bpm)
        print("bpm is \(outputString)")

        if let msyPolar = polarDel {
            msyPolar.updateBPM(outputString)
        }

        //doHeartBeat()

        return

       // return outputString

    }

    func getManufacturerName(characteristic:CBCharacteristic){
        let manufacturerName=NSString(data: characteristic.value!, encoding: NSUTF8StringEncoding)
        print("Manufacturer is \(manufacturerName)")

        return

    }


}