转载自:http://www.cocoachina.com/ios/20150917/13456.html

原文作者:刘彦玮

上一篇文章介 绍了蓝牙的技术知识,这里我们具体说明一下中心模式的应用场景。主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般来说,外设 (蓝牙设备,比如智能手环之类的东西),会由硬件工程师开发好,并定义好设备提供的服务,每个服务对于的特征,每个特征的属性(只读,只写,通知等等), 本文例子的业务场景,就是用一手机app去读写蓝牙设备。

iOS连接外设的代码实现流程

1. 建立中心角色

2. 扫描外设(discover)

3. 连接外设(connect)

4. 扫描外设中的服务和特征(discover)

- 4.1 获取外设的services

- 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值

5. 与外设做数据交互(explore and interact)

6. 订阅Characteristic的通知

7. 断开连接(disconnect)

准备环境

1 Xcode

2 开发证书和手机(蓝牙程序需要使用使用真机调试,使用模拟器也可以调试,但是方法很蛋疼,我会放在最后说)

3 蓝牙外设

实现步骤

1 导入CoreBluetooth头文件,建立主设备管理类,设置主设备委托

#import 
    @interface ViewController : UIViewController     @interface ViewController (){
        //系统蓝牙设备管理对象,可以把他理解为主设备,通过他,可以去扫描和链接外设
        CBCentralManager *manager;
    }     - (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;//断开外设的委托
                    */
                    //连接设备
                   [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]);         }

4 扫描外设中的服务和特征(discover)

设备连接成功 后,就可以扫描设备的服务了,同样是通过委托形式,扫描到结果后会进入委托方法。但是这个委托已经不再是主设备的委托 (CBCentralManagerDelegate),而是外设的委托(CBPeripheralDelegate),这个委托包含了主设备与外设交互 的许多 回叫方法,包括获取services,获取characteristics,获取characteristics的值,获取 characteristics的Descriptor,和Descriptor的值,写数据,读rssi,用通知的方式订阅数据等等。

4.1 获取外设的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];
                         }         }

4.2 获取外设的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];
    }

8 模拟器蓝牙调试,慎用,最好还是用真机去调试。

由于在iPhone 4s之后的iOS才支持BLE,新一代的这些iOS设备又都不便宜,在做测试的时候,用iOS模拟器进行调试,可以节约一些开发成本。怎么在iOS模拟器上调试BLE,

苹果最初给出的说明是,支持BLE的mac机子上可以用模拟器进行调试,并给出了一份技术文档(传送门),恶心的是,后来苹果抽风,又把这份文档移除,

并且把iOS 7.0的模拟器上对BLE的支持也移除掉了(难道是想让大家多买设备测试?Apple sucks.)后面,网上搜了一下,解决办法如下:

1. 买一个CSR蓝牙4.0 USB适配器(某宝上大概30块钱),在机子上插入该物(你懂的)

2. 在Terminal下敲入sudo nvram bluetoothHostControllerSwitchBehavior="never" , 重启Mac。

3. 用XCode 4.6调试代码,在iOS 6.1的模拟器上跑程序(用XCode 5.0跑iOS 7.0模拟器会抛异常,原因上面详诉过了,Apple sucks,你懂的)

如何降低模拟器的iOS版本呢?

XCode->Preferences->Downloads里面有很多simulators你可以下载

选择个6.1的下载好了

代码下载:

我博客中大部分示例代码都上传到了github,地址是:https://github.com/coolnameismy/demo

本文代码存放目录是BleDemo

如果大家支持,请follow我的github账号,并star我的项目,有其他问题可以在blog中给我留言 blog的RSS订阅

