最近项目应用到蓝牙与Glass端传输数据,所以写下收货与开发中注意的事项。
我没有使用原生的Bluetooth.framework 而是使用 BabyBluetooth https://github.com/coolnameismy/BabyBluetooth 它将各种事件封装成block和链式语法,很方便使用。
蓝牙开发原理看BabyBluetooth作者博客,非常详细。
数据交互
使用蓝牙通知接受数据。使用自带的写数据。每帧20字节,每帧间隔50毫秒
使用自定义的协议,分发送头,发送体。
发送头 是Byte数组,第0位有个边标志0,代表头。1-4位代表此次发送的总字节。
发送体 byte数组,第1个字节1,代表体。其余19个字节为内容,多次发送。
以下核心代码
/**
* 根据data,组织蓝牙发送数据
*
* @param data data
*
* @return 组织后的dic.
* headData: 头部数据
* bodyArray: 内容数据
* sendCount: 发送数量,头部数据
* 蓝牙数据约定:头部数据第一位value = 1 内容数据第一个字节 value = 2做标志
*/
- (NSMutableDictionary *)organizationSendData:(NSData *)data
{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
NSMutableArray *bodyArray = [NSMutableArray array];
dic[@"bodyArray"] = bodyArray;
//---------- 发送头部数据 -----------
long value = data.length;
Byte headBytes[5] = {0};
headBytes[4] = (Byte)((value>>24) & 0xFF);
headBytes[3] = (Byte)((value>>16) & 0xFF);
headBytes[2] = (Byte)((value>>8) & 0xFF);
headBytes[1] = (Byte)(value & 0xFF);
headBytes[0] = (Byte)(kDataHeadFlag & 0xFF); // 标示符:head数据
NSMutableData *headData = [NSMutableData dataWithBytes:headBytes length:5];
dic[@"headData"] = headData;
NSLog(@"头数据长度:%ld %ld flag:%ld", (unsigned long)data.length, [self bytesToLong:headBytes], [self bytesFirstBitToLong:headBytes]);
//---------- 发送内容数据 -----------
// 一帧发送的最大字节
// NSInteger byteCount = 19;
// 计算数据最后余多少字节
NSInteger yuCount = data.length % byteCount == 0 ? byteCount : data.length % byteCount;
// 余字节大于0,多发送一帧
NSInteger yuCountTransformSendCount = (yuCount > 0 && yuCount < byteCount) ? 1 : 0;
// 总发送帧数
NSInteger sendCount = floor(data.length / byteCount) + yuCountTransformSendCount;
dic[@"sendCount"] = @(sendCount);
for (int i = 0; i < sendCount; i++) {
// 截取位置
CGFloat subDataLocation = i * byteCount;
// 截取长度
NSInteger subDataLength = byteCount;
// 最后一次,截取长度为取余长度
if (i == sendCount - 1) {
subDataLength = yuCount;
}
// 截取发送数据
NSData *subData = [data subdataWithRange:NSMakeRange(subDataLocation, subDataLength)];
Byte bodyFirstFrameBytes[1] = {(kDataBodyFlag & 0xFF)}; // 标示符:body数据
NSMutableData *bodyFirstFrameData = [NSMutableData dataWithBytes:bodyFirstFrameBytes length:1];
[bodyFirstFrameData appendData:subData];
[bodyArray addObject:bodyFirstFrameData];
}
return dic;
}
/**
* 发送数据
*
* @param data data
*/
- (BOOL)sendData:(NSData *)data
{
/* // 1秒等于1000毫秒,等于1000000微秒 50000 = 1000000 / 20
usleep(50000);// 单位微妙。50毫秒发送一次
*/
if (data.length == 0) {
return NO;
}
// 获取特征
CBCharacteristic *currentCharacteristic = self.viewModel.writeCharacteristic;
CBPeripheral *currPeripheral = self.viewModel.currPeripheral;
if (!currentCharacteristic && !currPeripheral) {
DLog(@"未连接外设或未找到特征");
return NO;
}
NSMutableDictionary *sendDic = [self organizationSendData:data];
// 发送头部数据
[currPeripheral writeValue:sendDic[@"headData"] forCharacteristic:currentCharacteristic type:CBCharacteristicWriteWithResponse];
usleep(20000);// 单位微妙。50毫秒发送一次
NSLog(@"发送头部数据:%@", sendDic[@"headData"]);
// 发送body数据
NSArray *bodyArray = sendDic[@"bodyArray"];
for (int i = 0; i < bodyArray.count; i++) {
NSLog(@"发送body数据 index: %d:%@", i, bodyArray[i]);
[currPeripheral writeValue:bodyArray[i] forCharacteristic:currentCharacteristic type:CBCharacteristicWriteWithResponse];
usleep(20000);// 单位微妙。50毫秒发送一次
}
return YES;
}
/**
* 注册通知,接收数据
*/
- (void)addNotificationCharacteristic
{
self.viewModel = kApp.deviceViewModel;
CBCharacteristic *characteristic = self.viewModel.notificationCharacteristic;
// 订阅
if (!(characteristic.properties & CBCharacteristicPropertyNotify || characteristic.properties & CBCharacteristicPropertyIndicate)) {
[FWProgressHUD showText:@"这个characteristic没有nofity的权限"];
return;
}
if(characteristic.isNotifying) {
[self.viewModel.babyBluetooth cancelNotify:self.viewModel.currPeripheral characteristic:characteristic];
} else {
__weak typeof(self) _self = self;
__block NSMutableData *data = [[NSMutableData alloc] init];
__block long allLength = 0;
[self.viewModel.babyBluetooth notify:self.viewModel.currPeripheral characteristic:characteristic block:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {
__strong typeof(_self) self = _self;
if (characteristics.value.bytes == NULL) {
DLog(@"error:第一针头部数据为NULL或解析值为-1");
return;
}
NSData *dataValue = [NSMutableData dataWithData:characteristic.value];
Byte *dataValueBytes = (Byte *)dataValue.bytes;
long flag = [self bytesFirstBitToLong:dataValueBytes];
if (flag == kDataHeadFlag) {
allLength = [self bytesToLong:dataValueBytes];
data = NSMutableData.new;
} else {
NSData *subData = [dataValue subdataWithRange:NSMakeRange(1, dataValue.length - 1)];
[data appendData:subData];
// DLog(@"接收到数据:%ld/%ld",allLength, data.length);
if (data.length == allLength) {
DLog(@"接收到数据:%@",[data JSONString]);
// 处理数据逻辑
id dataObj = [data JSONObject];
[self selectDataTypeData:dataObj sendState:NO];
allLength = 0;
data = NSMutableData.new;
}
}
}];
}
}
断开蓝开问题
cancelPeripheralConnection: 调用后不会立即断开。查了资料这是正常的。不过确实影响到我了。测试的时候App已经断开,但物理层没断开。
网友评论