美文网首页技术学习
iOS蓝牙开发详解

iOS蓝牙开发详解

作者: 风不会停歇 | 来源:发表于2017-12-12 22:00 被阅读143次

    iOS蓝牙开发详解

    随着物联网技术的高速发展,蓝牙开发也越来越火热。不论是智能穿戴设备还是蓝牙家具,车联网蓝牙,都是通过蓝牙协议来进行通信。废话不多说了,先向大家简单的介绍有关蓝牙开发的知识。蓝牙低能耗(BLE),以下介绍的都是围绕iOS的蓝牙(4.0)框架展开的。

    蓝牙开发分为中心管理者者模式和外设管理者模式:1.常用的(90%)就是使用中心管理者者模式作为开发,就是我们手机作为中心管理者,扫描、连接蓝牙外设;2.外设管理者模式,这个基本用到的比较少,我们手机自己作为外设,自己创建服务和特征,然后通过广播,把自己的信息广播出去,其他的设备可以扫描连接到我们的手机。

    在做蓝牙开发之前,最好先了解一些概念:

    • 广播包: (Advertising)广播包是一个公开的信息,别人不需要连接你的设备就能读取到的信息;(iOS端由于苹果的限制,无法获取设备的Mac地址,只能让硬件放于广播包中。)

    • 外设: (peripheral)一个蓝牙硬件就是一个外设,一个外设下有一个或者多个服务;

    peripheral.png
    • 服务: (services)蓝牙外设对外广播的必定会有一个服务,可能也有多个,服务下面包含着一些特征值,服务可以理解成一个端口;
    service.png
    • 特征: (characteristic)特征可以理解成一个HTTP的接口,一般特征都会有value,也就是特征值。每个特征都有一个或多个权限,例如读、写、通知。向一个特征写值可以看做是发起一个HTTP请求。(和HTTP请求不同的是,如果你写入的type和硬件底层的type不配套会写不进去。无论是哪个type,系统都会通过代理回调告知我们是否写入。)因此特征是与外界交互的最小单位;
    characteristic.png
    • UUID:可以理解成蓝牙上的唯一标识符,为了区分不同的服务和特征,我们就用UUID来代表服务和特征。

    蓝牙连接可以大致分为以下几个步骤:

    1. 建立一个Central Manager实例,实现Central代理协议进行蓝牙管理。
    2. 打开蓝牙,扫描周围外设,获取广播包数据信息。
    3. 根据广播包数据连接指定外设。(目前我测试一个APP连接7个外设是没有问题,但是一个手机能连接多少个并没有测试,如果有知道的请告诉我。)
    4. 获得外设的服务。
    5. 获得服务的特征。
    6. 从外设读数据。
    7. 给外设发送数据。
    #pragma mark - 扫描、连接操作
    /**
     开始搜索
    */
    - (void)startScan {
        
        [self.manager scanForPeripheralsWithServices:nil options:nil];
    }
    /**
     开始搜索
    */
    - (void)startScanWith:(didDiscoverPeripheral)block {
        
        self.didDiscoverPeripheralBlock = block;
        [self.manager scanForPeripheralsWithServices:nil options:nil];//扫描设备,可设置扫描的服务和选项。不填写可以加快扫描速度。
    }
    /**
     连接指定设备,可设置连接选项
    */
    - (void)linkWith:(Device *)device options:(nullable NSDictionary<NSString *,id> *)dic handle:(didConnectPeripheral)block {
        
        //防止多次点击,连接多次,只让连接一个,如果做BLE4.0多连接可以取消这个限定
        if (self.linkStatus != BLEManagerUnLinked) {
            
            return;
        }
        self.linkStatus = BLEManagerLinking;
        self.didConnectPeripheralBlock = block;
        //iOS端链接外设只能用扫描到的外设,如果连接的外设为空会crash。
        if (device.per) {
            
            [self.manager connectPeripheral:device.per options:dic];
        }
        [self performSelector:@selector(linkOuttime) withObject:nil afterDelay:5];//超过五秒则认为超时,取消连接
        self.linkDevice = device;
    }
    
    /**
     连接超时
     */
    - (void)linkOuttime {
        
        if (self.linkStatus != BLEManagerLinked) {
            
            [self disconnectPeripheral:self.manager peripheral:self.linkDevice.per];
            NSError *error = [NSError errorWithDomain:@"连接超时" code:1 userInfo:nil];
            self.linkStatus = BLEManagerUnLinked;
            self.didConnectPeripheralBlock(NO, error);
        }
    }
    
    //断开连接
    - (void)disconnectPeripheral:(CBPeripheral *)peripheral {
        
        //断开连接 如果外设为空会crash
        if (peripheral) {
            
            [self.manager cancelPeripheralConnection:peripheral];
        } else {
            
            if (self.didDisconnectPeripheralBlock) {
    
                self.didDisconnectPeripheralBlock(peripheral, nil);
            }
        }
        
        [self cleanData];
    }
    
    - (void)cleanData {
        
        //数据回归初始化状态
        self.datas = [NSMutableArray array];
        self.allDevice = [@{} mutableCopy];
        self.linkStatus = BLEManagerUnLinked;
    }
    
    #pragma mark - 发送数据
    /**
     发送数据
    
     @param data 数据
     @param type 写入类型
     */
    - (void)sendData:(NSData *)data withType:(CBCharacteristicWriteType)type{
        
        //数据发送,发送的时候,外设特征值必须存在,发送的数据是NSData类型
        /*
         具体使用哪种类型,询问硬件工程师或者查看接口文档
         两者的区别在于写入的通道不同(硬件工程师和我说的),如果type不对应则会写入失败。
         type:
         type为CBCharacteristicWriteWithResponse类型时,didWriteValueForCharacteristic会返回写入调用的结果,而CBCharacteristicWriteWithoutResponse则不会。。
         
         */
    
        [self.send sendData:data withType:type];
    }
    
    
    
    /**
     外设的特征值更新通知
     
     @param peripheral 连接(更新)的外设
     @param characteristic 连接(更新)的外设的特征值
     @param error 错误信息
     */
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
        
        //打印出characteristic的UUID和值
        NSLog(@"characteristic.uuid = %@, value = %@",characteristic.UUID,characteristic.value);
        //!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据
        if ([characteristic.UUID.UUIDString isEqualToString:@"readCharacteristic"]) {
            
            NSData *data = characteristic.value;
            //把读取到的数据回调出去
            for (getData block in self.getDataBlockArr) {
                
                block(data);
            }
        } else if ([characteristic.UUID.UUIDString isEqual:readCharacteristicUUID]||[characteristic.UUID.UUIDString isEqual:notiyCharacteristicUUID]||[characteristic.UUID.UUIDString isEqual:writeCharacteristicUUID]) {
            
            NSData *data = characteristic.value;
            //把读取到的数据回调出去
            for (getData block in self.getDataBlockArr) {
                
                block(data);
            }
        }
    }
    
    /**
     已经写完数据回调
     
     @param peripheral 连接(写入)的外设
     @param characteristic 连接(写入)的外设的特征值
     @param error 是否写入成功,如果error为nil则写入成功,否则error会存在
     */
    - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
        
        NSLog(@"didWriteValue error = %@", error);
    }
    
    

    相关文章

      网友评论

        本文标题:iOS蓝牙开发详解

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