前面的蓝牙连接,这里就不再讲解了,直接从连接成功后,初始化发送指令给蓝牙说起;
项目中,蓝牙连接是进行了封装的,数据处理逻辑也封装成了SDK,这里就不给出代码了,主要讲大致的流程分析;
- 连接成功,拿到蓝牙的回调:
- (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];
}

这是命令的结构;
上面就是指令的统一拼接,使用如下:
比如要给设备设置时间
#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方法把数据统一收集,后面是处理数据
}
蓝牙数据处理:
- 保存数据
-(void)appendingNewData:(NSData *)data FromDevice:(CRBleDevice *)device{
if (!self.delegate) {
return;
}
//保存数据
[[self getAnalyzingDataForDevice:device] appendData:data];
//分析数据
[self analyzForDevice:device];
}
- 分析数据
#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类推;
因为代码不能上传,涉及到公司的东西,所以上面的很多代码也是省略了很多东西的,只能讲个大致流程吧,如果不明白可以留言,我单独弄个伪代码出来.
网友评论