美文网首页
iOS蓝牙通信CoreBluetooth框架学习

iOS蓝牙通信CoreBluetooth框架学习

作者: NicoalsNC | 来源:发表于2019-10-16 16:13 被阅读0次

    最近学习蓝牙,所以找了一些资料学习研究了一下蓝牙通讯的一个流程。写了一个小demo,demo效果见下图:


    设备a作为发送数据端,发送不同的数字 设备b作为接收数据端,接收到不同的数字

    需要demo的朋友自行下载,如果有用,请给star,谢谢

    接下来我们讲学习:

    • 基础知识了解
    • app作为主设备,发送数据
    • app作为从设备,接收数据

    1.基础知识了解

    蓝牙5.0是由蓝牙技术联盟在2016年提出的蓝牙技术标准,蓝牙5.0针对低功耗设备速度有相应提升和优化,蓝牙5.0结合wifi对室内位置进行辅助定位,提高传输速度,增加有效工作距离。百度百科

    我们在开发中要使用CoreBluetooth框架

    我们先学习几个概念:

    service and characteristic 服务和特征

    • 每个设备都会提供服务和特征,类似于服务端的api,但是由于结构不
      同,每个外设会有很多的服务,每个服务中又包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种,就是我们连接设备后具体需要操作的内容。
    • service是characteristic的集合
    • 一个characteristic包括一个单一变量和0-n个用来描述characteristic变量的descriptor,characteristic可以被认为是一个类型,类 似于类。

    Description

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

    CoreBluetooth框架

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

    两种模式图,左边叫做中心模式,右边称为外设模式

    外设-服务-特征

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

    属性列表

    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
    };
    

    蓝牙设备的几种状态:

    蓝牙设备状态图.jpg

    设备作为中心模式(central)流程:

    中心模式流程图.jpg

    设备作为外设模式(peripheral)流程:

    外设模式流程图.jpg

    2.app作为主设备,发送数据

    注意:蓝牙需要真机调试,所以必须要有真机

    1) 导入 CoreBluetooth 框架,和头文件

    import <CoreBluetooth/CoreBluetooth.h>

    两个属性:

     @property(nonatomic,strong)CBCentralManager *manager;//主设备
     @property(nonatomic,strong)NSMutableArray *peripherals;//被发现设备数组
    

    初始化,设置代理 CBCentralManagerDelegate

    self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
    self.peripherals = @[].mutableCopy;
    

    2) 实现CBCentralManagerDelegate相关的协议方法

    a. 当设备开关蓝牙 都会走这个回调
    -(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");
                        //开始扫描周围的外设
                        [manager scanForPeripheralsWithServices:nil options:nil];
     //参数可以添加一些option,来增加精确的查找范围, 如 :
         //   NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                //     [NSNumber numberWithBool:YES],       //CBCentralManagerScanOptionAllowDuplicatesKey,
                                    // nil];
         //   [manager scanForPeripheralsWithServices:nil options:options];
     */
                        break;
                    default:
                        break;
                }
    
            }
    
    b. 扫描到设备会进入这个方法,并连接外设(connect)
    -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
            NSLog(@"当扫描到设备:%@",peripheral.name);
            //接下来可以连接设备
      //这里自己去设置下连接规则,我设置的是nicolas开头的设备
        if ([peripheral.name hasPrefix:@"nicolas"]){
        //找到的设备必须持有它,否则CBCentralManager中也不会保存peripheral,那么CBPeripheralDelegate中的方法也不会被调用!!
            [self.peripherals addObject:peripheral];
                            //连接设备
            [self.manager connectPeripheral:peripheral options:nil];       
        }
      }
    
    c. 连接外设(connect)后是否成功会进入下面的方法
     - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//外设(Peripherals)连接成功的委托
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设(Peripherals)连接失败的委托
     - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设(Peripherals)的委托
    

    3) 获取外设的服务和特征

    在设备连接成功后,我们需要设置外设Peripherals的委托(CBPeripheralDelegate),并开始获取外设(Peripherals)的服务service和特征characteristic

    CBPeripheralDelegate 的委托里包含了主设备与外设交互的许多 回调方法,包括获取services,获取characteristics,获取characteristics的值,获取characteristics的Descriptor,和Descriptor的值,写数据,读rssi,用通知的方式订阅数据等等。

     //连接到Peripherals-成功回调
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
            {   [peripheral setDelegate:self];
                [peripheral discoverServices:nil];
                 // 获取service时候也可以设置option   添加指定条件可以 提高效率如: 
                //    NSMutableArray *serviceUUIDs = [NSMutableArray array ];
    //指定设备
              //    CBUUID *cbuuid = [CBUUID UUIDWithString:[NSString                  stringWithFormat:@"%x",Ble_Device_Service]];
               //    [serviceUUIDs addObject:cbuuid];
               //   [peripheral discoverServices:serviceUUIDs]; 
    
            }
    

    扫描到服务services

      -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
                if (error)
                {
                   return;
                }
                for (CBService *service in peripheral.services) {
                      NSLog(@"%@",service.UUID);
                 //扫描每个service的Characteristics
                      [peripheral discoverCharacteristics:nil forService:service];
                  }
      }
    

    扫描到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){
                 {
             //获取Characteristic的值
                     [peripheral readValueForCharacteristic:characteristic];
    //读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
                 }
             }
             for (CBCharacteristic *characteristic in service.characteristics){
       //搜索Characteristic的Descriptors
                 [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);
    //主设备存从设备的设备与特征   
         if(characteristic.properties & CBCharacteristicPropertyWrite){
            if ([characteristic.UUID.UUIDString isEqual:@"FFF2"]) {
                self.cunPeripheral = peripheral;
                self.cunCBCharacteristic = characteristic;
            }
        }
        }
    

    把数据写到 Characteristic 中,也就是发送数据
    Byte b =0X01;
    NSData *data = [NSData dataWithBytes:&b length:sizeof(b)];
    [self writeCharacteristic:self.cunPeripheral characteristic:self.cunCBCharacteristic value:data];

    -(void)writeCharacteristic:(CBPeripheral *)peripheral
                    characteristic:(CBCharacteristic *)characteristic
                             value:(NSData *)value{
           //只有 characteristic.properties 有write的权限才可以写
            if(characteristic.properties & CBCharacteristicPropertyWrite){
                [peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
            }else{
                NSLog(@"该字段不能写!");
            }
        }
    

    4) 订阅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];
        }
    

    5) 断开连接

        -(void)disconnectPeripheral:(CBCentralManager *)centralManager
                         peripheral:(CBPeripheral *)peripheral{
            //停止扫描
            [centralManager stopScan];
            //断开连接
            [centralManager cancelPeripheralConnection:peripheral];
        }
    

    3.app作为从设备,接收数据

    1) 初始化peripheralManager,设置peripheralManager的委托

    self.peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
    

    2) 实现代理方法

    创建characteristics 创建service,把characteristics添加到service 然后把service添加到peripheralManager

     - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
      // 在这里判断蓝牙的状态, 因为蓝牙打开成功后才能配置service和characteristics
      //具体配置代码见demo
    }
    

    开启广播

    //perihpheral添加了service
    - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
        if (error == nil) {
            serviceNum++;
        }
        //因为我们添加了2个服务,所以想两次都添加完成后才去发送广播
        if (serviceNum==2) {
            [peripheralManager startAdvertising:@{
                                                  CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
                                                  CBAdvertisementDataLocalNameKey : LocalNameKey
                                         }
             ];
        }
    }
    

    处理收到的数据

    //写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];
    
             int i;
            [c.value getBytes:&i length:sizeof(i)];
            self.numberLabel.text = @(i).stringValue;
        }else{
            [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
        }
    }
    

    从设备还有一个需要每次接收到给主设备发送一个时间

    蓝牙学习就告一段落了。
    需要demo的朋友自行下载,如果有用,请给star,谢谢

    直接用官方的框架写还是比较麻烦的,这篇文章只做学习用,现在其实有很多CoreBluetooth 的封装,比如这个BabyBluetooth就很棒

    相关文章

      网友评论

          本文标题:iOS蓝牙通信CoreBluetooth框架学习

          本文链接:https://www.haomeiwen.com/subject/nfosmctx.html