初识Bluetooth开发

API Reference

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

CoreBluetooth.png

基础知识

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

Bluetooth.png

中心端(接收端)

1 .创建中心端控制器(CBCentralManager)

2 .扫描设备(Discover)

3 .连接 (Connect)

4 .获取Service和Characteristic

  • 扫描Service (一个service中包含一个或多个Characteristic)
  • 获取Service中Characteristic
  • 获取Characteristic的值

5 . 数据交互(explore and interact)

  • 订阅Characteristic的通知

6 . 断开链接

Central.png

外设端(发送端)

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

Peripheral.png

蓝牙状态

1
2
3
4
5
6
7
8
typedef NS_ENUM(NSInteger, CBManagerState) {
CBManagerStateUnknown = 0,
CBManagerStateResetting,
CBManagerStateUnsupported, //不支持
CBManagerStateUnauthorized, //未授权
CBManagerStatePoweredOff, //关闭
CBManagerStatePoweredOn, //蓝牙打开状态
} NS_ENUM_AVAILABLE(NA, 10_0);

连接状态

1
2
3
4
5
6
7
8
9
10
11
12
/*!
* @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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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

1
2
3
4
5
//创建CentralManager
- (void)startUpCentralManager {
_centralManager = [[CBCentralManager alloc] initWithDelegate: self queue: nil];
_peripherals = [NSMutableArray array]; //用于存放扫描的外设
}

创建CentralManager会调用Delegate方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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;
}
}

扫描外部设备

1
2
3
4
- (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:

1
2
3
4
5
- (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,单位分贝。表示信号强度。

到这一步我们扫描到外部设备,那接下来就是建立连接了。

建立连接

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

1
2
3
- (void)startUpConnect {
[self.centralManager connectPeripheral: self.peripheral options: nil];
}

self.peripheral : 目标外设
options :字典,用来定制连接行为

* CBConnectPeripheralOptionNotifyOnConnectionKey
* CBConnectPeripheralOptionNotifyOnDisconnectionKey
* CBConnectPeripheralOptionNotifyOnNotificationKey

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

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

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

连接失败

1
2
3
4
//链接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"连接名称:%@ 失败,原因:%@", peripheral.name, error.localizedDescription);
}

另外,既然可以建立连接,那么肯定可以断开连接。

** 断开连接**

1
2
3
4
//断开链接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"与外设断开链接:%@, %@", peripheral.name, error.localizedDescription);
}

如果断开连接不是由cancelPeripheralConnection:发起的,error会给出详细信息。需要注意的是,当断开连接,所有的services, characteristics和Characteristic descriptions都是无效的。

获取服务

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

1
2
3
4
// 设置代理
peripheral.delegate = self;
//发现服务
[peripheral discoverServices: nil];

可以通过CBUUID指定的服务。

CBPeripheralDelegate

1
2
3
4
5
6
7
8
9
10
11
- (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来区分。

获取特征

1
2
3
4
5
6
7
8
9
10
11
12
- (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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//给指定的特征设置通知
- (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];
}
}

获取特征值

1
2
3
4
5
6
7
//readValueCharacteristic时调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {

//打印出characteristic的UUID和值
//!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据
NSLog(@"特性ID::%@ 值:%@",characteristic.UUID, characteristic.value);
}

数据交互

1
2
3
4
- (void)writeValue {
NSData *data = [@"Test" dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheral writeValue: data forCharacteristic: self.characteristic type: CBCharacteristicWriteWithResponse];
}

发送数据

  • value: 写入值
  • characteristic : 被写入的特征
  • type: 写入类型,是否有应答
  • 1
    2
    3
    4
    typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) {
    CBCharacteristicWriteWithResponse = 0,
    CBCharacteristicWriteWithoutResponse,
    };

当type为CBCharacteristicWriteWithResponse时,调用delegate方法:peripheral: didWriteValueForCharacteristic: error:

外设端(发送端)

创建PeripheralManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//创建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;
}
}

创建服务和特征

1
2
3
4
5
6
7
8
9
10
11
12
- (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 : 权限

属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
};

属性特征,可以组合使用。

权限

1
2
3
4
5
6
7
8
9
10
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:

发送广告

1
2
3
4
5
6
7
8
//开始广告
- (void)startAdvertise {
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]}];
}
//停止广告
- (void)stopAdvertise {
[self.peripheralManager stopAdvertising];
}

开始广告

advertisementData: 可选字典,包含想要广告的数据。两种类型:

* CBAdvertisementDataLocalNameKey :对应名称
* CBAdvertisementDataServiceUUIDsKey : 对应UUID

调用delegate方法:peripheralManagerDidStartAdvertising: error:

处理订阅

1
2
3
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"当前Characteristic被订阅");
}

当characteristic配置为notify或indicate,将唤起次方法。

1
2
3
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"Central unsubscribed from characteristic");
}

当central移除特征的notification/indications时调用,取消订阅回调。

数据交互

1
2
3
4
- (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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#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端外设。

参考