翻译Core Bluetooth Programming Guide(Performing Common Central Role Tasks)

原文

执行常规的中心角色(Central Role)任务

在低功耗蓝牙(以下简称“蓝牙”)通信中,充当中心角色的设备执行着几个常规任务——例如,发现和链接有效的外部设备(以下简称“外设”),探测和交互外设提供的数据。充当外设角色的设备也执行着常规但是不同的任务——例如,发布和广告服务,响应来自连接中心的读,写和订阅请求。

在这一章中,你将学会如何使用Core Bluetooth框架去执行蓝牙任务的中心部分的大多数常规任务。伴随基于代码的例子将帮你开发应用,以实现在你的本地设备上实现中心角色。主要的,你会学到:

  • 开启一个中心管理器对象
  • 发现和链接正在广告的外设
  • 连接上外设之后,探测它上面的数据
  • 向外设服务中特性的值发送读写请求
  • 订阅特性的值,以察觉它的更新

下一章,你学会如何在你的本地设备上开发实现了外设角色的应用。

你在本章中找到的代码例子是简化和抽象的;你也许需要适当修改才能将它包含到你现实世界的应用中。更多相于实现中心角色高级主题——包括建议,技巧和最佳实践——包含在以后的章节中,Background Processing for iOS AppsBest Practices for Interacting with a Remote Peripheral Device

开启中心管理器(Central Manager)

由于CBCentralManager对象是本地中心设备的Core Bluetooth面向对象的代表,在你开始执行任何蓝牙事务之前你要开辟和初始化一个中心管理器实例。通过调用中心管理器的initWithDelegate:queue:options:方法初始化它:

myCentralManager =
        [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];

在这个例子中, self被设为代理,以接受各种中心角色的事件。通过指定派发队列为nil,中心管理器使用主队列派发中心角色事件。

当你创建了一个中心管理器,它会调用其代理对象的方法centralManagerDidUpdateState:。你必须实现这个代理方法以确保中心设备蓝牙的有效性。更多关于何实现这个代理方法的信息,请见CBCentralManagerDelegate Protocol Reference

发现正在广告的外设

一旦完成初始化,中心管理器的第一个任务是发现外设。正如在Centrals Discover and Connect to Peripherals That Are Advertising中提到的,外设以广告的方式来被察觉。你的应用发现附近正在广告的外设的方式是,调用中心管理器的scanForPeripheralsWithServices:options:方法:

[myCentralManager scanForPeripheralsWithServices:nil options:nil];

注意:如果你指定nil为第一个参数,那么中心管理器返回所有发现的外设,忽视它们所支持的服务。在真实的应用中,你通常指定的是一个包含CBUUID对象的数组,其中每一个代表外设正在广播的服务的通用唯一识别码(UUID)。当你指定了服务UUID的数组,中心管理器只返回广播了那些服务的外设,让你只扫描到你可能感兴趣的设备。

UUID,以及它们的代表CBUUID,将会在Services and Characteristics Are Identified by UUIDs详细讨论。

每当中心管理器发现一个外设,它会调用其代理的方法centralManager:didDiscoverPeripheral:advertisementData:RSSI:。刚被发现的外设以一个CBPeripheral对象返回。如果你打算连接它,持有一个对它的强引用,遮掩系统就不会释放它。以下例子的场景是,用一个类的属性维持对一个已发现的外设的引用:

- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI {
 
    NSLog(@"Discovered %@", peripheral.name);
    self.discoveredPeripheral = peripheral;
    ...

如果你想连接多个外设,你可以相应地持有一个外设的数组。任何情况下,一旦你已经发现了所有你想连接的外设,停止对其他设备的扫描以省电:

[myCentralManager stopScan];

连接你发现的外设

发现你想要的外设之后,调用中心管理器的connectPeripheral:options:方法以请求连接到它,传入你想连接的外设:

[myCentralManager connectPeripheral:peripheral options:nil];

如果连接成功,中心管理器会调用其代理的centralManager:didConnectPeripheral:方法。在与外设交互之前,你要设置它的代理以确保代理能收到准确的回调:

- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral {
 
    NSLog(@"Peripheral connected");
    peripheral.delegate = self;
    ...

发现你连接的设备中的服务

同一个外设建立连接之后,你可以探测它的数据。探测数据的第一步是发现它有效的服务。因为一个外设可以广告的数据大小是有限的,你可能会发现一个外设拥有的服务总数超过它所广告的(在它的广告包中)。你可以调用方法discoverServices:来发现外设提供的所有服务,像这样:

[peripheral discoverServices:nil];

注意:在现实的应用中,你通常不会传nil作为参数,因为这样做会返回外设上所有的服务。因为一个外设包含的服务可能比你想要的多,全部发现它们会浪费电池寿命和时间。相反的,你通常会指定你感兴趣的服务的UUID,像Explore a Peripheral’s Data Wisely中所展示的一样。

当指定的设备被发现了,外设(我们连接的CBPeripheral对象)就回调用其代理的peripheral:didDiscoverServices:方法。Core Bluetooth创建了一个CBService数组——包含着外设上发现的服务。如下所示,你可以实现这个代理方法来以获取已发现服务的数组。

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

发现服务中的特性

当你发现了你感兴趣的服务,探测外设的下一步就是要发现服务的所有特性。发现所有的特性只需调用外设的discoverCharacteristics:forService:方法,同时传入合适的服务,像这样:

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

注意:在现实的应用中,你通常不会传nil作为第一个参数,因为这样做会返回外设的服务中所有的特性。因为一个外设的服务所包含的特性可能比你想要的多,全部发现它们会浪费电池寿命和时间。相反的,你通常会指定你感兴趣的特性的UUID。

目标服务的特性被发现的时候,外设就会调用其代理的peripheral:didDiscoverCharacteristicsForService:error: 方法。Core Bluetooth创建了一个CBCharacteristic数组——包含了被发现的特性。以下的例子展示了你如何可以实现这个代理方法来打印被发现的特性:

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

获取特性的值

每个特性包含了一个单一的值,描述一个外设的服务的信息。例如,一个体温计服务的温度测量值特性可以有一个以摄氏度为单位的温度值。获取特性的值可以通过直接读取或订阅它。

读取特性的值

当你已经找到你感兴趣的特性,要从中读值可以通过调用外设的readValueForCharacteristic:方法,同时传入适当的特性,像这样:

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,你可以知道它是否可读。如果你试图读取一个不可读的特性的值,代理方法peripheral:didUpdateValueForCharacteristic:error:将返回一个对应的错误。

订阅特性的值

虽然使用readValueForCharacteristic:方法可以有效的读取特性的静态值,但这并不是读取动态值最有效的方式。通过订阅的方式获取随时变化的特性值——例如,你的心率。当你订阅了特性的值,每当它发生变化,你就会收到通知。

订阅你感兴趣的特性的方法是调用外设的setNotifyValue:forCharacteristic:方法,第一个参数指定为YES,像这样像这样:

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

当你订阅(或者取消订阅)一个特性值,外设会调用其代理的setNotifyValue:forCharacteristic:,像这样:

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error changing notification state: %@",
           [error localizedDescription]);
    }
    ...

注意:并非所有特性提供订阅。通过检查特性的properties属性是否含有常量CBCharacteristicPropertyNotifyCBCharacteristicPropertyIndicate可以知道它是否提供订阅。

完成对特性的值的订阅之后,外设会通知你的app特性的值的改变。每当只发生变化,外设会调用其代理的peripheral:didUpdateValueForCharacteristic:error:方法。为了获取更新后的值,你可以按上述Reading the Value of a Characteristic的方法实现。

书写特性的值

偶尔需要书写特性值。例如,你的应用和一个蓝牙数字恒温器交互,你可能需要提供恒温器一个房间的温度。如果特性的值是可书写的,你可以使用data(NSData对象)书写它的值,通过调用外设的writeValue:forCharacteristic:type:方法,像这样:

NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
    [peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
        type:CBCharacteristicWriteWithResponse];

书写特性的值,你要指定你执行书写的类型。上面的例子中,书写的类型是CBCharacteristicWriteWithResponse,它让外设通知你的应用书写是否成功,通过调用其代理的peripheral:didWriteValueForCharacteristic:error:方法。你要实现这个代理方法来处理错误的情况,如下所示:

- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error writing characteristic value: %@",
            [error localizedDescription]);
    }
    ...

如果相反你指定的书写类型是CBCharacteristicWriteWithoutResponse,那么书写操作以尽力而为的方式执行,而且交付过程既没有保证也没有报告。外设不会回调代理方法。更多关于Core Bluetooth框架支持的书写类型的信息,参考CBPeripheral Class Reference中的枚举BCharacteristicWriteType

注意:一些特性可能只支持某几种书写类型,甚至完全不支持。查看特性的书写类型,要通过检查它的properties属性是否具有CBCharacteristicPropertyWriteWithoutResponse或者CBCharacteristicPropertyWrite常量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 蓝牙简介 蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离...
    Chefil阅读 2,027评论 2 19
  • 首先进一则广告: 蓝牙技术联盟(Bluetooth SIG)2010年7月7日宣布,正式采纳蓝牙4.0核心规范(B...
    L泽阅读 1,426评论 3 4
  • 不知道在大型企业有没这样的老板,反正小企业的员工肯定都有这样的痛,老板根本不懂管理! 小企业老板多为创业初期,没太...
    十里又春风阅读 414评论 0 1
  • 【柒月影语】20170612学习力践行Day22小盆友放学新结识了一个小伙伴,在外面疯玩了很晚才回家,新背会古诗《...
    暖小柒阅读 44评论 0 0
  • 子曰:人而不仁,如礼何?人而不仁,如乐何? 这个句子言简而意赅。礼乐的本质是什么呢?作为一个士人,却没有仁爱之心,...
    浅浅君子阅读 13,958评论 0 3