美文网首页
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