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