0

请建议我,iPhone 应用程序是否可以连接外部蓝牙设备,如 Polar h7/h6 心率传感器健身带(http://www.polar.com/en/products/accessories/H7_heart_rate_sensor)?

我正在关注这些链接: http ://www.tekritisoftware.com/scan-ble-devices-using-ios-core-bluetooth-framework https://github.com/sergiomtzlosa/CoreBluetooth-Demo/

但我不确定 iPhone 应用程序是否会连接到这个外部健身腰带。请指导。

谢谢。

4

3 回答 3

2

下面是一个完整工作示例的逻辑,用于扫描蓝牙 HRM 设备、发现它们的服务及其特征,然后是从通知中提取数据的逻辑。希望代码中的注释是自我解释的。在代码中,我向相关的蓝牙规范添加了一些 url。

ViewController.h 文件:

#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

ViewController.m 文件:

#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
于 2014-11-12T08:14:03.280 回答
2

获取 POLAR 的 Swift 实现 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

    }


}
于 2016-09-26T08:24:59.180 回答
1

所以:Bluetooth SmartBluetooth Low Energy的品牌名称。
资料来源:Bluetooth.com维基百科
Apple 示例:CoreBluetooth 温度传感器和 iOS 应用LightBlue(用于检查/测试)是很好的开始。

于 2013-05-16T11:56:35.963 回答