iOS蓝牙通信CoreBluetooth框架学习

最近学习蓝牙,所以找了一些资料学习研究了一下蓝牙通讯的一个流程。写了一个小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

两个属性:

 @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框架学习)