美文网首页
iOS BLE app作为主设备

iOS BLE app作为主设备

作者: 雪_晟 | 来源:发表于2019-04-16 18:51 被阅读0次

    首先是了解蓝牙的一些理论概念。

    蓝牙外设必需为4.0及以上(2.0需要MFI认证),否则无法开发,蓝牙4.0设施由于低耗电,所以也叫做BLE

    当前iOS中开发蓝牙所运使用的系统库是CoreBluetooth。该框架的核心是:两个东西,peripheral(外设)和central(中心)。所以会有两个方向:app 作为主设备以及app作为外设。

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

    一条属性链:蓝牙---->若干个服务(service)----> 若干个特征(characteristic) ---->Descriptor(形容)。

    iOS大概每次能接受90个字节, 安卓大概每次能接收20个字节。

    app 作为主设备(Center)

    看到上面的属性连可以很清晰的了解app 作为主设备扫描外设的几个步骤:
    1、创建一个CBCentralManager实例来进行蓝牙管理;
    2、搜索扫描外围设备;
    3、连接外围设备;
    4、获得外围设备的服务;
    5、获得服务的特征;
    6、从外围设备读取数据;
    7、给外围设备发送(写入)数据。

    1.初始化
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

    2.开始扫描

    • -- 初始化成功自动调用
    • -- 必须实现的代理,用来返回创建的centralManager的状态。
    • -- 注意:必须确认当前是CBCentralManagerStatePoweredOn状态才可以调用扫描外设的方法: scanForPeripheralsWithServices
    - (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表示默认扫描所有可见蓝牙设备。
                 -- 注意:第一个参数是用来扫描有指定服务的外设。然后有些外设的服务是相同的,比如都有FFF5服务,那么都会发现;而有些外设的服务是不可见的,就会扫描不到设备。
                 -- 成功扫描到外设后调用didDiscoverPeripheral
                 */
                [self.centralManager scanForPeripheralsWithServices:nil options:nil];
            }
                break;
            default:
                break;
        }
    }
    

    3.发现外设,根据外设名字过滤,可以开放接口提供扫描到的外设列表。

    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary*)advertisementData RSSI:(NSNumber *)RSSI{
        NSLog(@"Find device:%@", [peripheral name]);
        if (![_deviceDic objectForKey:[peripheral name]]) {
            NSLog(@"Find device:%@", [peripheral name]);
            if (peripheral!=nil) {
                if ([peripheral name]!=nil) {
                    if ([[peripheral name] hasPrefix:@"根据设备名过滤"]) {
                        [_deviceDic setObject:peripheral forKey:[peripheral name]];
                         // 停止扫描, 看需求决定要不要加
    //                    [_centralManager stopScan];
                        // 将设备信息传到外面的页面(VC), 构成扫描到的设备列表
                        if ([self.delegate respondsToSelector:@selector(dataWithBluetoothDic:)]) {
                            [self.delegate dataWithBluetoothDic:_deviceDic];
                        }
                    }
                }
            }
        }
    }
    

    4.连接外设,调用connectPeripheral方法,可以开放接口。

    // 连接设备(.h中声明出去的接口, 一般在点击设备列表连接时调用)
    - (void)connectDeviceWithPeripheral:(CBPeripheral *)peripheral
    {
    
        [self.centralManager connectPeripheral:peripheral options:nil];
    }
    

    5.连接外设成功,需要扫描服务(对应属性链)。

    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
        //连接成功后停止扫描,节省内存
        [central stopScan];
        peripheral.delegate = self; //此时代理更换为外设的代理,接下来获取的都是外设的一些属性
        self.peripheral = peripheral;
      
        /**
         --     外设的服务、特征、描述等方法是CBPeripheralDelegate的内容,所以要先设置代理peripheral.delegate = self
         --     参数表示你关心的服务的UUID,比如我关心的是"FFE0",参数就可以为@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回调内容就只有这两个UUID的服务,不会有其他多余的内容,提高效率。nil表示扫描所有服务
         --     成功发现服务,回调didDiscoverServices
         */
        [peripheral discoverServices:@[[CBUUID UUIDWithString:@"你要用的服务UUID"]]];
        if ([self.delegate respondsToSelector:@selector(didConnectBle)]) {
           // 已经连接 
            [self.delegate didConnectBle];
        }
    }
    
    

    当然也有可能会出发连接失败或者取消连接的回调。
    连接失败:
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@"%@", error); }
    取消连接:
    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@"%@", peripheral); }

    6.发现服务的回调,进一步调用发现服务的特征方法。

    #pragma mark 发现服务回调
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
        //NSLog(@"didDiscoverServices,Error:%@",error);
        CBService * __nullable findService = nil;
        // 遍历服务
        for (CBService *service in peripheral.services)
        {
            //NSLog(@"UUID:%@",service.UUID);
            if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"你要用的服务UUID"]])
            {
                findService = service;
            }
        }
        NSLog(@"Find Service:%@",findService);
        if (findService)
            [peripheral discoverCharacteristics:NULL forService:findService];
    }
    

    7.发现特征回调

    /**
     --  发现特征后,可以根据特征的properties进行:读readValueForCharacteristic、写writeValue、订阅通知setNotifyValue、扫描特征的描述discoverDescriptorsForCharacteristic。
     **/
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
        for (CBCharacteristic *characteristic in service.characteristics) {
            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你要用的特征UUID"]]) {
    
                /**
                 -- 读取成功回调didUpdateValueForCharacteristic
                 */
                self.characteristic = characteristic;
                // 接收一次(是读一次信息还是数据经常变实时接收视情况而定, 再决定使用哪个)
    //            [peripheral readValueForCharacteristic:characteristic];
                // 订阅, 实时接收
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
    
                // 发送下行指令(发送一条)
                NSData *data = [@"硬件工程师给我的指令, 发送给蓝牙该指令, 蓝牙会给我返回一条数据" dataUsingEncoding:NSUTF8StringEncoding];
                // 将指令写入蓝牙
                    [self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
            }
            /**
             -- 当发现characteristic有descriptor,回调didDiscoverDescriptorsForCharacteristic
             */
            [peripheral discoverDescriptorsForCharacteristic:characteristic];
        }
    }
    

    8.从外设读取数据

    #pragma mark - 获取值
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
        // characteristic.value就是蓝牙给我们的值(我这里是json格式字符串)
        NSData *jsonData = [characteristic.value dataUsingEncoding:NSUTF8StringEncoding];
            NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
        // 将字典传出去就可以使用了
    }
    #pragma mark - 中心读取外设实时数据
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
        if (characteristic.isNotifying) {
            [peripheral readValueForCharacteristic:characteristic];
        } else { 
            NSLog(@"Notification stopped on %@.  Disconnecting", characteristic);
            NSLog(@"%@", characteristic);
            [self.centralManager cancelPeripheralConnection:peripheral];
        }
    }
    

    9.给外设发送数据

    - (void)writeCheckBleWithBle
    {
        _style = 1;
        // 发送下行指令(发送一条)
        NSData *data = [@"硬件工程师提供给你的指令, 类似于5E16010203...这种很长一串" dataUsingEncoding:NSUTF8StringEncoding];
        [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
    }
    

    写入成功的回调.

    - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
        NSLog(@"写入成功");
        if ([self.delegate respondsToSelector:@selector(didWriteSucessWithStyle:)]) {
            [self.delegate didWriteSucessWithStyle:_style];
        }
    }
    

    10.其他

    - (void)scanDevice
    {
        if (_centralManager == nil) {
        self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
        [_deviceDic removeAllObjects];
         }
    }
    
    #pragma mark 断开连接
    - (void)disConnectPeripheral{
        /**
         -- 断开连接后回调didDisconnectPeripheral
         -- 注意断开后如果要重新扫描这个外设,需要重新调用[self.centralManager scanForPeripheralsWithServices:nil options:nil];
         */
        [self.centralManager cancelPeripheralConnection:self.peripheral];
    }
    
    #pragma mark 停止扫描外设
    - (void)stopScanPeripheral{
        [self.centralManager stopScan];
    }
    

    以上就是app 作为主设备扫描的一些主要步骤以及接收数据,发送数据的大概流程了,end.

    相关文章

      网友评论

          本文标题:iOS BLE app作为主设备

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