蓝牙(CoreBluetooth)-中心设备(客户端)

蓝牙客户端-中心设备

主要内容

1. 创建`中央管理器`
2. 发现并且连接外设
3. 寻找连接上的外设数据
4. 发送读或写`特征值`的请求
5. 订阅外设特征值

1. 创建中心管理器

因为CBCentralManager代表着本地中央设备,所以你必须先创建一个中央管理器对象,通过CBCentralManagerinitWithDelegate:queue:options: 如:

myCentralManager =
        [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];```
在上面的例子中,self设置为中央管理器的代理,它将接收来自`中央管理器`的事件回调,queue设置为nil,表示为主队列.当你创建一个中心管理器时,就会调用它代理的`centralManagerDidUpdateState:` 方法,你必须实现这个代理方法来确保你的设备支持蓝牙4.0.

####2. 扫描外部设备

[myCentralManager scanForPeripheralsWithServices:nil options:nil];

1. `注意`:如果第一个参数传入nil,`中央管理器` 返回全部找到的外设,忽略它们所支持的服务,你可以传入一个有独特UUID的`服务`数组,当你指定服务的时候,`中央管理器` 只返回具拥有这些服务的外设.
2. 在你调用` scanForPeripheralsWithServices:options: `方法之后,`中央管理器`就会调用它代理的` centralManager:didDiscoverPeripheral:advertisementData:RSSI:`,没发现一个外设就会调用一次,发现的外部设备通过` CBPeripheral `对象传入.你可以实现这个方法列出所发现的外设.
    ```
   - (void)centralManager:(CBCentralManager *)central
    didDiscoverPeripheral:(CBPeripheral *)peripheral
        advertisementData:(NSDictionary *)advertisementData
                     RSSI:(NSNumber *)RSSI {
    
       NSLog(@"Discovered %@", peripheral.name);
       ...

当你找到你需要的外设后,你需要停止搜索.

3. 连接外部设备

当你找到自己需要的外设后,你应该请求连接外部设备,通过调用BCentralManagerconnectPeripheral:options:方法.如:

[myCentralManager connectPeripheral:peripheral options:nil];
假如连接外设成功,中央管理器就会调用它代理的centralManager:didConnectPeripheral:方法,

- (void)centralManager:(CBCentralManager *)central
 didConnectPeripheral:(CBPeripheral *)peripheral {

   NSLog(@"Peripheral connected");
   ...```
在与外设进行交互之前你应该首先设置外设的代理

####4. 发现已经连接设备上的服务.
当你连接上一个外设,你就可以开始检索数据了,第一步,检索一个外设上都提供什么服务,通过下面的方法你可以检索出,所有的外设提供的所有的服务

[peripheral discoverServices:nil];```

注意:尽管你这么做可以反问这个外设上的所有服务,但是在一个真实的App中你通常不传入一个nil,因为一个外设可能非常多的服务,这些服务并不是你需要的,发现他们全部可能缩短电池的使用时间并且浪费时间.更多情况你需要制定服务UUID来检索你感兴趣的服务.

但发现指定的服务后,将会调用CBPeripheral 对象的代理方法peripheral:didDiscoverServices:,核心蓝牙框架会把所发现的服务放到一个数组中,设置给这个外设对象.你可以实现这个代理方法,访问这些服务

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
 
    for (CBService *service in peripheral.services) {
        NSLog(@"Discovered service %@", service);
        ...
    }
    ...```

####5. 发现一个服务上的特征.

假设你已经找到了你需要的服务,下一步就是检索该服务上的特征,检索服务上的所有特征你只需要调用` CBPeripheral` 方法 ` discoverCharacteristics:forService: ` 并指定服务

NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];```

注意: 尽管你这么做可以反问这个服务上的所有特征,但是在一个真实的App中你通常不要出入一个nil,因为一个服务可能非常多的特征,这些特征并不是你需要的,发现他们全部可能会缩短电池的使用时间并且浪费时间.更多情况你需要制定特征UUID来检索你感兴趣的 特征.

当外设检索到指定服务的特征后,就会调用代理对象的peripheral:didDiscoverCharacteristicsForService:error: 核心蓝牙会把所发现的特征放到数组中设置给服务的characteristics 属性,你可以实现这个代理方法,获取检索到的特征

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
             error:(NSError *)error {
 
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"Discovered characteristic %@", characteristic);
        ...
    }
    ...

6. 取出一个特征的值

一个特征包含一个单独的,这个代表着外设提供的服务的详细信息.例如在一个体温计中的一个温度的特征有一个值代表着摄氏度的温度.你可以可以直接读取这个值或订阅这个值

1. 读出一个特征的值

当你找到需要的服务的一个特征后,你可以读取这个特征的值,通过调用CBPeripheralreadValueForCharacteristic: 传入那个特征.像这样

  NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
    [peripheral readValueForCharacteristic:interestingCharacteristic];```

当你试图读取一个特征的值的时候,外设就会调用它代理的`peripheral:didUpdateValueForCharacteristic:error: ` 方法,如果这个被成功的获取,你就可访问`特征`的`value`属性获取这个值,像这样
  • (void)peripheral:(CBPeripheral *)peripheral
    didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
    error:(NSError *)error {

    NSData *data = characteristic.value;
    // parse the data as needed
    ...```

注意: 并不是所有的特征都有读的权限,你要检测这个特征是否有读的权限可以通过特征的properties属性中CBCharacteristicPropertyRead Key ,如果你试图读取一个不可度的特征值,那么在peripheral:didUpdateValueForCharacteristic:error: 方法中将会传入一个合适的 错误

2. 订阅一个特征的值

尽管在某些情况下你可以通过readValueForCharacteristic: 很方便的读取一个特征的值,但是对于一个经常变化的值这不是一中高效的方式.大部分特征的值的变化的--例如心率是每时每刻都在变化,此时你应该订阅它.当你订阅一个特征的值的时候,你将会收到一个通知,当外设的值改变的时候

你可以订阅一个特征的值,通过调用CBPeripheralsetNotifyValue:forCharacteristic: 第一个参数传入YES,像这样

 [peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];```

当你尝试订阅(或取消订阅)一个特征的值时,外设的代理方法`peripheral:didUpdateNotificationStateForCharacteristic:error: ` 就会被调用.如果订阅请求因任何原因失败,这个代理方法中都会通过`error`告诉你错误的原因.例如:
  • (void)peripheral:(CBPeripheral *)peripheral
    didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
    error:(NSError *)error {

    if (error) {
    NSLog(@"Error changing notification state: %@",
    [error localizedDescription]);
    }
    ...


> `注意:` 不是所有特征都可以订阅他们的值,你可以通过` Characteristic ` 的`Properties` 属性来判断这个特征是否支持订阅.


当你成功订阅一个特征的值后,外设将会在它的值改变的时候通知你.每次值的改变都会调用代理的`peripheral:didUpdateValueForCharacteristic:error: `方法,为了获取这个这你需要实现这个代理方法.

####7.写入一个特征的值

有些时候可能需要需要写入一个特征的值,比如你app和基于蓝牙4.0的自动温度调节器交互,你可能需要提供一个值来设置室内温度,如果这个特征值是可写的,你通过`CBPeripheral ` 的 ` writeValue:forCharacteristic:type: ` 方法,第一个参数传入一个`NSData`对象,像这样:
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
    type:CBCharacteristicWriteWithResponse];
当你写入特征的值时,你需要指定按照什么类型写入,在上面的例子中指定是`CBCharacteristicWriteWithResponse` 它告诉外设需要让你的app知道是否写入成功.

外设通过调用代理对象的` peripheral:didWriteValueForCharacteristic:error: ` 来响应指定类型参数`CBCharacteristicWriteWithResponse`写入请求.任何原因导致的写入失败,你都会收到一个错误对象,它描述了错误的原因,例如
  • (void)peripheral:(CBPeripheral *)peripheral
    didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
    error:(NSError *)error {
    if (error) {
    NSLog(@"Error writing characteristic value: %@",
    [error localizedDescription]);
    }
    ...
> `注意:`特征可能只允许以特定的类类型写入,想知道特征都支持那种类型的写入可以遍历`Characteristic`的 `properties` 属性.
####附录. 中心设备流程
![中心设备流程.jpg](http://upload-images.jianshu.io/upload_images/321489-76d3201f640ad14e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容