美文网首页
iOS蓝牙中心端开发

iOS蓝牙中心端开发

作者: oldSix_Zhu | 来源:发表于2017-09-11 23:45 被阅读200次

    iOS蓝牙开发分为两部分,一部分是手机作为中心端;一部分是手机作为外设端。
    我的项目是医疗类的,手机作为中心端,收到病人穿戴的外设发过来的脑电、心电等数据传给服务器,一般的APP都是开发中心端。
    原理性的东西我就不写了,网上有很多,主要记录我在实际开发项目时用到的方法与一些注意点。

    第一步 配置文件
    第二步 选择控制器
    第三步 实现协议

    形形色色的蓝牙硬件
    第一步 配置文件

    设置下工程plist文件,让用户允许APP使用蓝牙:

    如果你想让手机在后台时也可以与外设进行交互的话,还要添加一个key:

    或者这样配置:

    BackgroundMode

    具体解释看这里: iOS后台模式BackgroundModeiOS后台模式教程 (一)


    第二步 选择控制器

    首先要有一个前提,就是你的设备要在后台接收数据的话(也就是程序在后台,及手机黑屏),必须要有一个控制器引用着蓝牙中心类,最基础的是放在根控制器中。这一步很关键。
    当然,我们一般根控制器是一个UITabBarController,但是上面的几个RootViewController都是可以的。
    尤其需要注意的是,当你想要切换控制器还想操纵蓝牙外设的时候,也要把蓝牙中心和外设当做属性传给下一个控制器,然后在下一个控制器中再重新连接,设置代理,实现协议等等...

    我们的控制器选好了后(我的是HomeVC),就在该控制器中引入框架,遵循协议,设置属性:

    #import "JDHomeVC.h"
    #import <CoreBluetooth/CoreBluetooth.h>
    
    @interface JDHomeVC()<CBCentralManagerDelegate, CBPeripheralDelegate>
    
    //中心
    @property (nonatomic,strong) CBCentralManager *centralManager;
    //外设
    @property (nonatomic,strong) CBPeripheral *peripheral;
    //特征
    @property (nonatomic, strong) CBCharacteristic *characteristic;
    
    @end
    

    然后在你想要启动蓝牙的方法里加入以下代码(比如“连接外设”按钮),启动蓝牙相关的一系列代码

    //初始化中心端,开始蓝牙模块
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    self.centralManager.delegate = self;
    

    第三步 实现协议:
    1、<CBCentralManagerDelegate>协议:

    当centralManager创建之后,会立刻监测手机蓝牙状态,触发第一个代理方法,如果没有打开蓝牙,会有系统的弹窗“打开蓝牙来允许“XXXX”连接到配件”,并可以点击“设置”跳转到蓝牙。

    第一个代理方法:

    // 状态更新后触发
    - (void)centralManagerDidUpdateState:(CBCentralManager *)central
    {
        switch (central.state) {
            case CBCentralManagerStatePoweredOff://蓝牙关闭
                break;
            case CBCentralManagerStatePoweredOn:
                break;
            case CBCentralManagerStateResetting:
                break;
            case CBCentralManagerStateUnauthorized:
                break;
            case CBCentralManagerStateUnknown:
                break;
            case CBCentralManagerStateUnsupported://当前设备不支持蓝牙
                break;
            default:
                break;
        }
        // services参数为nil时, 表示扫描所有的蓝牙外设. 指定时只扫描匹配UUID的外设
        [central scanForPeripheralsWithServices:nil options:nil];
    }
    

    第二个代理方法:

    // 扫描到外部设备后触发//多次调用的
    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(nonnull CBPeripheral *)peripheral advertisementData:(nonnull NSDictionary<NSString *,id> *)advertisementData RSSI:(nonnull NSNumber *)RSSI
    {
    //    扫描到的外部设备
    //    NSString *msg = [NSString stringWithFormat:@"信号强度: %@, 外设: %@", RSSI, peripheral];
    //    NSLog(@"%@",msg);
        if ([peripheral.name isEqualToString:@"BLE"])
        {
            //连接外部设备
            self.peripheral = peripheral;
            [central connectPeripheral:peripheral options:nil];
            //停止搜索
            [central stopScan];
        }
    }
    

    这时候可以打开你的外设的开关,就可以通过程序搜索出来外设的名字和信号值了;
    在这里可以做处理是否直接连接外设,比如ofo是以“ofo”为开头的蓝牙名称;
    连接外设时要注意强引用外设,保存一下;
    我们在连接上外设后,必须要停止搜索,以免影响蓝牙连接的稳定性。

    第三个代理方法:

    //连接失败
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
    {
        NSLog(@"%@",error.localizedDescription);
        [self.centralManager connectPeripheral:self.peripheral options:nil];
    }
    

    当连接失败的时候,我们要用中心端再次尝试连接我们保存的外设。

    第四个代理方法:

    // 当中心端连接上外设时触发
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
    {
        NSLog(@"连接上外设");
        self.peripheral.delegate = self;
        [peripheral discoverServices:nil];
    }
    

    当我们的中心类已经连上了外设,就要开始实现外设协议的方法了,所以要设置代理。

    第五个代理方法:

    //如果连接上的两个设备突然断开了,程序里面会自动回调下面的方法
    -   (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
    {
        NSLog(@"设备断开重连");
        [self.centralManager connectPeripheral:self.peripheral options:nil];
    }
    

    当我们的外设与手机断开连接的时候,肯定要做一些处理,让他们再连接上。
    在这里不得不说,蓝牙硬件芯片的好坏,信号的好坏太重要了,如果是便宜的芯片,在连接上之后会隔个几秒钟就调用这个方法。。。
    所以我们不仅要做断开重连,如果你上传给服务器的数据是按赫兹来传的话,还要对上传的数据做缺省处理。

    当然了,中心类还有别的代理方法,不过我就用到了这五个,下面是外设的代理方法。

    2、<CBPeripheralDelegate>协议:

    第一个代理方法:

    // 外设端发现了服务时触发
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    {
        NSLog(@"%@",peripheral.services);
        for (CBService *service in peripheral.services)
        {
            //只找有用的服务
            if ([service.UUID.description isEqualToString:@“外设服务的UUID名称”])
            {
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }
    

    一个外设有很多服务UUID,所以是一个数组,要从这些UUID中找出有用的服务,然后要discover一下,才能发现服中的特征。

    第二个代理方法:

    //从服务获取特征
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
    {
        NSLog(@"%@",service.characteristics);
        for (CBCharacteristic *characteristic in service.characteristics)
        {
           NSLog(@"%@",service.characteristics);
        for (CBCharacteristic *characteristic in service.characteristics)
        {
            // -------- 读特征的处理 --------
            if ([characteristic.UUID.description isEqualToString: @"读特征的UUID名称"])
            {
                NSLog(@"处理读特征");
                [self.peripheral readValueForCharacteristic:characteristic];
            }
            
            // -------- 写特征的处理 --------
            if ([characteristic.UUID.description isEqualToString: @"写特征的UUID名称"])
            {
                NSLog(@"处理写特征");
                //向外设发送0001命令
                NSData *data = [@"0001" dataUsingEncoding:NSUTF8StringEncoding];
                [self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
                self.characteristic = characteristic;
            }
    
            // -------- 订阅特征的处理 --------
            if ([characteristic.UUID isEqual:[CBUUID @"订阅特征的UUID名称"]])
            {
                NSLog(@"处理了订阅特征");
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
        }
    }
    

    这里就是真正与外设交互的地方了,一个UUID有三种属性:可读,可写,可监听。

    可读属性,一般用来读取一次时使用。比如外设的名称,电量之类的,不常用。

    可写属性,就是向外设发指令。这个很重要,不过当你写入失败的时候,记得更换你writeValue方法后面的type试试,有的是有回调的,用CBCharacteristicWriteWithResponse,有的是没有回调的,用type:CBCharacteristicWriteWithoutResponse
    一般都保存下可写特征,在外面写入发指令。

    可监听属性,就是接收外设发送的数据了。一般实时变化的数据都要监听外设的,很重要。值得注意的是,当监听成功后,特征的Notify属性的值会“=Yes”,以此判断你是否监听成功了。

    第三个代理方法:

    // 写特征CBCharacteristicWriteWithResponse的数据写入的结果回调
    - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        if (error) {
            NSLog(@"数据写入失败: %@", error);
        } else {
            NSLog(@"数据写入成功");
            [peripheral readValueForCharacteristic:characteristic];
        }
    }
    

    这个就是当type为CBCharacteristicWriteWithResponse时,向外设写入命令后的回调

    第四个代理方法:

    //获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        NSLog(@"%@",characteristic.value);
    }
    

    这里就是从外设获取数据的方法。
    向外设读特征读后,该方法只调用一次;
    监听外设的监听特征后,这个方法会在外设发送一个数据时调用一次,也就是说会多次调用。
    可以在这个方法里做一些数据处理,然后将数据发送给服务器。

    比如说服务器要接收的数据是按赫兹来接收的(就是一秒钟几个数据,还要对应时间),那就要自定义个网络工具类,不能直接用AFN了事,然后要把从外设接收的数据转成服务器要求的格式(比如多长时间一个包,定义字段等)。这时就要使用NSMutableData在这个方法里拼接数据,然后设置请求体。
    如果遇到上述情况,想知道我是如何处理的,可以看看我的另一篇文章。

    第五个代理方法:

    // 订阅特征的值改变时触发的回调
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        NSLog(@"订阅特征的值改变了 : %@", characteristic);
        NSLog(@"%@",characteristic.value);
    }
    

    当订阅特征的值改变时会触发该方法,用处不大。

    蓝牙的基本功能实现就是这些,高级一点会把蓝牙的相关代理方法封装成一个中心管理类,可以看下我的下一篇文章:iOS封装蓝牙中心管理者类
    还有我在开发过程中遇到了什么问题,是如何处理的,会在另一篇文章记下来。
    如果有帮助到你,给个喜欢吧:-D

    相关文章

      网友评论

          本文标题:iOS蓝牙中心端开发

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