I'm trying to set up my iPhone to act as a heart rate monitor and send information using the standard heart rate service so that the app I have running on my PC can retrieve the data. I'm a newbie to iOS but I have got bluetooth stuff running on Android and Windows before. I'm following the information here and I'm falling at the first hurdle...
As specified in the doc, I first call
CBPeripheralManager *myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:(id<CBPeripheralManagerDelegate>)self queue:nil options:nil];
I also implement the peripheralManagerDidUpdateState callback but here lies the problem. This callback never runs! I have waited for minutes and nothing has happened. I suspect I'm missing a very simple step because I have searched all day and have found nothing online! Can anyone explain the explicit steps to take here both in code and in physically doing stuff with my iPhone? Does it have to have a connection before this callback runs? Does it have to be paired to anything?
Here's the full code. It all runs fine, I'm getting no errors at all.
#import "ViewController.h"
@import CoreBluetooth;
@interface ViewController()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"App running...");
//Create the peripheral manager
CBPeripheralManager *myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:(id<CBPeripheralManagerDelegate>)self queue:nil options:nil];
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
int state = peripheral.state;
NSLog(@"Peripheral manager state = %d", state);
//Set the UUIDs for service and characteristic
CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];
CBUUID *heartRateCharacteristicUUID = [CBUUID UUIDWithString:@"2A37"];
CBUUID *heartRateSensorLocationCharacteristicUUID = [CBUUID UUIDWithString:@"0x2A38"];
//char heartRateData[2]; heartRateData[0] = 0; heartRateData[1] = 60;
//Create the characteristics
CBMutableCharacteristic *heartRateCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:heartRateCharacteristicUUID
properties: CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
CBMutableCharacteristic *heartRateSensorLocationCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:heartRateSensorLocationCharacteristicUUID
properties:CBCharacteristicPropertyRead
value:nil
permissions:CBAttributePermissionsReadable];
//Create the service
CBMutableService *myService = [[CBMutableService alloc] initWithType:heartRateServiceUUID primary:YES];
myService.characteristics = @[heartRateCharacteristic, heartRateSensorLocationCharacteristic];
//Publish the service
NSLog(@"Attempting to publish service...");
[peripheral addService:myService];
//Set the data
NSDictionary *data = @{CBAdvertisementDataLocalNameKey:@"iDeviceName",
CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:@"180D"]]};
//Advertise the service
NSLog(@"Attempting to advertise service...");
[peripheral startAdvertising:data];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didAddService:(CBService *)service
error:(NSError *)error {
if (error) {
NSLog(@"Error publishing service: %@", [error localizedDescription]);
}
else NSLog(@"Service successfully published");
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
error:(NSError *)error {
if (error) {
NSLog(@"Error advertising: %@", [error localizedDescription]);
}
else NSLog(@"Service successfully advertising");
}
@end
The object myPeripheralManager
is deallocated as soon as viewDidLoad
method returns, as you have only one reference pointing to this object and it goes out of scope.
The solution is to create a property in ViewController
which references the instance of CBPeripheralManager
:
@interface ViewController()
@property (nonatomic, strong) CBPeripheralManager *myPeripheralManager;
@end
and then initialise the property in viewDidLoad
:
self.myPeripheralManager = myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:(id<CBPeripheralManagerDelegate>)self queue:nil options:nil];
You may want to read more about memory management (in particular, Automatic Reference Counting) in Objective-C.
My solution on this matter is:
import UIKit
import CoreBluetooth
class BluetoothManager: NSObject, CBPeripheralManagerDelegate {
static let sharedInstance = BluetoothManager()
var bluetoothPeripheralManager: CBPeripheralManager?
override fileprivate init() {
}
func start() {
bluetoothPeripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
}
// MARK - CBCentralManagerDelegate
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
var statusMessage = ""
switch peripheral.state {
case .poweredOn:
statusMessage = "Bluetooth Status: Turned On"
case .poweredOff:
statusMessage = "Bluetooth Status: Turned Off"
case .resetting:
statusMessage = "Bluetooth Status: Resetting"
case .unauthorized:
statusMessage = "Bluetooth Status: Not Authorized"
case .unsupported:
statusMessage = "Bluetooth Status: Not Supported"
default:
statusMessage = "Bluetooth Status: Unknown"
}
print(statusMessage)
if peripheral.state == .poweredOff {
//TODO: Update this property in an App Manager class
}
}}
Usage:
BluetoothManager.sharedInstance.start()
Start up your up and turn/off and bluetooth a log message should appear indicating bluetooth status.
来源:https://stackoverflow.com/questions/31248908/ios-bluetooth-peripheralmanagerdidupdatestate-never-called