美文网首页
iOS 蓝牙开发

iOS 蓝牙开发

作者: 孔凡伍 | 来源:发表于2016-11-28 16:50 被阅读178次
    最近项目应用到蓝牙与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已经断开,但物理层没断开。

    相关文章

      网友评论

          本文标题:iOS 蓝牙开发

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