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

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

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

    @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<cbcentralmanagerdelegate,cbperipheraldelegate>
            [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];
    }

  

时间: 2024-10-06 04:03:52

iOS_SN_BlueTooth (二)iOS 连接外设的代码实现的相关文章

iOS蓝牙开发(上)基础以及连接外设的实现

蓝牙常见名称和缩写 MFI ======= make for ipad ,iphone, itouch 专们为苹果设备制作的设备 BLE ==== buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE peripheral,central == 外设和中心,发起连接的时central,被连接的设备为perilheral service and characteristic === 服务和特征 每个设备会提供服务和特征,类似于服务端的api,但是机构不同.每个外设会

iOS安全攻防(二十三):Objective-C代码混淆

iOS安全攻防(二十三):Objective-C代码混淆 class-dump可以很方便的导出程序头文件,不仅让攻击者了解了程序结构方便逆向,还让着急赶进度时写出的欠完善的程序给同行留下笑柄. 所以,我们迫切的希望混淆自己的代码. 混淆的常规思路 混淆分许多思路,比如: 1)花代码花指令,即随意往程序中加入迷惑人的代码指令 2)易读字符替换 等等 防止class-dump出可读信息的有效办法是易读字符替换. Objective-C的方法名混淆 混淆的时机 我们希望在开发时一直保留清晰可读的程序代

Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!!一.Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架) Android的蓝牙系统,自下而上包括以下一些

使用Dnsmasq作为dhcp,解决IOS连接路由器慢

android手机第一次连接路由器WiFI速度很快,而苹果手机连接速度很慢,原因主要有两个:1.IOS系统WiFI存在Bug:2.dhcp服务器分配IP地址时间过长. 通过dnsmasq日志发现,IOS第一次连接路由器WiFI耗时原因: 1.读取机子中ip地址缓存,耗时1s(android不会): 2.发送REQUEST报文,报文里的IP是机子上次连接WiFi的分配的IP地址,如果不是同一个网段的路由器,dnsmasq直接发送NACK报文(设置dnsmasq配置文件,如果不设置,耗时2s): 3

编程算法 - 二叉搜索树 与 双向链表 代码(C++)

二叉搜索树 与 双向链表 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目:输入一颗二叉搜索树, 将该二叉搜索树转换成一个排序的双向链表. 要求不能创建不论什么新的结点, 仅仅能调整数中结点的指针的指向. 方法: 使用中序遍历每个结点, 并进行连接, 即左子树指前, 右子树指后, 并保存前一个节点. 本程序包括算法原理, 測试程序, 及 输出. /* * main.cpp * * Created on: 2014.6.12 * Author

iOS书写高质量代码之耦合的处理 干货!

iOS书写高质量代码之耦合的处理 耦合是每个程序员都必须面对的话题,也是容易被忽视的存在,怎么处理耦合关系到我们最后的代码质量.今天Peak君和大家聊聊耦合这个基本功话题,一起捋一捋iOS代码中处理耦合的种种方式及差异. 简化场景 耦合的话题可大可小,但原理都是相通的.为了方便讨论,我们先将场景进行抽象和简化,只讨论两个类之间的耦合. 假设我们有个类Person,需要喝水,根据职责划分,我们需要另一个类Cup来完成喝水的动作,代码如下: 1 2 3 4 5 6 7 8 9 //Person.h

【好程序员笔记分享】——iOS开发之纯代码键盘退出

-iOS培训,iOS学习-------型技术博客.期待与您交流!------------ iOS开发之纯代码键盘退出(非常简单)     iOS开发之纯代码键盘退出 前面说到了好几次关于键盘退出的,但是最近开始着手项目的时候却闷了,因为太多了,笔者确实知道有很多中方法能实现,而且令我影响最深的就是 EndEditing,但是因为即有textView,又有TextField而且他们各有不同的方法,虽然笔者现在搞懂了,但是不知道什么时候又不记得 了,而且虽然感觉很简单现在感觉很简单的样子,但是对于没

[iOS笔记]《编写高质量iOS与OS X代码的52个有效方法》:1.熟悉Objective-C

简介: 最近公司项目收尾,可以有时间看看书了.<编写高质量iOS与OS X代码的52个有效方法>这本书讲解了很多iOS开发的技巧和规范,大力推荐! 下面是自己看书时整理的笔记,照惯例先上目录: 目录: 第一章:熟悉Objective-C 第二章:Object.Message.Runtime 第三章:接口与API设计 第四章:Protocol与Category 第五章:内存管理 第六章:Block与GCD 第七章:系统框架 第一章    熟悉Objective-C 第1条:了解Objective

iOS 编译含C++代码出现ld: symbol(s) not found for architecture i386错误之解决(转载)

最近项目需要搭建自己的IM服务器,在快速配置好Openfire之后,开始研究使用gloox开发XMPP客户端实现通信, 先下载gloox源码,然后./configure,make ,sudo make install,在/usr/local/下找到头文件夹和静态库,加到项目中,然后加入openssl库,编写测试代码,编译,报错:XXX not being for architecture i386,感觉是gloox静态库有问题(排除了网上说的头文件路径缺失.building phases没添加.