iOS蓝牙开发

iOS蓝牙开发(一)蓝牙相关基础知识

20071214084035267.jpg


蓝牙常见名称和缩写

  • MFI ======= make for ipad ,iphone, itouch 专们为苹果设备制作的设备

  • BLE ==== buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE

  • peripheral,central == 外设和中心,发起连接的时central,被连接的设备为perilheral

  • service and characteristic === 服务和特征 每个设备会提供服务和特征,类似于服务端的api,但是机构不同。每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种,就是我们连接设备后具体需要操作的内容。

  • Description 每个characteristic可以对应一个或多个Description用户描述characteristic的信息或属性

  • MFI === 开发使用ExternalAccessory 框架

  • 4.0 BLE === 开发使用CoreBluetooth 框架

蓝牙基础知识

CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心。对应他们分别有一组相关的API和类

blob.png

  • 这两组api分别对应不同的业务场景,左侧叫做中心模式,就是以你的app作为中心,连接其他的外设的场景,而右侧称为外设模式,使用手机作为外设别其他中心设备操作的场景。

  • 服务和特征,特征的属性(service and characteristic):

    每个设备都会有一些服务,每个服务里面都会有一些特征,特征就是具体键值对,提供数据的地方。每个特征属性分为这么几种:读,写,通知这么几种方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
//objcetive c特征的定义枚举
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
};

外设、服务、特征间的关系

blob.png

蓝牙中心模式流程

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. 启动一个Peripheral管理对象

2. 本地Peripheral设置服务,特性,描述,权限等等

3. Peripheral发送广告

4. 设置处理订阅、取消订阅、读characteristic、写characteristic的委托方法

蓝牙设备状态

1. 待机状态(standby):设备没有传输和发送数据,并且没有连接到任何设

2. 广播状态(Advertiser):周期性广播状态

3. 扫描状态(Scanner):主动寻找正在广播的设备

4. 发起链接状态(Initiator):主动向扫描设备发起连接。

5. 主设备(Master):作为主设备连接到其他设备。

6. 从设备(Slave):作为从设备连接到其他设备。

蓝牙设备的五种工作状态

  • 准备(standby)

  • 广播(advertising)

  • 监听扫描(Scanning

  • 发起连接(Initiating)

  • 已连接(Connected)

蓝牙和版本的使用限制

  • 蓝牙2.0 === 越狱设备

  • 蓝牙4.0 === iOS 6 以上

  • MFI认证设备(Make For ipod/ipad/iphone) === 无限制

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

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

再上一节说了app作为central连接peripheral的情况,这一节介绍如何使用app发布一个peripheral,给其他的central连接

1.jpeg

还是这张图,central模式用的都是左边的类,而peripheral模式用的是右边的类

peripheral模式的流程

1. 打开peripheralManager,设置peripheralManager的委托

2. 创建characteristics,characteristics的description 创建service,把characteristics添加到service中,再把service添加到peripheralManager中

3. 开启广播advertising

4. 对central的操作进行响应

    - 4.1 读characteristics请求

    - 4.2 写characteristics请求

    - 4.4 订阅和取消订阅characteristics

准备环境

  1 Xcode

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

  3 蓝牙外设

实现步骤

1. 打开peripheralManager,设置peripheralManager的委托

设置当前ViewController实现CBPeripheralManagerDelegate委托

    @interface BePeripheralViewController : UIViewController

初始化peripheralManager

     /*
     和CBCentralManager类似,蓝牙设备打开需要一定时间,打开成功后会进入委托方法
     - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
     模拟器永远也不会得CBPeripheralManagerStatePoweredOn状态
     */
    peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];

2. 创建characteristics,characteristics的description ,创建service,把characteristics添加到service中,再把service添加到peripheralManager中

在委托方法 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral中,当peripheral成功打开后,才可以配置service和characteristics。 这里创建的service和chara对象是CBMutableCharacteristic和CBMutableService。他们的区别就像NSArray和NSMutableArray区别类似。 我们先创建characteristics和description,description是characteristics的描述,描述分很多种, 这里不细说了,常用的就是CBUUIDCharacteristicUserDescriptionString。

