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;
}
-
取字节中的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号新更:
- 需求:希望在方法中获取到形参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属性
- 把时间转成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;
}
后面还写了一篇文章,就是对我们公司项目中的蓝牙数据处理的具体流程总结,大家可以看看.
网友评论