iOS 蓝牙开发(二)iOS 连接外设的代码实现(转)的更多相关文章

  1. iOS 蓝牙开发资料记录

    一.蓝牙基础认识:   1.iOS蓝牙开发:  iOS蓝牙开发:蓝牙连接和数据读写   iOS蓝牙后台运行  iOS关于app连接已配对设备的问题(ancs协议的锅)          iOS蓝牙空中 ...

  2. ios蓝牙开发(二)ios连接外设的代码实现

    上一篇文章介绍了蓝牙的技术知识,这里我们具体说明一下中心模式的应用场景.主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用.一般来说,外设(蓝牙设备,比如智能手环之类的东西), 会由 ...

  3. ios蓝牙开发(三)ios连接外设的代码实现:手机app去读写蓝牙设备。

    手机app去读写蓝牙设备....... 代码下载: 原文博客主提供Github代码连接,地址是:https://github.com/coolnameismy/demo ios连接外设的代码实现流程: ...

  4. iOS蓝牙开发(二)蓝牙相关基础知识

    原文链接: http://liuyanwei.jumppo.com/2015/07/17/ios-BLE-1.html iOS蓝牙开发(一)蓝牙相关基础知识: 蓝牙常见名称和缩写 MFI ====== ...

  5. IOS 蓝牙相关-连接外设的代码实现(2)

    我们具体说明一下中心模式的应用场景.主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用.一般来说,外设(蓝牙设备,比如智能手环之类的东西), 会由硬件工程师开发好,并定义好设备提供的 ...

  6. iOS_SN_BlueTooth (二)iOS 连接外设的代码实现

    原文:http://www.cocoachina.com/ios/20150917/13456.html?utm_source=tuicool&utm_medium=referral 上一篇文 ...

  7. iOS蓝牙开发(4.0)详解

    最近由于项目需要, 一直在研究蓝牙4.0,在这儿分享给大家, 望共同进步. 一.关于蓝牙开发的一些重要的理论概念: 1.当前ios中开发蓝牙所运用的系统库是<CoreBluetooth/Core ...

  8. iOS 蓝牙开发详解

    目前iOS智能硬件的开发交互方式主要分为两种,一种是基于低功耗的蓝牙4.0技术(由于耗电低,也称作为BLE(Bluetooth Low Energy))对应iOS的框架为CoreBluetooth,另 ...

  9. iOS蓝牙开发CoreBluetooth快速入门

    在iOS开发中,实现蓝牙通信有两种方式,一种是使用传统的GameKit.framework,另一种就是使用在iOS 5中加入的CoreBluetooth.framework. 利用CoreBlueto ...

随机推荐

  1. 转:windows下命令行工具

    转自: http://www.cnblogs.com/haochuang/p/5593411.html Windows下CMD不好用,远没有Linux,或者一些SSH工具用起来方便.其实Windows ...

  2. uwsgi+flask环境中安装matplotlib

    uwsgi+flask的python有自身的virtual environment,可以通过如下命令进入 . venv/bin/activate 虽然通过sudo apt-get install py ...

  3. C#实现php的hash_hmac函数

    from:http://blog.csdn.net/ciaos/article/details/12618487 PHP代码示例如下 <?php         $res1 = hash_hma ...

  4. DEDECMS之三 首页、列表页怎么调用文章内容

    一.首页调用 百度了很多,没有找到实际的解决方法,对于直接读取数据库,这种写法不会采取. 后来,仔细考虑,这部分解决的内容不会很多,所以直接使用了简介的内容 方法一(默认长度55) [field:in ...

  5. MVC 图片预览

    1.页面cshtml <form name="frmInput" id="frmInput" method="post" action ...

  6. POJ 3714 Raid

    Description After successive failures in the battles against the Union, the Empire retreated to its ...

  7. [转]使用URLDecoder和URLEncoder对中文进行处理

    一 URLEncoder HTML 格式编码的实用工具类.该类包含了将 String 转换为 application/x-www-form-urlencoded MIME 格式的静态方法.有关 HTM ...

  8. C#计算文件的MD5值实例

    C#计算文件的MD5值实例 MD5 是 Message Digest Algorithm 5(信息摘要算法)的缩写,MD5 一种散列(Hash)技术,广泛用于加密.解密.数据签名和数据完整性校验等方面 ...

  9. getSelection、range 对象属性,方法理解,解释

    网上转了一圈发现没有selection方面的解释,自己捣鼓下 以这段文字为例子.. <p><b>法国国营铁路公司(SNCF)20日承认,</b>新订购的2000列火 ...

  10. 利用ActiveX实现web页面设置本地默认打印机、纸张大小

    通常web技术无法设置本地计算机的默认打印机,包括用代码设置纸张大小,如果业务系统中真遇到这种需求,只能通过其它辅助手段(比如ActiveX)实现.下面这段代码,出自网上被广泛使用的"泥人张 ...