OBJ-C 连接到蓝牙服务
OBJ-C Connect to Services in Bluetooth
***** 编辑:我现在所做的一切,请参阅下面接受的答案并更新此问题中的代码... *****
好的,我对微控制器代码非常陌生,但对
Objective-c 蓝牙代码。
我想要的
我想用一个 iOS 应用程序连接到我的蓝牙 Bluefruit Friend UART LE(NRF51822 芯片)并向它发送一个 128 位令牌并让它响应一个 HID 消费者代码。
我试过的
目前,我可以使用串行监视器连接到芯片并发送我想要的消费者代码,它会通过执行该操作进行响应。因此,如果我发送代码使设备或音量 up/down 静音,它将在我的 iOS 设备中 mute/volume up/down。这很棒!
现在我需要在笔记本电脑上没有串行监视器的情况下执行此操作,并引入发送令牌而不是 HID 代码的概念,并让它以 HID 代码响应。这非常棘手,似乎向我介绍了服务和渠道等新概念。似乎大多数蓝牙设备都带有许多可用的自定义服务,开发人员可以添加这些服务并允许将读取与写入操作分开。每项服务都允许一些特征,例如 read/write/notify/update 等等。
所以...我假设可以通过这些服务使用我想要的解决方案。如果我有一个用于读取令牌的服务和另一个用于写入 GATT HID Consumer Code
的服务,那么我应该能够通过 objective-c 中的蓝牙分别连接到每个服务。这将使我能够区分发送令牌和接收回代码。
问题
如您所见,我在下面的代码中加入了很多 NSLog,目前 none 这些日志显示了我放在盒子上的任何服务。服务总是打印为零。如果我使用名为 RF Connect
的应用商店中的 iOS 应用程序,我可以连接到盒子,我可以看到这些服务肯定在那里...
我试过的代码
这是我一直用来尝试检测设备服务的当前代码...我认为这是一个很好的起点。
BluetoothMgr.h
#import <Foundation/Foundation.h>
@protocol BluetoothMgrDelegate <NSObject>
@optional
- (void)didConnectToDevice;
- (void)didDisconnectFromDevice;
@end
@interface BluetoothMgr : NSObject {
}
@property (nonatomic, weak) id <BluetoothMgrDelegate> delegate;
+ (BluetoothMgr *)sharedInstance;
- (void)scanForPeripherals;
- (void)disconnectDevice;
- (void)connectToDevice;
@end
BluetoothMgr.m
#define SERVICE_UUID @"0000BBB5-2222-3333-4444-AABBCCDDEEFF"
#define CARACHTERISTIC_UUID @"0000BBB6-1111-2222-3333-AABBCCDDEEFF"
#import "BluetoothMgr.h"
#import <UIkit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import <QuartzCore/QuartzCore.h>
@interface BluetoothMgr () <CBCentralManagerDelegate, CBPeripheralDelegate> {
int count;
}
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) CBPeripheral *myPeripheral;
@property (nonatomic, strong) CBUUID *deviceUUID;
@property (nonatomic, strong) CBService *functionalityServiceLayer;
@property (nonatomic, strong) CBCharacteristic *characteristic;
@end
@implementation BluetoothMgr
+ (BluetoothMgr *)sharedInstance {
static BluetoothMgr *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[BluetoothMgr alloc] init];
});
return _sharedInstance;
}
- (id)init {
self = [super init];
if(self) {
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:@{ CBCentralManagerOptionRestoreIdentifierKey : @"DriveSafeDevice"}];
}
return self;
}
- (void)connectToDevice {
if(_myPeripheral != nil) {
[self.centralManager connectPeripheral:self.myPeripheral
options:@{ CBConnectPeripheralOptionNotifyOnConnectionKey: [NSNumber numberWithInt:1] }
];
}
}
- (void)scanForPeripherals {
NSLog(@"*** scan for Bluetooth peripherals");
if (_centralManager.state == CBCentralManagerStatePoweredOn) {
NSLog(@"--- central manager powered on. Start scanning.");
[self.centralManager scanForPeripheralsWithServices:@[self.deviceUUID] options:nil];
}
}
- (void)disconnectDevice {
if (!(self.centralManager == nil || _myPeripheral == nil)) {
[self.centralManager cancelPeripheralConnection:_myPeripheral];
}
}
#pragma mark - CBCentralManager delegate methods
- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary<NSString *,id> *)state {
NSLog(@"--- will restore state.");
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
NSLog(@"centralManagerDidUpdateState invoked...");
// Determine the state of the peripheral
if ([central state] == CBCentralManagerStatePoweredOff) {
NSLog(@"CoreBluetooth BLE hardware is powered off");
} else if (central.state == CBCentralManagerStatePoweredOn) {
NSLog(@"CoreBluetooth BLE hardware is powered on.");
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@"did discover characteristic! peripheral: %@", peripheral);
NSLog(@"did discover characteristic! characteristic: %@", characteristic);
NSLog(@"did discover characteristic! error: %@", error);
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
NSLog(@"did discover service! peripheral: %@", peripheral);
NSLog(@"services??: %@", peripheral.services);
NSLog(@"did discover service! error: %@", error);
}
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"peripheral: %@", peripheral);
NSLog(@"advertisement data: %@", advertisementData);
if([peripheral.name containsString:@"Adafruit Bluefruit LE"]) {
NSLog(@"central: %@", central);
NSLog(@"peripheral: %@", peripheral);
NSLog(@"advertisement data: %@", advertisementData);
NSLog(@"RSSI: %@", RSSI);
NSLog(@"found peripheral services!: %@", peripheral.services);
self.myPeripheral = peripheral;
self.myPeripheral.delegate = self;
//self.serviceUUID = ;
self.deviceUUID = [CBUUID UUIDWithString:[peripheral.identifier UUIDString]];
[self connectToDevice];
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"--- didConnectPeripheral");
NSLog(@"peripheral in didConnect function: %@", peripheral);
NSLog(@"any services??: %@", peripheral.services);
[peripheral discoverServices:@[self.deviceUUID]];
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) {
NSLog(@"--- connected to peripheral in foreground");
[[NSNotificationCenter defaultCenter] postNotificationName:@"connectedToDevice" object:self];
[self triggerLocalNotification];
} else {
NSLog(@"--- connected to peripheral in background");
[[NSNotificationCenter defaultCenter] postNotificationName:@"connectedToDevice" object:self];
[self triggerLocalNotification];
//[self.delegate didConnectToDevice];
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error {
NSLog(@"--- did disconnect ConnectPeripheral");
}
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices {
NSLog(@"peripheral services: %@", peripheral.services);
NSLog(@"services invalidated: %@", invalidatedServices);
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"Failed to connect");
}
- (void)sendCodeToBTDevice:(NSString *)code
characteristic:(CBCharacteristic *)characteristic {
if(code != nil) {
NSData *data = [code dataUsingEncoding:NSUTF8StringEncoding];
[self.ourPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
@end
最后的想法...
我在如何在 Arduino 板/任何 BT 设备中编写自定义服务/特性方面的知识差距显然是这里的问题所在。我不确定我是否应该在设备或我的 OBJ-C 应用程序中创建 services/characteristics。
现在考虑到这些是在设备而不是应用程序中创建的是多么明显,这个概念现在很可笑。
是的,这是可能的。在 HID 设备中设置您要更新以通知的特征。然后在应用程序中 setNotify
到 true
并收听 didUpdateValueFor
Reference
然后你要做的就是写入你想要写入的CBCharacteristic
,一旦写入另一个CBCharacteristic
,就会调用didUpdateValueFor
。
对于在 Google 上搜索的任何人,我最终在这里找到了关于这个问题的正确答案。
原来我需要在外围BT设备(不是iOS设备)上创建自定义services/characteristics。一旦我创建了这些用于接收 128 位令牌字符串的自定义服务,就很容易让设备在 HID 的单独服务层上响应。
我已经更新了上面的代码以反映此答案,因此请查看代码以遵循此答案。
在我的蓝牙管理器 didDiscoverPeripheral
方法中 class,我确保设备 UUID 与已知设备 UUID 匹配。通过 UUID 找到正确的设备后,我将外围对象的(默认情况下由本机方法传入)委托设置为 self
(因为我的 class 使用外围委托),然后我分配self.ourPeripheral
属性 of class 到当前外设obj。一旦我都设置好了,我启动我的自定义 connectToDevice
方法,该方法对齐 centralManager
属性 以连接给定的外围设备。
在 centralManager 委托给出的 didConnectPeripheral
方法中,我调用了 [peripheral discoverServices:nil];
,它启动了名为 didDiscoverServices
的委托方法。然后我从这里迭代所有发现的服务,寻找我在我构建的 BT 外围设备的自定义服务中设置的预定服务 UUID。一旦我在盒子上找到了定制服务,我就会打电话给 [peripheral discoverCharacteristics:nil forService:service];
。这也会自动调用 didDiscoverCharacteristicsForService
的委托方法(如您所料)。再一次,更多的迭代,在这个用例中,我改为搜索我在我的服务中写在盒子上的特征 UUID,用于收听 BT 串行通信。
一旦找到正确的特征,我就将其全局保存为 class 的强 属性,然后我可以使用上面显示的 sendCodeToBTDevice
方法。这会将数据作为 text/string 通过 BT 写入设备,由盒子通过串行接收,解释为 128 位令牌并进行验证。如果有效,它会在不同的服务层上响应,并根据请求发回符合 HID 标准的消费者代码。
***** 编辑:我现在所做的一切,请参阅下面接受的答案并更新此问题中的代码... *****
好的,我对微控制器代码非常陌生,但对 Objective-c 蓝牙代码。
我想要的
我想用一个 iOS 应用程序连接到我的蓝牙 Bluefruit Friend UART LE(NRF51822 芯片)并向它发送一个 128 位令牌并让它响应一个 HID 消费者代码。
我试过的
目前,我可以使用串行监视器连接到芯片并发送我想要的消费者代码,它会通过执行该操作进行响应。因此,如果我发送代码使设备或音量 up/down 静音,它将在我的 iOS 设备中 mute/volume up/down。这很棒!
现在我需要在笔记本电脑上没有串行监视器的情况下执行此操作,并引入发送令牌而不是 HID 代码的概念,并让它以 HID 代码响应。这非常棘手,似乎向我介绍了服务和渠道等新概念。似乎大多数蓝牙设备都带有许多可用的自定义服务,开发人员可以添加这些服务并允许将读取与写入操作分开。每项服务都允许一些特征,例如 read/write/notify/update 等等。
所以...我假设可以通过这些服务使用我想要的解决方案。如果我有一个用于读取令牌的服务和另一个用于写入 GATT HID Consumer Code
的服务,那么我应该能够通过 objective-c 中的蓝牙分别连接到每个服务。这将使我能够区分发送令牌和接收回代码。
问题
如您所见,我在下面的代码中加入了很多 NSLog,目前 none 这些日志显示了我放在盒子上的任何服务。服务总是打印为零。如果我使用名为 RF Connect
的应用商店中的 iOS 应用程序,我可以连接到盒子,我可以看到这些服务肯定在那里...
我试过的代码
这是我一直用来尝试检测设备服务的当前代码...我认为这是一个很好的起点。
BluetoothMgr.h
#import <Foundation/Foundation.h>
@protocol BluetoothMgrDelegate <NSObject>
@optional
- (void)didConnectToDevice;
- (void)didDisconnectFromDevice;
@end
@interface BluetoothMgr : NSObject {
}
@property (nonatomic, weak) id <BluetoothMgrDelegate> delegate;
+ (BluetoothMgr *)sharedInstance;
- (void)scanForPeripherals;
- (void)disconnectDevice;
- (void)connectToDevice;
@end
BluetoothMgr.m
#define SERVICE_UUID @"0000BBB5-2222-3333-4444-AABBCCDDEEFF"
#define CARACHTERISTIC_UUID @"0000BBB6-1111-2222-3333-AABBCCDDEEFF"
#import "BluetoothMgr.h"
#import <UIkit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import <QuartzCore/QuartzCore.h>
@interface BluetoothMgr () <CBCentralManagerDelegate, CBPeripheralDelegate> {
int count;
}
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) CBPeripheral *myPeripheral;
@property (nonatomic, strong) CBUUID *deviceUUID;
@property (nonatomic, strong) CBService *functionalityServiceLayer;
@property (nonatomic, strong) CBCharacteristic *characteristic;
@end
@implementation BluetoothMgr
+ (BluetoothMgr *)sharedInstance {
static BluetoothMgr *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[BluetoothMgr alloc] init];
});
return _sharedInstance;
}
- (id)init {
self = [super init];
if(self) {
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:@{ CBCentralManagerOptionRestoreIdentifierKey : @"DriveSafeDevice"}];
}
return self;
}
- (void)connectToDevice {
if(_myPeripheral != nil) {
[self.centralManager connectPeripheral:self.myPeripheral
options:@{ CBConnectPeripheralOptionNotifyOnConnectionKey: [NSNumber numberWithInt:1] }
];
}
}
- (void)scanForPeripherals {
NSLog(@"*** scan for Bluetooth peripherals");
if (_centralManager.state == CBCentralManagerStatePoweredOn) {
NSLog(@"--- central manager powered on. Start scanning.");
[self.centralManager scanForPeripheralsWithServices:@[self.deviceUUID] options:nil];
}
}
- (void)disconnectDevice {
if (!(self.centralManager == nil || _myPeripheral == nil)) {
[self.centralManager cancelPeripheralConnection:_myPeripheral];
}
}
#pragma mark - CBCentralManager delegate methods
- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary<NSString *,id> *)state {
NSLog(@"--- will restore state.");
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
NSLog(@"centralManagerDidUpdateState invoked...");
// Determine the state of the peripheral
if ([central state] == CBCentralManagerStatePoweredOff) {
NSLog(@"CoreBluetooth BLE hardware is powered off");
} else if (central.state == CBCentralManagerStatePoweredOn) {
NSLog(@"CoreBluetooth BLE hardware is powered on.");
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@"did discover characteristic! peripheral: %@", peripheral);
NSLog(@"did discover characteristic! characteristic: %@", characteristic);
NSLog(@"did discover characteristic! error: %@", error);
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
NSLog(@"did discover service! peripheral: %@", peripheral);
NSLog(@"services??: %@", peripheral.services);
NSLog(@"did discover service! error: %@", error);
}
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"peripheral: %@", peripheral);
NSLog(@"advertisement data: %@", advertisementData);
if([peripheral.name containsString:@"Adafruit Bluefruit LE"]) {
NSLog(@"central: %@", central);
NSLog(@"peripheral: %@", peripheral);
NSLog(@"advertisement data: %@", advertisementData);
NSLog(@"RSSI: %@", RSSI);
NSLog(@"found peripheral services!: %@", peripheral.services);
self.myPeripheral = peripheral;
self.myPeripheral.delegate = self;
//self.serviceUUID = ;
self.deviceUUID = [CBUUID UUIDWithString:[peripheral.identifier UUIDString]];
[self connectToDevice];
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"--- didConnectPeripheral");
NSLog(@"peripheral in didConnect function: %@", peripheral);
NSLog(@"any services??: %@", peripheral.services);
[peripheral discoverServices:@[self.deviceUUID]];
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) {
NSLog(@"--- connected to peripheral in foreground");
[[NSNotificationCenter defaultCenter] postNotificationName:@"connectedToDevice" object:self];
[self triggerLocalNotification];
} else {
NSLog(@"--- connected to peripheral in background");
[[NSNotificationCenter defaultCenter] postNotificationName:@"connectedToDevice" object:self];
[self triggerLocalNotification];
//[self.delegate didConnectToDevice];
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error {
NSLog(@"--- did disconnect ConnectPeripheral");
}
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices {
NSLog(@"peripheral services: %@", peripheral.services);
NSLog(@"services invalidated: %@", invalidatedServices);
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"Failed to connect");
}
- (void)sendCodeToBTDevice:(NSString *)code
characteristic:(CBCharacteristic *)characteristic {
if(code != nil) {
NSData *data = [code dataUsingEncoding:NSUTF8StringEncoding];
[self.ourPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
@end
最后的想法...
我在如何在 Arduino 板/任何 BT 设备中编写自定义服务/特性方面的知识差距显然是这里的问题所在。我不确定我是否应该在设备或我的 OBJ-C 应用程序中创建 services/characteristics。
现在考虑到这些是在设备而不是应用程序中创建的是多么明显,这个概念现在很可笑。
是的,这是可能的。在 HID 设备中设置您要更新以通知的特征。然后在应用程序中 setNotify
到 true
并收听 didUpdateValueFor
Reference
然后你要做的就是写入你想要写入的CBCharacteristic
,一旦写入另一个CBCharacteristic
,就会调用didUpdateValueFor
。
对于在 Google 上搜索的任何人,我最终在这里找到了关于这个问题的正确答案。
原来我需要在外围BT设备(不是iOS设备)上创建自定义services/characteristics。一旦我创建了这些用于接收 128 位令牌字符串的自定义服务,就很容易让设备在 HID 的单独服务层上响应。
我已经更新了上面的代码以反映此答案,因此请查看代码以遵循此答案。
在我的蓝牙管理器 didDiscoverPeripheral
方法中 class,我确保设备 UUID 与已知设备 UUID 匹配。通过 UUID 找到正确的设备后,我将外围对象的(默认情况下由本机方法传入)委托设置为 self
(因为我的 class 使用外围委托),然后我分配self.ourPeripheral
属性 of class 到当前外设obj。一旦我都设置好了,我启动我的自定义 connectToDevice
方法,该方法对齐 centralManager
属性 以连接给定的外围设备。
在 centralManager 委托给出的 didConnectPeripheral
方法中,我调用了 [peripheral discoverServices:nil];
,它启动了名为 didDiscoverServices
的委托方法。然后我从这里迭代所有发现的服务,寻找我在我构建的 BT 外围设备的自定义服务中设置的预定服务 UUID。一旦我在盒子上找到了定制服务,我就会打电话给 [peripheral discoverCharacteristics:nil forService:service];
。这也会自动调用 didDiscoverCharacteristicsForService
的委托方法(如您所料)。再一次,更多的迭代,在这个用例中,我改为搜索我在我的服务中写在盒子上的特征 UUID,用于收听 BT 串行通信。
一旦找到正确的特征,我就将其全局保存为 class 的强 属性,然后我可以使用上面显示的 sendCodeToBTDevice
方法。这会将数据作为 text/string 通过 BT 写入设备,由盒子通过串行接收,解释为 128 位令牌并进行验证。如果有效,它会在不同的服务层上响应,并根据请求发回符合 HID 标准的消费者代码。