//peripheralManager状态改变
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    switch (peripheral.state) {
            //在这里判断蓝牙设别的状态  当开启了则可调用  setUp方法(自定义)
        case CBPeripheralManagerStatePoweredOn:
            NSLog(@"powered on");
            [info setText:[NSString stringWithFormat:@"设备名%@已经打开,可以使用center进行连接",LocalNameKey]];
            [self setUp];
            break;
        case CBPeripheralManagerStatePoweredOff:
            NSLog(@"powered off");
            [info setText:@"powered off"];
            break;
        default:
            break;
    }
}
 //配置bluetooch的
 -(void)setUp{
        //characteristics字段描述
        CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
        /*
         可以通知的Characteristic
         properties:CBCharacteristicPropertyNotify
         permissions CBAttributePermissionsReadable
         */
        CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
        /*
         可读写的characteristics
         properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
         permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable
         */
        CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
        //设置description
        CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];
        [readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
        /*
         只读的Characteristic
         properties:CBCharacteristicPropertyRead
         permissions CBAttributePermissionsReadable
         */
        CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
        //service1初始化并加入两个characteristics
        CBMutableService *service1 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
        [service1 setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];
        //service2初始化并加入一个characteristics
        CBMutableService *service2 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
        [service2 setCharacteristics:@[readCharacteristic]];
        //添加后就会调用代理的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
        [peripheralManager addService:service1];
        [peripheralManager addService:service2];
 }

3. 开启广播advertising

//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
    if (error == nil) {
        serviceNum++;
    }
    //因为我们添加了2个服务,所以想两次都添加完成后才去发送广播
    if (serviceNum==2) {
        //添加服务后可以在此向外界发出通告 调用完这个方法后会调用代理的
        //(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
        [peripheralManager startAdvertising:@{
                                              CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
                                              CBAdvertisementDataLocalNameKey : LocalNameKey
                                             }
         ];
    }
}
//peripheral开始发送advertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
    NSLog(@"in peripheralManagerDidStartAdvertisiong");
}

4. 对central的操作进行响应

- 4.1 读characteristics请求

- 4.2 写characteristics请求

- 4.3 订阅和取消订阅characteristics

