美文网首页
03.BLE数据处理之项目总结(二)

03.BLE数据处理之项目总结(二)

作者: 越来越胖了 | 来源:发表于2020-05-15 18:25 被阅读0次

    前面的蓝牙连接,这里就不再讲解了,直接从连接成功后,初始化发送指令给蓝牙说起;
    项目中,蓝牙连接是进行了封装的,数据处理逻辑也封装成了SDK,这里就不给出代码了,主要讲大致的流程分析;

    1. 连接成功,拿到蓝牙的回调:
    - (void)bleManager:(CRBlueToothManager *)manager didConnectDevice:(CRBleDevice *)device{
        NSLog(@"%@",[NSThread currentThread]);
        [self.connectBtn setTitle:@"连接成功" forState:0];
        
        //连接成功--->SDK发送心跳包 
    [SDK shareInstance].delegate = self;
     [[SDK shareInstance] didConnectDevice:device];
    }
    #pragma mark - *******************连接蓝牙成功后的准备工作*******************
    -(void)didConnectDevice:(CRBleDevice *)device{
        NSString *key = [self getKeyFormDevice:device];
        [self.analyzingDataDict setObject:[NSMutableData data] forKey:key];
        [self.gluDeviceTypeDicts setObject:[NSNumber numberWithInt:1] forKey:key];
        [self sendHeartForDevice:device];
    }
    

    主要操作就是配置SDK的蓝牙数据处理后返回的代理和 sdk的一些初始化操作;sdk是一个单利;

    对于所有的命令,我单独写了一个类,这个类中把所有的命令都集中在这里了:

    #pragma mark 生成命令data
    -(void)createCommandDataWithToken:(Byte)tokenByte length:(Byte)commandLenght type:(Byte)commandType device:(CRBleDevice *)device{
        [self createCommandDataWithToken:tokenByte length:commandLenght type:commandType data:nil dataLenth:0 device:device];
    }
    
    -(void)createCommandDataWithToken:(Byte)tokenByte length:(Byte)commandLenght type:(Byte)commandType data:(Byte[])commandDataBytes dataLenth:(NSUInteger)commandDataBytesLength device:(CRBleDevice *)device{
        //无数据传入情况下,6byte;
        NSUInteger allByteLength = 6;
        if (commandDataBytes) {
            allByteLength += commandDataBytesLength;
        }
        //创建一个存储所有byte的对象
        Byte *bytes = malloc(allByteLength*sizeof(Byte));
        bytes[0] = CRHEADFIRST;
        bytes[1] = CRHEADSECOND;
        bytes[2] = tokenByte;
        bytes[3] = commandLenght;
        bytes[4] = commandType;
        if (commandDataBytesLength>0) {
            for (int i=5; i<allByteLength-1; i++) {
                bytes[i] = commandDataBytes[i-5];
            }
        }
        bytes[allByteLength-1] = 0x00;//占位
        //生成一个CRC
        bytes[allByteLength-1] = [self getCRCValue:bytes andLen:allByteLength];
        //转成data
        NSData *commandData = [NSData dataWithBytes:bytes length:allByteLength];
        //发送指令
        [self writeCommand:commandData ForDevice:device];
        free(bytes);
    }
    #pragma mark 写数据到特征值中
    - (void)writeCommand:(NSData *)commandData ForDevice:(CRBleDevice *)device
    {
        if (!commandData || !commandData.length){
            return;
        }
        [device.peripheral writeValue:commandData forCharacteristic:device.writeCharact type:CBCharacteristicWriteWithoutResponse];
    }
    
    
    image.png

    这是命令的结构;

    上面就是指令的统一拼接,使用如下:
    比如要给设备设置时间

    #pragma mark 设置时间
    -(void)setDeviceTimer:(CRBleDevice *)device{
        Byte *timeByte = [self backTimerBytes];
        Byte commandLength = 0x08;
        Byte commandType = 0x03;
        [self createCommandDataWithToken:TokenQueryDeviceInfo length:commandLength type:commandType data:timeByte dataLenth:6 device:device];
        free(timeByte);
    }
    #pragma mark 获取当前时间
    - (NSString *)getTime{
        NSDate *date = [NSDate date];
        NSCalendar *cal = [NSCalendar currentCalendar];
        NSDateComponents *components = [cal components:NSCalendarUnitYear fromDate:date];
        NSString *strYear = [NSString stringWithFormat:@"%d", (int)[components year]];
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
        NSString *timestamp1 = [formatter stringFromDate:date];
        NSString *noHaveYear = [timestamp1 substringFromIndex:4];
        NSString *timestamp = [strYear stringByAppendingString:noHaveYear];
        return timestamp;
    }
    //返回时间的byte数组 --->返回的是指针,在使用过后需要进行free(byte);
    -(Byte *)backTimerBytes{
        NSString *time = [self getTime];
        Byte *bytes = malloc(6*sizeof(Byte));
        bytes[0] = [time substringWithRange:NSMakeRange(2, 2)].intValue;
        bytes[1] = [time substringWithRange:NSMakeRange(5, 2)].intValue;
        bytes[2] = [time substringWithRange:NSMakeRange(8, 2)].intValue;
        bytes[3] = [time substringWithRange:NSMakeRange(11, 2)].intValue;
        bytes[4] = [time substringWithRange:NSMakeRange(14, 2)].intValue;
        bytes[5] = [time substringWithRange:NSMakeRange(17, 2)].intValue;
        return bytes;
    }
    
    

    蓝牙返回的所有数据都会通过方法:
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error传给我们,把数据全部传入到sdk中

    #pragma mark 获取到设备发送的二进制数据
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        NSData *data = characteristic.value;
    CRBleDevice *device = [self getConnectedDeviceForPeripheral:peripheral];
       [[SDK shareInstance] appendingNewData:data FromDevice:device]; //通过appendingNewData方法把数据统一收集,后面是处理数据
    }
    
    
    

    蓝牙数据处理:

    1. 保存数据
    -(void)appendingNewData:(NSData *)data FromDevice:(CRBleDevice *)device{
        if (!self.delegate) {
            return;
        }
        //保存数据
        [[self getAnalyzingDataForDevice:device] appendData:data];
        //分析数据
        [self analyzForDevice:device];
    }
    
    
    1. 分析数据
    #pragma mark 分析数据
    - (void)analyzForDevice:(id)device{
        //获取到所有的剩下的未处理的数据(以处理的删除)
        NSData *data = [self getAnalyzingDataForDevice:device];
        //检查包-->分析包头-->保存指令model-->得到当前接收data中的所有command
        [self checkPackageForDevice:device];
        //获取
        NSMutableArray *commands = [self getCommandArrayForDevice:device];
        //对指令集进行遍历分析
        for (ByteModel *command in commands){
            if (command.complete){
                [self analyzeCommand:command ForDevice:device];
            }
        }
        //清空所有处理过的数据
        [commands removeAllObjects];
    }
    #pragma mark 检查包
    /**
     记录包头位置
     初始化命令集合
     通过各包头位置对数据进行分析
     */
    - (void)checkPackageForDevice:(CRBleDevice *)device{
        NSMutableData *analyzingData = [self getAnalyzingDataForDevice:device];
        //1.包小于5,包不完整,不解包
        if (analyzingData.length < 5)
            return ;
        Byte *receiveData = (Byte *)[analyzingData bytes];
        //记录包头的位置
        NSMutableArray <NSNumber *>*headCount = [NSMutableArray array];
        //2.遍历保存的数据,
        for (int i = 0; i < analyzingData.length - 1; i ++)
        {
           
            if (receiveData[i] == CRHEADFIRST && receiveData[i + 1] == CRHEADSECOND)
            {
                [headCount addObject:[NSNumber numberWithInt:i]];
            }
        }
        
        //如果没有包头
        if (!headCount.count)
            return ;
        
        //检查是否为首次接收数据,如果不是,则认为在检索到的包头之前的数据都无效
        //之前只要有接收,commands中就会有数据存在;
        NSMutableArray *commands = [self getCommandArrayForDevice:device];
        if(!commands){
            //初始化命令数组
            [self initCommandArrayForDevice:device];
            //将第一个包头前面的数据丢掉,让包头站在保存数据的第一个
            [analyzingData resetBytesInRange:NSMakeRange(0, headCount.firstObject.intValue)];
            [analyzingData replaceBytesInRange:NSMakeRange(0, headCount.firstObject.intValue) withBytes:NULL length:0];
        }
        
        //通过各包头位置对数据进行分析
        [self checkPackageHead:headCount ForDevice:device];
        
    }
    #pragma mark 通过各包头位置对数据进行分析存储
    - (void)checkPackageHead:(NSArray<NSNumber *>*)headCount ForDevice:(CRBleDevice *)device {
        //要分析的数据
        NSMutableData *analyzingData = [self getAnalyzingDataForDevice:device];
        //只有一个包头的情况下
        if (headCount.count == 1){
            
            //每次将data成功转为命令时,将此次data从缓存区中删除0ByteModel
            ByteModel *model = [self commandModelWithData:analyzingData ForDevice:device];
            
            //包头有效,保存命令model,将包头前的数据丢弃
            if (model){
                NSMutableArray *commands = [self getCommandArrayForDevice:device];
                [commands addObject:model];
                [self deleteProcessedDataWithLength:headCount.firstObject.intValue device:device];
            }
            return;
        }
        
        //缓冲区里含有多个包头
        for (int i = 0; i < headCount.count; i++){
            NSData *commandData;
            ByteModel *model;
            
            //前面的包头
            if ((i + 1) != headCount.count){
                int nextHeadPosition = headCount[i + 1].intValue - headCount[i].intValue;
                commandData = [analyzingData subdataWithRange:NSMakeRange(0, nextHeadPosition)];
            }else{//最后一个包头
               commandData = analyzingData;
            }
            
            //转为命令模型
            model = [self commandModelWithData:commandData ForDevice:device];
            //转换成功则保存命令
            if (model){
                NSMutableArray *commands = [self getCommandArrayForDevice:device];
                [commands addObject:model];
                continue;
            }
            //转换失败,如果不是最后一个包头,则认为在此包的数据无效,清除该段数据
            if ((i + 1) != headCount.count)
                [self deleteProcessedDataWithLength:headCount[i+1].intValue device:device];
            
        }
    }
    
    

    得到指令集合后,可以分token进行数据的划分处理:

    - (void)analyzeCommand:(ByteModel *)command ForDevice:(id)device
    {
        Byte token = command.token;
        //分token处理
        switch (token)
        {
            case 0xff:
                [self handleShakeHandsPackageWithCommand:command ForDevice:device];
                break;
    ......
    

    同一个token下,数据类型还要具体的分类进行处理:

    token == 0xFF下,返回的类型:
     0x01 ---> 产品名称
     0x04 ---> 版本等级
     0x02 ---> 版本等级(老机器)
     0x03 --->查询时间 ---> 中央需要应答
     o0x10---->  文档
     其他的token类推;
    

    因为代码不能上传,涉及到公司的东西,所以上面的很多代码也是省略了很多东西的,只能讲个大致流程吧,如果不明白可以留言,我单独弄个伪代码出来.

    相关文章

      网友评论

          本文标题:03.BLE数据处理之项目总结(二)

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