一般iOS开发者做APP开发大部分时候都是通过Http(s)请求跟后台服务器打交道,做一些信息展示和用户交互。很少涉及到去跟外部硬件设备连接的开发。随着近年来车联网和物联网的兴起,智能家居和智能硬件的逐步火热,越来越多的app被开发出来,用来跟硬件设备进行来连接,获取硬件相关信息展示或者发送指令控制硬件来提供服务。
在正式学习之前我看了一个培训机构的视频教学:装逼特技蓝牙,喜欢的朋友可以先现在看一下,那么你在学习蓝牙的时候不会那么吃力
蓝牙常见名称和缩写
MTI:make for ipad ,iphone, itouch 专们为苹果设备制作的设备关于MFi认证你所必须要知道的事情
BLE:buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE
peripheral,central: peripheral(外设)被连接的设置为peripheral;central(中心):发起链接时的设置
service and characteristic:服务和特征,类似于服务端的API,但是机构不同。每个外设会有很多的服务,每一个服务都包含了很多的字段,这些字段的权限一般分为 读read 写write 通知notify,就是我们连接设备或具体需要操作的内容
Description 每个characteristic可以对应一个或多个Description用户描述characteristic的信息或属性
MFI === 开发使用ExternalAccessory 框架
4.0 BLE === 开发使用CoreBluetooth 框架
CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心。对应他们分别有一组相关的API和类
这两组api分别对应不同的业务场景,左侧叫做中心模式,就是以你的app作为中心,连接其他的外设的场景,而右侧称为外设模式,使用手机作为外设别其他中心设备操作的场景。
蓝牙中心模式流程
中心模式的应用场景:主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般来说,外设(蓝牙设备,比如智能手环之类的东西), 会由硬件工程师开发好,并定义好设备提供的服务,每个服务对于的特征,每个特征的属性(只读,只写,通知等等),本文例子的业务场景,就是用一手机app去读写蓝牙设备。
ios连接外设的代码实现流程
- 建立中心角色
- 扫描外设(discover)
- 连接外设(connect)
- 扫描外设中的服务和特征(discover)
--4.1 获取外设的services
--4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值 - 与外设做数据交互(explore and interact)
- 订阅Characteristic的通知
- 断开连接(disconnect)
实现步骤
1 导入CoreBluetooth头文件,建立主设备管理类,设置主设备委托
#import
@interface ViewController : UIViewController
@interface ViewController (){
//系统蓝牙设备管理对象,可以把他理解为主设备,通过他,可以去扫描和链接外设
CBCentralManager *manager;
//用于保存被发现设备
NSMutableArray *peripherals;
}
- (void)viewDidLoad {
[super viewDidLoad];
/*
设置主设备的委托,CBCentralManagerDelegate
必须实现的:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主设备状态改变的委托,在初始化CBCentralManager的适合会打开设备,只有当设备正确打开后才能使用
其他选择实现的委托中比较重要的:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外设的委托
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功的委托
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败的委托
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设的委托
*/
//初始化并设置委托和线程队列,最好一个线程的参数可以为nil,默认会就main线程
manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
2 扫描外设(discover),扫描外设的方法我们放在centralManager成功打开的委托中,因为只有设备成功打开,才能开始扫描,否则会报错。
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBCentralManagerStateUnknown:
NSLog(@">>>CBCentralManagerStateUnknown");
break;
case CBCentralManagerStateResetting:
NSLog(@">>>CBCentralManagerStateResetting");
break;
case CBCentralManagerStateUnsupported:
NSLog(@">>>CBCentralManagerStateUnsupported");
break;
case CBCentralManagerStateUnauthorized:
NSLog(@">>>CBCentralManagerStateUnauthorized");
break;
case CBCentralManagerStatePoweredOff:
NSLog(@">>>CBCentralManagerStatePoweredOff");
break;
case CBCentralManagerStatePoweredOn:
NSLog(@">>>CBCentralManagerStatePoweredOn");
//开始扫描周围的外设
/*
第一个参数nil就是扫描周围所有的外设,扫描到外设后会进入
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
*/
[manager scanForPeripheralsWithServices:nil options:nil];
break;
default:
break;
}
}
//扫描到设备会进入方法
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"当扫描到设备:%@",peripheral.name);
//接下来可以连接设备
}
3 连接外设(connect)
//扫描到设备会进入方法
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
//接下连接我们的测试设备,如果你没有设备,可以下载一个app叫lightbule的app去模拟一个设备
//这里自己去设置下连接规则,我设置的是P开头的设备
if ([peripheral.name hasPrefix:@"P"]){
/*
一个主设备最多能连7个外设,每个外设最多只能给一个主设备连接,连接成功,失败,断开会进入各自的委托
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功的委托
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败的委托
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设的委托
*/
//找到的设备必须持有它,否则CBCentralManager中也不会保存peripheral,那么CBPeripheralDelegate中的方法也不会被调用!!
[peripherals addObject:peripheral];
//连接设备
[manager connectPeripheral:peripheral options:nil];
}
}
//连接到Peripherals-成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);
}
//连接到Peripherals-失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
}
//Peripherals断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);
}
有一点非常容易出错,大家请注意。在 didDiscoverPeripheral这个委托中有这一行
//找到的设备必须持有它,否则CBCentralManager中也不会保存peripheral,那么CBPeripheralDelegate中的方法也不会被调用!!
[peripherals addObject:peripheral];
请特别注意,如果不保存,会影响到后面的方法执行,这个地方很多人出错,在我的蓝牙交流群中每天几乎都会因为这个问题导致无法连接和对外设后续的操作。
大家也可以看一下这个委托在xcode中的说明,重点看@discussion中的内容,里面特别指出了需要retained对象
/*!
* @method centralManager:didDiscoverPeripheral:advertisementData:RSSI:
*
* @param central The central manager providing this update.
* @param peripheral A CBPeripheral
object.
* @param advertisementData A dictionary containing any advertisement and scan response data.
* @param RSSI The current RSSI of peripheral, in dBm. A value of 127
is reserved and indicates the RSSI
* was not available.
*
* @discussion This method is invoked while scanning, upon the discovery of peripheral by central. A discovered peripheral must
* be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager. For
* a list of advertisementData keys, see {@link CBAdvertisementDataLocalNameKey} and other similar constants.
*
* @seealso CBAdvertisementData.h
*
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
4 扫描外设中的服务和特征(discover)
设备连接成功后,就可以扫描设备的服务了,同样是通过委托形式,扫描到结果后会进入委托方法。但是这个委托已经不再是主设备的委托(CBCentralManagerDelegate),而是外设的委托(CBPeripheralDelegate),这个委托包含了主设备与外设交互的许多 回叫方法,包括获取services,获取characteristics,获取characteristics的值,获取characteristics的Descriptor,和Descriptor的值,写数据,读rssi,用通知的方式订阅数据等等。
获取外设的services
//连接到Peripherals-成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);
//设置的peripheral委托CBPeripheralDelegate
//@interface ViewController : UIViewController
[peripheral setDelegate:self];
//扫描外设Services,成功后会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
[peripheral discoverServices:nil];
}
//扫描到Services
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
// NSLog(@">>>扫描到服务:%@",peripheral.services);
if (error)
{
NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
return;
}
for (CBService *service in peripheral.services) {
NSLog(@"%@",service.UUID);
//扫描每个service的Characteristics,扫描到后会进入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
[peripheral discoverCharacteristics:nil forService:service];
}
}
获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值
//扫描到Characteristics
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error)
{
NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
return;
}
for (CBCharacteristic *characteristic in service.characteristics)
{
NSLog(@"service:%@ 的 Characteristic: %@",service.UUID,characteristic.UUID);
}
//获取Characteristic的值,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
for (CBCharacteristic *characteristic in service.characteristics){
{
[peripheral readValueForCharacteristic:characteristic];
}
}
//搜索Characteristic的Descriptors,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
for (CBCharacteristic *characteristic in service.characteristics){
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}
//获取的charateristic的值
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//打印出characteristic的UUID和值
//!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据
NSLog(@"characteristic uuid:%@ value:%@",characteristic.UUID,characteristic.value);
}
//搜索到Characteristic的Descriptors
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//打印出Characteristic和他的Descriptors
NSLog(@"characteristic uuid:%@",characteristic.UUID);
for (CBDescriptor *d in characteristic.descriptors) {
NSLog(@"Descriptor uuid:%@",d.UUID);
}
}
//获取到Descriptors的值
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
//打印出DescriptorsUUID 和value
//这个descriptor都是对于characteristic的描述,一般都是字符串,所以这里我们转换成字符串去解析
NSLog(@"characteristic uuid:%@ value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
}
5 把数据写到Characteristic中
//写数据
-(void)writeCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic
value:(NSData *)value{
//打印出 characteristic 的权限,可以看到有很多种,这是一个NS_OPTIONS,就是可以同时用于好几个值,常见的有read,write,notify,indicate,知知道这几个基本就够用了,前连个是读写权限,后两个都是通知,两种不同的通知方式。
/*
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
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
};
*/
NSLog(@"%lu", (unsigned long)characteristic.properties);
//只有 characteristic.properties 有write的权限才可以写
if(characteristic.properties & CBCharacteristicPropertyWrite){
/*
最好一个type参数可以为CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,区别是是否会有反馈
*/
[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}else{
NSLog(@"该字段不可写!");
}
}
6 订阅Characteristic的通知
//设置通知
-(void)notifyCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic{
//设置通知,数据通知会进入:didUpdateValueForCharacteristic方法
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
//取消通知
-(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic{
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
}
7 断开连接(disconnect)
//停止扫描并断开连接
-(void)disconnectPeripheral:(CBCentralManager *)centralManager
peripheral:(CBPeripheral *)peripheral{
//停止扫描
[centralManager stopScan];
//断开连接
[centralManager cancelPeripheralConnection:peripheral];
}
app作为外设被连接的实现
peripheral模式的流程
- 打开peripheralManager,设置peripheralManager的委托
- 创建characteristics,characteristics的description 创建service,把characteristics添加到service中,再把service添加到peripheralManager中
- 开启广播advertising
- 对central的操作进行响应
- 4.1 读characteristics请求
- 4.2 写characteristics请求
- 4.4 订阅和取消订阅characteristics
实现步骤
1. 打开peripheralManager,设置peripheralManager的委托
设置当前ViewController实现CBPeripheralManagerDelegate委托
@interface BePeripheralViewController : UIViewController
初始化peripheralManager
/*
和CBCentralManager类似,蓝牙设备打开需要一定时间,打开成功后会进入委托方法
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
模拟器永远也不会得CBPeripheralManagerStatePoweredOn状态
*/
peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
2. 创建characteristics,characteristics的description ,创建service,把characteristics添加到service中,再把service添加到peripheralManager中
在委托方法 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral中,当peripheral成功打开后,才可以配置service和characteristics。 这里创建的service和chara对象是CBMutableCharacteristic和CBMutableService。他们的区别就像NSArray和NSMutableArray区别类似。 我们先创建characteristics和description,description是characteristics的描述,描述分很多种, 这里不细说了,常用的就是CBUUIDCharacteristicUserDescriptionString。
//peripheralManager状态改变
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
switch (peripheral.state) {
//在这里判断蓝牙设别的状态 当开启了则可调用 setUp方法(自定义)
case CBPeripheralManagerStatePoweredOn:
NSLog(@"powered on");
[info setText:[NSString stringWithFormat:@"设备名%@已经打开,可以使用center进行连接",LocalNameKey]];
[self setUp];
break;
case CBPeripheralManagerStatePoweredOff:
NSLog(@"powered off");
[info setText:@"powered off"];
break;
default:
break;
}
}
//配置bluetooch的
-(void)setUp{
//characteristics字段描述
CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
/*
可以通知的Characteristic
properties:CBCharacteristicPropertyNotify
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
/*
可读写的characteristics
properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable
*/
CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
//设置description
CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];
[readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
/*
只读的Characteristic
properties:CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
//service1初始化并加入两个characteristics
CBMutableService *service1 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
[service1 setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];
//service2初始化并加入一个characteristics
CBMutableService *service2 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
[service2 setCharacteristics:@[readCharacteristic]];
//添加后就会调用代理的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
[peripheralManager addService:service1];
[peripheralManager addService:service2];
}
3. 开启广播advertising
//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
if (error == nil) {
serviceNum++;
}
//因为我们添加了2个服务,所以想两次都添加完成后才去发送广播
if (serviceNum==2) {
//添加服务后可以在此向外界发出通告 调用完这个方法后会调用代理的
//(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
[peripheralManager startAdvertising:@{
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
CBAdvertisementDataLocalNameKey : LocalNameKey
}
];
}
}
//peripheral开始发送advertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
NSLog(@"in peripheralManagerDidStartAdvertisiong");
}
4. 对central的操作进行响应
- 4.1 读characteristics请求
- 4.2 写characteristics请求
- 4.3 订阅和取消订阅characteristics
//订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"订阅了 %@的数据",characteristic.UUID);
//每秒执行一次给主设备发送一个当前时间的秒数
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic repeats:YES];
}
//取消订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"取消订阅 %@的数据",characteristic.UUID);
//取消回应
[timer invalidate];
}
//发送数据,发送当前时间的秒数
-(BOOL)sendData:(NSTimer *)t {
CBMutableCharacteristic *characteristic = t.userInfo;
NSDateFormatter *dft = [[NSDateFormatter alloc]init];
[dft setDateFormat:@"ss"];
NSLog(@"%@",[dft stringFromDate:[NSDate date]]);
//执行回应Central通知数据
return [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];
}
//读characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
NSLog(@"didReceiveReadRequest");
//判断是否有读数据的权限
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = request.characteristic.value;
[request setValue:data];
//对请求作出成功响应
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
//写characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
NSLog(@"didReceiveWriteRequests");
CBATTRequest *request = requests[0];
//判断是否有写数据的权限
if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
//需要转换成CBMutableCharacteristic对象才能进行写值
CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
c.value = request.value;
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
参考链接:iOS蓝牙开发(一)蓝牙相关基础知识
ios蓝牙开发(二)ios连接外设的代码实现
ios蓝牙开发(三)app作为外设被连接的实现