//订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
    NSLog(@"订阅了 %@的数据",characteristic.UUID);
    //每秒执行一次给主设备发送一个当前时间的秒数
    timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic  repeats:YES];
}
//取消订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
    NSLog(@"取消订阅 %@的数据",characteristic.UUID);
    //取消回应
    [timer invalidate];
}
//发送数据,发送当前时间的秒数
-(BOOL)sendData:(NSTimer *)t {
    CBMutableCharacteristic *characteristic = t.userInfo;
    NSDateFormatter *dft = [[NSDateFormatter alloc]init];
    [dft setDateFormat:@"ss"];
    NSLog(@"%@",[dft stringFromDate:[NSDate date]]);
    //执行回应Central通知数据
    return  [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];
}
//读characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
    NSLog(@"didReceiveReadRequest");
    //判断是否有读数据的权限
    if (request.characteristic.properties & CBCharacteristicPropertyRead) {
        NSData *data = request.characteristic.value;
        [request setValue:data];
        //对请求作出成功响应
        [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}
//写characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
    NSLog(@"didReceiveWriteRequests");
    CBATTRequest *request = requests[0];
    //判断是否有写数据的权限
    if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
        //需要转换成CBMutableCharacteristic对象才能进行写值
        CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
        c.value = request.value;
        [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    }else{
        [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}

代码下载:

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

本文代码存放目录是BleDemo

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


iOS 蓝牙开发(四)BabyBluetooth蓝牙库介绍

BabyBluetooth 是一个最简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容iOS和Mac OS X。

特色:

  • 基于原生CoreBluetooth框架封装的轻量级的开源库,可以帮你更简单地使用CoreBluetooth API。

  • CoreBluetooth所有方法都是通过委托完成,代码冗余且顺序凌乱。BabyBluetooth使用block方法,可以重新按照功能和顺序组织代码,并提供许多方法减少蓝牙开发过程中的代码量。

  • 链式方法体,代码更简洁、优雅。

  • 通过channel切换区分委托调用,并方便切换

来源

最近几个月都在做蓝牙项目,用CoreBluetooch感觉语句写的到处都是,不优雅。一整条链下来要近10几个委托方法,并且不断的在委托方法中调用方法再进入其他的委托,导致 代码很零散。因此我就想让coreBlueTooth使用更简单,语法更优雅,所以开始写这个BabyBluetooch蓝牙库。

刚开始写BabyBluetooth时,只有目标,也不值得如何实现,于是一步步安装目标去实现,也遇到过很多问题,慢慢的也想出了解决方案,并一步步直到9月7日早晨,才算完成了 第一个可以用的版本。对于蓝牙这种在app中亮相率不高的功能,github上ios使用的操作蓝牙的库最多就100来个star,然后BabyBluetooch2天的时间内已经有了15个star和10个fork, 已经相当不错了,很快就有越来越多的人参与到项目中,issues,pull request等等。这些都是个人成长和项目成长非常宝贵的资源。

更新于:20150916,现在BabyBluetooth 已经有了96个star

期待

  • 蓝牙库写起来很辛苦,希望大家可以多多支持,多多star。BabyBluetooth主页

  • 如果在使用过程中遇到BUG,或发现功能不够用,希望你能Issues我,谢谢

  • 期待大家也能一起为BabyBluetooth输出代码,这里我只是给BabyBluetooth开了个头,他可以增加和优化的地方还是非常多。也期待和大家在Pull Requests一起学习,交流,成长。

Quick Example

//导入.h文件和系统蓝牙库的头文件
#import "BabyBluetooth.h"
-(void)viewDidLoad {
    [super viewDidLoad];
   //初始化BabyBluetooth 蓝牙库
    baby = [BabyBluetooth shareBabyBluetooth];
     //设置蓝牙委托
     [self babyDelegate];
     //设置委托后直接可以使用,无需等待CBCentralManagerStatePoweredOn状态
     baby.scanForPeripherals().begin()
}
//蓝牙网关初始化和委托方法设置
-(void)babyDelegate{
    //设置扫描到设备的委托
    [baby setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
        NSLog(@"搜索到了设备:%@",peripheral.name);
    }];
    //设置设备连接成功的委托
    [baby setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) {
        NSLog(@"设备:%@--连接成功",peripheral.name);
    }];
    //设置发现设备的Services的委托
    [baby setBlockOnDiscoverServices:^(CBPeripheral *peripheral, NSError *error) {
        for (CBService *service in peripheral.services) {
            NSLog(@"搜索到服务:%@",service.UUID.UUIDString);
        }
    }];
    //设置发现设service的Characteristics的委托
    [baby setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
        NSLog(@"===service name:%@",service.UUID);
        for (CBCharacteristic *c in service.characteristics) {
            NSLog(@"charateristic name is :%@",c.UUID);
        }
    }];
    //设置读取characteristics的委托
    [baby setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {
        NSLog(@"characteristic name:%@ value is:%@",characteristics.UUID,characteristics.value);
    }];
    //设置发现characteristics的descriptors的委托
    [baby setBlockOnDiscoverDescriptorsForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {
        NSLog(@"===characteristic name:%@",characteristic.service.UUID);
        for (CBDescriptor *d in characteristic.descriptors) {
            NSLog(@"CBDescriptor name is :%@",d.UUID);
        }
    }];
    //设置读取Descriptor的委托
    [baby setBlockOnReadValueForDescriptors:^(CBPeripheral *peripheral, CBDescriptor *descriptor, NSError *error) {
        NSLog(@"Descriptor name:%@ value is:%@",descriptor.characteristic.UUID, descriptor.value);
    }];
    //过滤器
    //设置查找设备的过滤器
    [baby setDiscoverPeripheralsFilter:^BOOL(NSString *peripheralsFilter) {
        //设置查找规则是名称大于1 , the search rule is peripheral.name length > 1
        if (peripheralsFilter.length >1) {
            return YES;
        }
        return NO;
    }];
    //设置连接的设备的过滤器
     __block BOOL isFirst = YES;
    [baby setFilterOnConnetToPeripherals:^BOOL(NSString *peripheralName) {
        //这里的规则是:连接第一个AAA打头的设备
        if(isFirst && [peripheralName hasPrefix:@"AAA"]){
            isFirst = NO;
            return YES;
        }
        return NO;
    }];
}

CoreBluetooch中实现上扫描,连接,发现服务和characteristic以及它的值相关方法调用是很麻烦啰嗦凌乱的。如下: centralManager启动->状态委托->调用扫描方法->进入扫描到设备的委托->调用连接设备方法->进入连接到设备的委托->发现服务方法->发现服务委托-> 发现characteristic方法->发现characteristic委托->读characteristic的value->读characteristic的value的委托->读description,读description的value-> ....的委托

而BabyBluetooth只需要一句话就执行了上面的内容。

  //扫描设备 然后读取服务,然后读取characteristics名称和值和属性,获取characteristics对应的description的名称和值
  baby.scanForPeripherals().connectToPeripheral().discoverServices()
  .discoverCharacteristics().readValueForCharacteristic().discoverDescriptorsForCharacteristic()
  .readValueForDescriptors().begin();

另一方面,BabyBluetooth所有的委托方法都紧凑的聚在了一起。此外,快速示例中没有包括channel的使用,如果包括了channel,那么ios几个页面或者组件的蓝牙 调用模块都可以写在一起,看起来就觉得很方便。

关于更多BabyBluetooth的介绍和使用示例已经api,请移步到BabyBluetooth主页查看



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值