美文网首页
01.BLE数据处理,Byte数组,malloc,位操作(一)

01.BLE数据处理,Byte数组,malloc,位操作(一)

作者: 越来越胖了 | 来源:发表于2020-04-30 17:27 被阅读0次

    1. 设备端发送过来的数据为NSData,需要把data数据转成byte:

    Byte * byte = (Byte *)[data bytes];
    

    byte转data

    NSData *data = [[NSData alloc] initWithBytes:byte length:6];
    

    下面是把一个string转成byte数组再进行拼接的过程:

    -(void)demo1{
        
        NSString *abc = @"abc";
    
        NSData *data = [abc dataUsingEncoding:NSUTF8StringEncoding];
        
        NSUInteger len = data.length;
        
        Byte *byte = (Byte *)[data bytes];
        
        NSString *commandString = @"";
        
        NSMutableArray *commandArray = [NSMutableArray arrayWithCapacity:10];
        
        for (int i = 0; i < len; i++){
            NSString *byteS =  [NSString stringWithFormat:@"%02x",byte[i]];
            [commandArray addObject:byteS];
        }
        commandString = [commandArray componentsJoinedByString:@"-"];
        
        NSLog(@"data == %@  len==%lu  commandString == %@",data,len,commandString);
        
    }
    
    

    解析数据时,我建立了一个数据模型:

    @interface CommandModel : NSObject
    /** 包头第一个字节  */
    @property (nonatomic, assign) Byte firstHead;
    /** 包头的第二个字节  */
    @property (nonatomic, assign) Byte secondHead;
    /** token  */
    @property (nonatomic, assign) Byte token;
    /** dataType 数据类型 */
    @property (nonatomic, assign) Byte dataType;
    /** 内容长度  */
    @property (nonatomic, assign) Byte contentLen;
    /** 内容  */
    @property (nonatomic, strong) NSMutableArray <NSNumber *>*content;
    /** CRC校验码  */
    @property (nonatomic, assign) Byte crc;
    /** 命令是否完整  */
    @property (nonatomic, assign) BOOL complete;
    @end
    

    2. 取Byte数组中的数据:

    model.head = byte[0]; 
    model.secondHead = byte[1];
    model.token = byte[2];
    直接下标法取值
    
    

    2.1 创建一个byte数组,往数组中添加元素

    -(void)demo{
      NSArray <NSNumber *>*arr = @[@10,@20,@30];//要添加的元素
    Byte *byteData = (Byte *)malloc(arr.count*sizeof(Byte));//初始化一个byte数组
    for (int i=0; i<arr.count; i++) {
            byteData[i] = arr[i].intValue;
        }
        NSLog(@"%hhu---%hhu",byteData[0],byteData[1]);
    //添加对byte数组的使用方法 XXX
    
    //最后,一定要释放:
     free(byteData);
    }
    

    2.2 取两个字节中的低12位bit有效数据

    每个采样点数据占 2 字节,共 16 位,先低字节,后高字节。其中只有低 12位为有效数据
    Byte dataByte[] = {0X80,0X86,0X9C,0XAA,0x33,0x6e};
    NSMutableArray <NSNumber *>*content = [NSMutableArray array];
        
        for (int i=0; i<sizeof(dataByte); i++) {
            [content addObject:[NSNumber numberWithInt:dataByte[i]]];
        }
        
        int waveData[3] = {0};
        for (int i=0; i<content.count; i+=2) {
            
            int value = content[i].intValue + ((content[i+1].intValue &0x0f)<<8);
            waveData[i/2] = value;
        }
    
    

    2.3 截取byte[]中一部分数据,从begin 开始,长度是 count

    - (void)bytesplit2byte:(Byte[])src orc:(Byte[])orc begin:(NSInteger)begin count:(NSInteger)count{
        for (NSInteger i = begin; i < begin+count; i++){
            orc[i-begin] = src[i];
        }
    }
    
    //调用
     NSInteger count = 2;//截取的大小
        
        Byte *orc = (Byte *)malloc(count*sizeof(Byte));;
        
        [self bytesplit2byte:dataByte orc:orc begin:0 count:2];
        
        for (int i=0; i<2; i++) {
            NSLog(@"%hhu",orc[i]);
        }
        free(orc);
    
    
    

    2.4 需求:传给后台的data数据,要能被200整除,同时最后一位要传入data的大小;例: data长度是 195 则要补5位且最后一位是data长度;data 长度是 205 则要补 195

    -(void)demo32{
        NSData *data = [@"123456abcdef" dataUsingEncoding:NSUTF8StringEncoding];
        NSLog(@"data == %@",data);
        //要传给后台的mut_data
        NSMutableData *mut_data = [[NSMutableData alloc] initWithData:data];
        NSUInteger addNum = 200 - mut_data.length%200; //需要补得长度
        Byte *bytes = malloc(addNum*sizeof(Byte));//创建补到data中的byte数组
        //给数组添加元素
        for (int i=0; i<addNum; i++) {
            int byte = 0;
            if (i == addNum-1) {
                byte = (int)data.length;
            }
            bytes[i] = byte;
        }
        //拼接
        [mut_data appendBytes:bytes length:addNum];
        NSLog(@"新的data ==  %@",mut_data);
        free(bytes);
        
        
        //*************************************************************************
        //解析后台返回的同样的data
        NSData *backData = mut_data;
        
        Byte *testBytes =(Byte *)[backData bytes];
        
        int data_num = testBytes[backData.length-1];//最后一位是长度
        
        Byte * new_bytes = (Byte *)malloc(data_num*sizeof(Byte)); //储存有效数据的byte数组
        
        memcpy(new_bytes, testBytes, data_num);//拷贝
        
        NSData *new_data = [[NSData alloc] initWithBytes:new_bytes length:data_num];
        
        NSLog(@"解析到后台返回的data == %@",new_data);
        free(new_bytes);
    }
    
    

    C中的一些函数的解释:

    • malloc()函数:void *malloc(unsigned int num_bytes);
      功能:分配长度为num_bytes字节的内存块
      如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL
      当内存不再使用时,应使用free()函数将内存块释放;
      malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的,申请的内存是连续的。
      返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。
    • 工作机制:
      malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。
      调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。
    • 调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。
      如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断
    • memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作;
      Byte * new_bytes = (Byte )malloc(countsizeof(int));
      memset(new_bytes, 0, sizeof(int)*count);
      参考:https://www.cnblogs.com/fyp7077/p/7666309.html
      C语言中的一些常用函数的介绍:https://www.runoob.com/cprogramming/c-standard-library-stdlib-h.html

    下面是验证malloc分配空间不足时的情况:

    -(void)demo33{
        /**
         系统返回错误:
         *** error: can't allocate region
         *** set a breakpoint in malloc_error_break to debug
         */
        
        int data_num = 1000000;
        Byte * new_bytes = (Byte *)malloc(data_num*data_num*data_num*sizeof(int));
        if (NULL == new_bytes) {
            NSLog(@"内存空间不足,初始化失败");
        }else{
            NSLog(@"初始化成功✌️");
        }
    }
    

    3. 删除data数据中的部分数据 ,Range自己定义

    [analyzingData resetBytesInRange:NSMakeRange(0, (int)model.contentLen )];
        [analyzingData replaceBytesInRange:NSMakeRange(0, (int)model.contentLen) withBytes:NULL length:0];
    

    4. int 转 byte数组

    int value = 10;
    //开始转换为4个字节
        Byte byteData[4] = {};
        
        byteData[0] =(Byte)((value & 0xFF000000)>>24);
        
        byteData[1] =(Byte)((value & 0x00FF0000)>>16);
        
        byteData[2] =(Byte)((value & 0x0000FF00)>>8);
        
        byteData[3] =(Byte)((value & 0x000000FF));
        //开始拼接
        Byte byte[] = {0x80,0x9B,byteData[0],byteData[1],byteData[2],byteData[3]};
        //转为NSData
        NSData *temphead = [[NSData alloc]initWithBytes:byte length:6];//80 9B 00 00 6B 57
    

    5. data拼接

    NSMutableData *m_data = [[NSMutableData alloc] init];
    [m_data appendData:data1];
     [m_data appendData:data2];
    

    %hu用于输出一个unsigned short int类型的数值,此类型占两个字节,范围为0到65535(2^16-1)
    %hhu用于输出一个unsigned short类型的数值,此类型占一个字节,范围为zd0到255(2^8-1)

    6. 截取data解析(一)

    NSData *lengthData = [head subdataWithRange:NSMakeRange(2, 4)];//取得长度数据
    //2 将4个字节的数据还原为content的长度
        //2.1 将lengthData转为16进制数
        NSString* hexString = [lengthData convertDataToHexStr];
        //2.2 将16进制数转为10进制
        NSInteger length = [[hexString hexToDecimal]integerValue];//得出内容长度
        
        NSInteger complateDataLength = length + 6;//算出一个包完整的长度(内容长度+头长度)
    
    

    其中转换的分类:

    #import "NSData+SwitchData.h"
    
    @implementation NSData (SwitchData)
    /**
     NSData 转  十六进制string
     
     @return NSString类型的十六进制string
     */
    - (NSString *)convertDataToHexStr{
        if (!self || [self length] == 0) {
            return @"";
        }
        NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[self length]];
        
        [self enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
            unsigned char *dataBytes = (unsigned char*)bytes;
            for (NSInteger i = 0; i < byteRange.length; i++) {
                NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
                if ([hexStr length] == 2) {
                    [string appendString:hexStr];
                } else {
                    [string appendFormat:@"0%@", hexStr];
                }
            }
        }];
        
        return string;
    }
    
    /**
     NSData 转 NSString
     
     @return NSString类型的字符串
     */
    - (NSString *)dataToString {
        Byte *bytes = (Byte *)[self bytes];
        NSMutableString *string = [[NSMutableString alloc] init];
        for(int i = 0; i< [self length]; i++) {
            if (i == 0) {
                [string appendString:[NSString stringWithFormat:@"%hhu",bytes[i]]];
            }else {
                [string appendString:[NSString stringWithFormat:@",%hhu",bytes[i]]];
            }
        }
        return string;
    }
    
    
    /**
     十六进制转十进制
     
     @return 十进制字符串
     */
    - (NSString *)hexToDecimal {
        return [NSString stringWithFormat:@"%lu",strtoul([self UTF8String],0,16)];
    }
    
    

    07. data解析(二)

    个人感觉上面的方式比较麻烦,所以下面第二种方法:

    //截取数据的包头:  809b
        //截取最左边的809b这两个字节(16位),这个数据是UInt16类型
        uint16_t result = [self unsignedDataTointWithData:data Location:0 Offset:2];
    
    
    // 转为本地大小端模式 返回Unsigned类型的数据
    - (unsigned int)unsignedDataTointWithData:(NSData *)data Location:(NSInteger)location Offset:(NSInteger)offset {
        unsigned int value=0;
        NSData *intdata= [data subdataWithRange:NSMakeRange(location, offset)];
        
        if (offset==2) {
            value=CFSwapInt16BigToHost(*(int*)([intdata bytes]));
        }
        else if (offset==4) {
            value = CFSwapInt32BigToHost(*(int*)([intdata bytes]));
        }
        else if (offset==1) {
            unsigned char *bs = (unsigned char *)[[data subdataWithRange:NSMakeRange(location, 1) ] bytes];
            value = *bs;
        }
        return value;
    }
    
    
    1. 取字节中的bit 数据
      接着07中的809b讲,假设,这里是假设,因为包头不可能这样干:809b取出来了,对应的二进制是:1000 0000 1001 1011(算不过来的小伙伴直接上计算器).假设:低四位 1011为每组的人数,5-8位(1001)为组数,我们应该怎么获取 组数 和 人数 ?
    //这个result就是上面的 uint16_t result 
    [self getGroupAndGroupNumWith:result];
    
    
    //低四位 1011为每组的人数,5-8位(1001)为组数,我们应该怎么获取?
    -(void)getGroupAndGroupNumWith:(uint16_t)result{
       //获取5-8位,先把除了5-8位的其他位置0 result& 0x00f0  (0000 0000 1111 0000)--> 0000 0000 1001 0000 也就是 1001 0000
        //然后右移4位 --> 1001
        int group = (result & 0x00f0) >> 4;
        NSLog(@"组数 == %d",group);
        int num = (result & 0x000f);
        NSLog(@"人数 == %d",num);
        
    }
    
    

    对位操作中的 与 或 异或 取反 << >> 的介绍

    位与 &
    特点:
    (1)清零:任何数和0相与,结果为0.
    (2)取出指定位的值。取哪一位,就把对应的位定为1。

    按位或 | 只要有一个为1就为1
    特点:
    (1)对数据的某些位置1。

    异或运算 ^
    如果对应的位不同则为1,相同为0
    特点:
    (1)特定位翻转,哪一位需要翻转就把对应的位设置为1
    (2)任何数和0异或,原值不变。
    (3)异或运算可以交换位置:3 ^ 5 ^ 6 == 3 ^ 6 ^ 5
    (4)相同的数异或等于0:9 ^ 9 == 0
    (5)a ^ b ^ a == b

    取反 ~
    0变1,1变0
    特点:
    (1)配合按位与把一个数的最低位设置为0

    左移运算 <<
    二进制位全部左移若干位,左边的丢弃,右边补0

    右移运算 >>
    二进制右移若干位,正数左边补0,负数左边补1,右边丢弃。

    链接:https://www.jianshu.com/p/1b3c8fc6995a

    公司在做蓝牙项目,数据传输全是byte数组,此文不定期更新,如有不对的地方,请不吝赐教.

    5月14号新更:

    1. 需求:希望在方法中获取到形参byte数组的字节数
    Byte dataByte[] = {0X80,0X86,0X9C,0XAA,0x33,0x6e,0XAA,0x33,0x6e};
        [self testBytes:dataByte];//拿到的永远是8字节,也就是指针的大小,所以 这种方式,如果方法内向用字节数组大小,只能传进去 sizeof(dataByte) ,能力有限,不会其他方法了;
    
    
    //需求-->在方法内 需要 获取到传入的byte数组的字节数
    -(void)testBytes:(Byte[])bytes{
    
    }
    
    -(void)test_two:(Byte *)bytes{
        
    }
    
    

    目前唯一能想到的一个方法就是,先把 dataByte[]转成NSData,因为外部的dataByte[]的大小可以用sizeof(dataByte)获取得到data,而data又data.length属性

    1. 把时间转成byte数组传给蓝牙外设
    #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;
    }
    
    使用
    #pragma mark 设置时间
    -(void)setDeviceTimer{
        Byte *timeByte = [self backTimerBytes];
        Byte commandLength = 0x08;
        Byte commandType = 0x03;
    //使用--------->看个人的开发需求
        [self createCommandDataWithToken:0xFF length:commandLength type:commandType data:timeByte dataLenth:6 device:nil];
    //释放
        free(timeByte);
    }
    
    

    获取12个bit表示的压力值大小

    需求
    content 中的第2,3个元素,也就是两个字节,表示压力的大小(content中的content[0],是数据类型):
    具体是 MSB(高字节)的bit0-bit3 和LSB(低字节)的 bit0-bit7
    也就是压力使用的 12个bit表示的,因为一个字节不够,而16bit又多了

    且MSB中的bit4 :1为有心跳,0为无心跳
    获取心跳和压力大小

    -(void)demo34{
        //后台的data--->转成了 content
        NSArray <NSNumber *>*content = @[@(0x11),@(0x7d),@(0x6c),@(0x7a)];
        
        BOOL heartBeat = (content[1].intValue>>4) & 0x01;
        
        NSLog(@"heartBeat == %d",heartBeat);
        
        int pressureValue = (content[1].intValue & 0x0f)*256 + content[2].intValue;
        //*256是因为高bit前面已经有了一个字节长度了
        NSLog(@"%d",pressureValue);
        
        //下面这种写法是错误的,左移四位,意味着右侧补0,0x7d(0111 1101) 变成了 0111 1101 000
        // & 0x0f ---> 变成了 0000 1101,这个才是我们要的
        //int pers_two = (content[1].intValue <<4)*256 + content[2].intValue;
        
    }
    
    
    

    后面还写了一篇文章,就是对我们公司项目中的蓝牙数据处理的具体流程总结,大家可以看看.

    相关文章

      网友评论

          本文标题:01.BLE数据处理,Byte数组,malloc,位操作(一)

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