草庐IT

iOS开发,Bluetooth你应该了解一些!

Owenli_千 2023-03-28 原文

API结构导图

CoreBluetooth中,需要用到的类和协议(完整导图):

基础知识

蓝牙分类中心端和外设端(完整导图)。

中心端(接收端)

1 .创建中心端控制器(CBCentralManager) 2 .扫描设备(Discover) 3 .连接 (Connect) 4 .获取Service和Characteristic

  • 扫描Service (一个service中包含一个或多个Characteristic)
  • 获取Service中Characteristic
  • 获取Characteristic的值
5 . 数据交互(explore and interact)

  • 订阅Characteristic的通知
6 . 断开链接

外设端(发送端)

  1. 创建Peripheral管理对象
  2. 创建Service和Characteristic树
  3. 发送广告
  4. 处理读写请求和订阅

蓝牙状态

typedef NS_ENUM(NSInteger, CBManagerState) { CBManagerStateUnknown = 0, CBManagerStateResetting, CBManagerStateUnsupported, //不支持 CBManagerStateUnauthorized, //未授权 CBManagerStatePoweredOff, //关闭 CBManagerStatePoweredOn, //蓝牙打开状态 } NS_ENUM_AVAILABLE(NA, 10_0); 连接状态

/*! * @enum CBPeripheralState * * @discussion Represents the current connection state of a CBPeripheral. * */ typedef NS_ENUM(NSInteger, CBPeripheralState) { CBPeripheralStateDisconnected = 0, CBPeripheralStateConnecting, CBPeripheralStateConnected, CBPeripheralStateDisconnecting NS_AVAILABLE(NA, 9_0), } NS_AVAILABLE(NA, 7_0); CBCentralManagerDelegate

@required // 更新CentralManager状态,参数central就是当前的Manager。 - (void)centralManagerDidUpdateState:(CBCentralManager *)central; @optional // 蓝牙状态的保存和恢复,进入后台和重新启动有关 - (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict; //扫描外部设备 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI; //与外设完成连接 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral; //连接失败 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error; //断开连接 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error; CBPeripheralDelegate

@optional //peripheral更新 - (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0); //服务更新时触发 - (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices NS_AVAILABLE(NA, 7_0); //更新RSSI,过时用下一代替 - (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error NS_DEPRECATED(NA, NA, 5_0, 8_0); // RSSI值 - (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error NS_AVAILABLE(NA, 8_0); //发现服务 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error; //发现嵌套服务 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error; //发现服务中的Characteristic - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error; //更新Characteristic - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error; - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error; - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error; - (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error; - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error; - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error; CBPeripheralManagerDelegate

@required //类似CBCentralManager - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral; @optional - (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary<NSString *, id> *)dict; //开始广播 - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error; //添加服务 - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error; //订阅 - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic; //未订阅 - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic; //接收读取请求 - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request; //接收写入请求 - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests; //更新订阅 - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;

中心端(接收端)

从上面了解了蓝牙开发的基本流程和API结构,那下面我们一起看看中心端的开发步骤。

创建CentralManager

//创建CentralManager - (void)startUpCentralManager { _centralManager = [[CBCentralManager alloc] initWithDelegate: self queue: nil]; _peripherals = [NSMutableArray array]; //用于存放扫描的外设 } 创建CentralManager会调用Delegate方法:

#pragma mark - CBCentralManagerDelegate // 实现: 确保支持BL和有效的Central设备 - (void)centralManagerDidUpdateState:(CBCentralManager *)central { switch (central.state) { case CBManagerStatePoweredOn: //只有此状态下才可以进行扫描设备 [self scan]; break; case CBManagerStateUnknown: break; case CBManagerStateResetting: break; case CBManagerStateUnsupported: break; case CBManagerStateUnauthorized: break; case CBManagerStatePoweredOff: break; default: break; } }

扫描外部设备

- (void)scan { [self.centralManager scanForPeripheralsWithServices: nil options: nil]; NSLog(@"Scanning started"); }
serviceUUIDs: 是一个CBUUID类型的数组,每个CBUUID代表一个service的UUID。使用这个参数可以限制扫描内容,如果设置为nil,则表示搜索全部外设。 options: 定义扫描 ,字典。

  • CBCentralManagerScanOptionAllowDuplicatesKey : 布尔值,无重复过滤的扫描。默认是NO。如果设置为YES,对电池有不利影响。
  • CBCentralManagerScanOptionSolicitedServiceUUIDsKey 想要扫描的Service UUIDs数组(NSArray)。
另外,在bluetooth-central后台模式下,option将被忽略。这个问题后面会详细讲解。

当启动扫描后,每次扫描到一个外设就会调用delegate方法:centralManager: didDiscoverPeripheral: advertisementData: RSSI:

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI { NSLog(@"%@", peripheral.name); //查找到设备并持有它,否则不会保存 [self.peripherals addObject: peripheral]; }
  • peripheral : 扫描到的设备
  • advertisementData: 字典包含所有的广告数据
  • RSSI:received signal strength indicator,单位分贝。表示信号强度。
到这一步我们扫描到外部设备,那接下来就是建立连接了。

建立连接

通过扫描,我们发现了目标外设,接下来建立连接。

- (void)startUpConnect { [self.centralManager connectPeripheral: self.peripheral options: nil]; }
self.peripheral : 目标外设 options :字典,用来定制连接行为

  • CBConnectPeripheralOptionNotifyOnConnectionKey
  • CBConnectPeripheralOptionNotifyOnDisconnectionKey
  • CBConnectPeripheralOptionNotifyOnNotificationKey
进行连接通常会出现两种情况:成功、失败。

连接成功 本地连接成功,会调用方法:

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { NSLog(@"链接设备名称为:%@", peripheral.name); // 设置代理 peripheral.delegate = self; //发现服务 [peripheral discoverServices: nil]; } 连接失败

//链接失败 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"连接名称:%@ 失败,原因:%@", peripheral.name, error.localizedDescription); } 另外,既然可以建立连接,那么肯定可以断开连接。

** 断开连接**

//断开链接 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"与外设断开链接:%@, %@", peripheral.name, error.localizedDescription); }
如果断开连接不是由cancelPeripheralConnection:发起的,error会给出详细信息。需要注意的是,当断开连接,所有的services, characteristics和Characteristic descriptions都是无效的。

获取服务

创建连接成功后,在delegate方法中

// 设置代理 peripheral.delegate = self; //发现服务 [peripheral discoverServices: nil];
可以通过CBUUID指定的服务。

CBPeripheralDelegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { if (error) { NSLog(@"发现服务错误:%@", error.localizedDescription); return; } // 一个外设会发送多个服务 for (CBService *service in peripheral.services) { //扫描每个service的Characteristic,通过Characteristic的UUID来指定查找那个Characteristic。 [peripheral discoverCharacteristics: nil forService: service]; } }
注:一个外设包含多个Service,可以通过Service的UUID来区分。一个Service包含多个Characteristic,通过Characteristic的UUID来区分。

获取特征

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { if (error) { NSLog(@"发现特征错误:%@", error.localizedDescription); return; } for (CBCharacteristic *characteristic in service.characteristics) { //直接读取Characteristic值,此时调用peripheral: didUpdateValueForCharacteristic: error: [peripheral readValueForCharacteristic: characteristic]; //另一种情况,订阅特征。此时调用 peripheral: didUpdateNotificationStateForCharacteristic: error: [peripheral setNotifyValue:YES forCharacteristic: characteristic]; } }

订阅Characteristic

//给指定的特征设置通知 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"Error changing notification state : %@", error.localizedDescription); } if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:@""]]) { return; } if (characteristic.isNotifying) { NSLog(@"Notification began on :%@", characteristic); } else { NSLog(@"Notification stoped on : %@ Disconnecting", characteristic); [self.centralManager cancelPeripheralConnection: peripheral]; } }

获取特征值

//readValueCharacteristic时调用 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { //打印出characteristic的UUID和值 //!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据 NSLog(@"特性ID::%@ 值:%@",characteristic.UUID, characteristic.value); }

数据交互

- (void)writeValue { NSData *data = [@"Test" dataUsingEncoding:NSUTF8StringEncoding]; [self.peripheral writeValue: data forCharacteristic: self.characteristic type: CBCharacteristicWriteWithResponse]; }
发送数据

  • value: 写入值
  • characteristic : 被写入的特征
  • type: 写入类型,是否有应答
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) { CBCharacteristicWriteWithResponse = 0, CBCharacteristicWriteWithoutResponse, };
当type为CBCharacteristicWriteWithResponse时,调用delegate方法:peripheral: didWriteValueForCharacteristic: error:

外设端(发送端)

创建PeripheralManager

//创建PeripheralManager - (void)startUpPeripheralManager { _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue: nil]; } //唯一@required方法 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { switch (peripheral.state) { case CBManagerStatePoweredOn: // 创建服务和特征 [self buildService]; break; case CBManagerStateUnknown : break; case CBManagerStateResetting: break; case CBManagerStateUnsupported: break; case CBManagerStateUnauthorized: break; case CBManagerStatePoweredOff: break; default: break; } }

创建服务和特征

- (void)buildService { //创建Characteristic self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value: nil permissions:CBAttributePermissionsReadable]; //创建服务 CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES]; //将特征添加到服务中 transferService.characteristics = @[self.transferCharacteristic]; //将服务添加到PeripheralManager中,调用delegate方法: peripheralManager: didAddService: error: [self.peripheralManager addService: transferService]; }

创建Characteristic

创建Characteristic

  • UUID : 唯一标识
  • properties:属性
  • value : 设置nil, 动态设置。
  • permission : 权限
属性

typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) { //特征值可以使用特征描述广播 CBCharacteristicPropertyBroadcast = 0x01, //特征值可以被读取,通过readValueForCharacteristic: 来读取 CBCharacteristicPropertyRead = 0x02, // 可以被读写,通过writeValue: forCharacteristic: type: 写入特征值,无应答标识写入成功。 CBCharacteristicPropertyWriteWithoutResponse = 0x04, // 可以被写入,有应答标识写入成功 CBCharacteristicPropertyWrite = 0x08, //允许通知特征值,无应答表示接收 CBCharacteristicPropertyNotify = 0x10, //允许通知特征值,有应答表示接收 CBCharacteristicPropertyIndicate = 0x20, CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, CBCharacteristicPropertyExtendedProperties = 0x80, //只有信任的设备接收特征值通知 CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100, CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200 };
属性特征,可以组合使用。

权限

typedef NS_OPTIONS(NSUInteger, CBAttributePermissions) { // 可读 CBAttributePermissionsReadable = 0x01, // 可写 CBAttributePermissionsWriteable = 0x02, // 信任可读 CBAttributePermissionsReadEncryptionRequired = 0x04, // 信任可写 CBAttributePermissionsWriteEncryptionRequired = 0x08 } NS_ENUM_AVAILABLE(NA, 6_0);

创建Service

UUID : 唯一标识 isPrimary:YES:主服务;NO:次服务

添加特征到characteristics数据 调用delegate方法: peripheralManager: didAddService: error:

发送广告

//开始广告 - (void)startAdvertise { [self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]}]; } //停止广告 - (void)stopAdvertise { [self.peripheralManager stopAdvertising]; }
开始广告 advertisementData: 可选字典,包含想要广告的数据。两种类型:

  • CBAdvertisementDataLocalNameKey :对应名称
  • CBAdvertisementDataServiceUUIDsKey : 对应UUID
调用delegate方法:peripheralManagerDidStartAdvertising: error:

处理订阅

- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"当前Characteristic被订阅"); }
当characteristic配置为notify或indicate,将唤起次方法。

- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"Central unsubscribed from characteristic"); }
当central移除特征的notification/indications时调用,取消订阅回调。

数据交互

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request { } - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests { }
当接收到中心端的读写请求式,会触发这个方法。 在该方法被调用时,可以在方法内通过PeripheralManager调用:respondToRequest: withResult:来响应中心端的读写请求。

蓝牙的后台模式

执行模式

plist文件中,设置UIBackgroundModes:

  • bluetooth-central
  • bluetooth-peripheral

bluetooth-central 执行模式

当设置为此模式,允许APP切入后台后还能进行蓝牙服务。依然能进行扫描,连接,交互数据等。 进入后台CBCentralManagerScanOptionAllowDuplicatesKey扫描被忽略。

bluetooth-peripheral 执行模式

当设置为此模式,允许APP进行读写,连接中心端等。 CBAdvertisementDataLocalNameKey被忽略,本地外设名不在广播。

实现代码

PeripheralManager

#import "LQPeripheralManager.h" #import <CoreBluetooth/CoreBluetooth.h> static NSString *const ServiceUUID1 = @"FFF0"; static NSString *const notifyCharacteristicUUID = @"FFF1"; static NSString *const readwriteCharacteristicUUID = @"FFF2"; static NSString *const ServiceUUID2 = @"FFE0"; static NSString *const readCharacteristicUUID = @"FFE1"; static NSString *const LocalNameKey = @"Owenli"; @interface LQPeripheralManager ()<CBPeripheralManagerDelegate> @property (nonatomic, strong) CBPeripheralManager *peripheralManager; @property (nonatomic, strong) NSTimer *timer; @property (nonatomic, assign) NSInteger index; @end @implementation LQPeripheralManager + (instancetype)shareInstance { static LQPeripheralManager *peripheral; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ peripheral = [[self alloc] init]; }); return peripheral; } - (instancetype)init { if (self = [super init]) { _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil]; _index = 0; } return self; } /** * @Description 初始化化UUID和服务信息 */ - (void)setup { //characteristic字段描述 CBUUID *cbuuidCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString]; /* 可以通知的Characteristic properities : CBCharacteristicPropertyNotify permissions : CBAttributePermissionsReadable */ CBMutableCharacteristic *notifyCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:notifyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; /* 可读写的characteristic prperitise: CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead permisssions : CBAttributePermissionsReadable | CBAttributePermisssionWriteable */ CBMutableCharacteristic * readwriteCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable]; // 设置descriptor CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc] initWithType:cbuuidCharacteristicUserDescriptionStringUUID value:@"name"]; [readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]]; /* 只读Characteristic properties: CBCharacteristicPropertyRead permisssions: CBAttributePermissionsReadable */ CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable]; // 第一个Service CBMutableService *service1 = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES]; [service1 setCharacteristics:@[notifyCharacteristic, readwriteCharacteristic]]; //第二个Service CBMutableService *service2 = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES]; [service2 setCharacteristics:@[readCharacteristic]]; //添加到periperal此时会调用,peripheralManager: didAddService: error: [self.peripheralManager addService:service2]; [self.peripheralManager addService:service1]; } #pragma mark - PeripherManagerDelegate //检测蓝牙状态, - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { switch (peripheral.state) { case CBManagerStatePoweredOn: //初始化 [self setup]; break; case CBManagerStatePoweredOff: NSLog(@"powered off"); break; default: break; } } - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error { if (error) { NSLog(@"%@", error.localizedDescription); return; } //添加服务后,开始广播 //自动回调: peripheralManagerDidStartAdvertising: error: [self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1], [CBUUID UUIDWithString:ServiceUUID2]], CBAdvertisementDataLocalNameKey : LocalNameKey }]; } //通知发送广播 - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error { if (error) { NSLog(@"%@", error.localizedDescription); } NSLog(@"start advert"); } - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"subscribe data of : %@", characteristic.UUID); //分配定时任务 self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic repeats:YES]; } - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { NSLog(@"unsubscrible: %@", characteristic.UUID); [self.timer invalidate]; } - (void)sendData:(NSTimer *)timer { CBMutableCharacteristic *characteristic = timer.userInfo; if ([self.peripheralManager updateValue:[[NSString stringWithFormat:@"send data : %ld", _index] dataUsingEncoding: NSUTF8StringEncoding] forCharacteristic:characteristic onSubscribedCentrals:nil]) { NSLog(@"发送成功"); _index ++; } else { NSLog(@"发送数据错误"); } } //中心设备读取请求 - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request { NSLog(@"readRequest"); if (request.characteristic.properties & CBCharacteristicPropertyRead) { NSData *data = [[NSString stringWithFormat:@"by characteristic request"] dataUsingEncoding:NSUTF8StringEncoding]; [request setValue:data]; //对请求作出成功响应 [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; } else { [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted]; } } //写入请求 - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests { NSLog(@"writeRequest"); CBATTRequest *request = requests.firstObject; if (request.characteristic.properties & CBCharacteristicPropertyWrite) { CBMutableCharacteristic *charateristic = (CBMutableCharacteristic *)request.characteristic; charateristic.value = request.value; NSLog(@"receive data :%@", [[NSString alloc] initWithData:charateristic.value encoding:NSUTF8StringEncoding]); [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; } else { [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted]; } } - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral { NSLog(@"peripheralManagerIsReadyToUpdateSubscribers"); } @end
使用LighBlue测试外设端

备注

命令行生成UUID方法:uuidgen。 Mac测试软件:LighBlue,可以用来测试iOS端外设。

参考

有关iOS开发,Bluetooth你应该了解一些!的更多相关文章

  1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  2. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  3. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  4. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  5. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  6. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  7. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  8. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  9. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  10. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

随机推荐