iOS轻松搞定蓝牙开发

作者: NotFunGuy | 来源:发表于2017-05-16 13:37 被阅读137次

    最近刚做了一个蓝牙开发的项目,要求通过蓝牙接收数据和写入数据,下面将蓝牙开发的流程做一个简单的介绍。

    概念理解

    开发蓝牙之前,通过看看官方文档和网上查看资料了解了蓝牙开发的大体流程,首先得理解关于蓝牙开发中的一些概念。我们都是通过基于蓝牙4.0的低功耗蓝牙进行开发,开发用到的框架是CoreBluetooth。下面解释几个概念:

    中心者模式和管理者模式

    中心者模式:这个使用的很普遍,就是手机作为主机,蓝牙作为外设。
    管理者模式:我们手机自己作为外设,自己创建服务和特征,然后有其他的设备连接我们的手机。

    中心CBCentral和外设CBPeripheral

    BLE中的交互涉及两个重要角色:中心CBCentral和外围设备(简称外设)CBPeripheral。外设通常具有其他设备所需要的数据,中心通常使用外设提供的信息来实现特定的功能。

    服务CBService和特征CBCharacteristic

    每个外设CBPeripheral中包含一个或者多个服务CBService以及有关其连接信号的信息。服务是实现一个函数或者功能的设备的数据采集和相关行为的集合。每个服务下面又有很多特征CBCharacteristic,特征提供了这些服务的详细内容。我们在进行BLE蓝牙开发的时候,就需要通过特定服务下面的特定特征来获取想要的数据和内容,特征是与外界交互的最小单位。

    UUID

    关于UUID,暂时可以理解为蓝牙的唯一标识,每个服务和特征都会有相应的UUID,可以通过UUID来代表特定的服务和特征。

    步骤:

    1. 导入头文件#import <CoreBluetooth/CoreBluetooth.h>
    2. 遵守协议CBCentralManagerDelegate,CBPeripheralDelegate
    3. 建立中心管理者CBCentralManager对蓝牙进行管理
    4. 扫描外设,使用方法scanForPeripheralsWithServices
    5. 连接扫描到的外设
    6. 扫描并获取外设的服务CBService
    7. 扫描并获取外射服务下面的特征CBCharacteristic
    8. 获取特征的值,即从外围设备读取数据,用didUpdateValueForCharacteristic方法
    9. 订略特征的通知,实现当连接蓝牙成功之后以通知的方式实现对特征里面数据的读写。需要调用- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic方法
    10. 写入数据,调用writeValue:forCharacteristic:type:方法

    示例代码

    1. 建立中心管理者和外设
    /** 主设备(可以扫描和链接外设备) */
    @property (nonatomic, strong) CBCentralManager * centeralManger;
    /** 外设备 */
    @property (nonatomic, strong) CBPeripheral * peripheral;
    /** 用于保存被发现的设备 */
    @property (nonatomic, strong) NSMutableArray * discoverPeripherals;
    

    2.初始化中心管理者并设置委托

        // 初始化并设置委托和线程
        self.centeralManger = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
        self.discoverPeripherals = [[NSMutableArray alloc]init];
    

    将queue的参数设置为nil,默认就是在住线程中执行。

    3.初始化中心管理者之后,用代理方法监听主设备状态的改变,当检测到蓝牙打开以后,调用扫描外设的方法:

    #pragma mark - CBCentralManagerDelegate
    /**
     * 主设备状态改变
     */
    - (void)centralManagerDidUpdateState:(CBCentralManager *)central{
        
        switch (central.state) {
            case CBManagerStateUnknown:
                NSLog(@">>>CBManagerStateUnknown");
                break;
            case CBManagerStateResetting:
                NSLog(@">>>CBManagerStateResetting");
            case CBManagerStateUnsupported:
                NSLog(@">>>CBManagerStateUnsupported");
            case CBManagerStateUnauthorized:
                NSLog(@">>>CBManagerStateUnauthorized");
            case CBManagerStatePoweredOff:
                NSLog(@">>>CBManagerStatePoweredOff");
                
    //            [SVProgressHUD setBackgroundColor:[UIColor yellowColor]];
                [SVProgressHUD showErrorWithStatus:@"请打开手机蓝牙开关!"];
                break;
                
            case CBManagerStatePoweredOn:
                NSLog(@">>>CBManagerStatePoweredOn");
                
                // 开始扫描外围设备
                /**
                 * 第一个参数为nil就是扫描周围所有的外设,扫描后会进入
                 */
                [self.centeralManger scanForPeripheralsWithServices:nil options:nil];
                break;
            default:
                break;
        }
    }
    
    1. 在扫描到外设的代理方法里面持有外设
    // 扫描到设备会进入方法
    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
        
        [SVProgressHUD dismiss];
        
        NSLog(@"扫描到设备名字:%@", peripheral.name);
        
        for (CBPeripheral * thePeripheral in self.discoverPeripherals) {
            
            if ([thePeripheral.name isEqual:peripheral.name]) return;
            
        }
        
        if (peripheral) {
            
            //找到设备必须持有他,否者CBCentralManager也不会保存peripheral
            [self.discoverPeripherals addObject:peripheral];
            
            
            NSLog(@"discoverPeripherals - %@", self.discoverPeripherals);
            
            [self.BLEtableView reloadData];
            
            // 如果在这停止扫描 那么就每次就只能扫描到一个外设备
    //        [self.centeralManger stopScan];
        }else{
            
            [SVProgressHUD showErrorWithStatus:@"没有扫描到蓝牙设备!"];
        }   
    }
    

    5.用代理方法实现连接蓝牙,在连接成功的方法里面实现读取数据的通知,并设置外设的代理

    // 连接成功
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
        
        NSLog(@">>>连接到名称为(%@)的设备 - 成功", peripheral.name);
        
        [SVProgressHUD showSuccessWithStatus:@"连接成功!"];
        
        
        peripheral.delegate = self;
        
        //扫描外设备的services
        [peripheral discoverServices:nil];
        
    
            
            //发送通知
            [self notifyCharacteristic:peripheral characteristic:self.characteristic];
    
        [self.centeralManger stopScan];
    }
    
    
    // 连接到peripherals - 失败
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
        
        NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
        [SVProgressHUD showErrorWithStatus:@"连接失败!"];
    }
    
    // peripherals连接断开
    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
        
        NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);
        
        [SVProgressHUD showErrorWithStatus:@"连接断开!"];
    }
    

    6.用外设的代理方法扫描服务和特征

    #pragma mark  - CBPeripheralDelegate
    
    // 扫描到services
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
        
        
        
        if (error) {
            NSLog(@"扫描失败原因 - %@", error.localizedDescription);
            return;
        }
        
        NSLog(@"扫描到服务 - %@", peripheral.services);
        
        // 扫描每个服务的特征
        for (CBService * service in peripheral.services) {
            
            NSLog(@"service 的 UUID : %@", 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.localizedFailureReason);
            return;
        }
        
        
        
        for (CBCharacteristic * characteristics in service.characteristics) {
            
            NSLog(@"services:%@ 的 characteristics:%@", service.UUID, characteristics.UUID);
            
            
        }
        
        // 获取characteristics的值
        for (CBCharacteristic * characteristics in service.characteristics) {
            
            [peripheral readValueForCharacteristic:characteristics];
            
        }
        
        // 搜索characteristics的description
        for (CBCharacteristic * characteristics in service.characteristics) {
            
            [peripheral discoverDescriptorsForCharacteristic:characteristics];
            
            [peripheral setNotifyValue:YES forCharacteristic:characteristics];
        }
        
        
    }
    

    7.读取特征的值(即读取数据)

    // 获取characteristics的值 - 从外围设备读数据
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
       
     NSLog(@"characteristic 的 uuid:%@--value:%@",characteristic.UUID,characteristic.value);
    
     if (error) {
            NSLog(@"Error changing notification state: %@", [error localizedDescription]);
        }
        self.characteristic = characteristic;   
    }
    

    8.实现写入数据的方法

    // 写数据 - 将数据写入特征
    - (void)writeCharactereristic:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic value:(NSData *)value{
        
        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(@"该字段不可写!");
        }
        
    }
    

    8 征订通知和取消通知

    // 6.订阅特征的通知
    //设置通知
    -(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];
    }
    

    如果将扫描到的蓝牙用tebleView来显示,就会得到如下效果:


    相关文章

      网友评论

        本文标题:iOS轻松搞定蓝牙开发

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