BLE傳輸大數(shù)據(jù)

背景

某智能硬件模擬程序,例如模擬插卡/拔卡/加熱等事件, 采用的策略是使用模擬程序發(fā)送模擬命令,APP端解析之后,進(jìn)行下一步業(yè)務(wù)邏輯。

問題

傳輸大一點(diǎn)的數(shù)據(jù)就會(huì)被截?cái)?,也就是說每次傳輸?shù)臄?shù)據(jù)包大小很小(測(cè)試是一次155個(gè),但是ble上說是20個(gè),appleDemo也是分包20個(gè)字節(jié))。

模擬命令采用json格式,舉例:

{
    "array": [
              1,
              2,
              3
              ],
    "boolean": true,
    "null": null,
    "number": 123,
    "object": {
        "a": "b",
        "c": "d",
        "e": "f"
    },
    "string": "Hello World"
}

結(jié)果明顯被截?cái)嗔?

2017-05-16 16:50:26.417340 GlucometerTestApp[65247:10879249] 收到特征更新通知...
2017-05-16 16:50:30.353523 GlucometerTestApp[65247:10879249] 讀取到特征值:{

    "array": [

              1,

              2,

              3

              ],

    "boolean": true,

    "null": null,

    "number": 123,
2017-05-16 16:50:30.405110 GlucometerTestApp[65247:10879249] 讀取到特征值:EOM
2017-05-16 16:50:30.430806 GlucometerTestApp[65247:10879249] didReceiveData:(null)

因?yàn)閎le傳輸包大小限制,主流做法還有直接操作字節(jié)的方式。但是對(duì)要傳輸復(fù)雜數(shù)據(jù)明顯用字節(jié)形式不夠用。

解決方案

藍(lán)牙肯定也可以傳輸較大的數(shù)據(jù)包,例如圖片傳輸。應(yīng)該采用那種方案呢?在Apple Documents里搜到一個(gè)叫BTLE的Demo。
具體的核心理論就是分包,也就是把一條命令分成多次來發(fā)送,而后最后組裝一下即可。
分包示例圖:

packge.png

如上圖所示,模擬命令分成15個(gè)包傳輸過來,每個(gè)包大小固定位最大20個(gè)字符,第1-14包為模擬命令數(shù)據(jù),第十五個(gè)包只發(fā)送了一個(gè)EOM字符串,接收端每接收到一個(gè)包則把這個(gè)數(shù)據(jù)追加到上一個(gè)包數(shù)據(jù)上,直到收到EOM標(biāo)識(shí),則把當(dāng)前收到的所有數(shù)據(jù)進(jìn)行解析,解析完成就可以進(jìn)行下一步業(yè)務(wù)處理了。

Demo分包傳輸日志如下:

2017-05-16 17:03:44.832831 GlucometerTestApp[65291:10885464] 收到特征更新通知...
2017-05-16 17:03:57.853364 GlucometerTestApp[65291:10885464] 讀取到特征值:{

    "array": [
2017-05-16 17:03:57.861861 GlucometerTestApp[65291:10885464] 讀取到特征值:             1,
2017-05-16 17:03:57.869106 GlucometerTestApp[65291:10885464] 讀取到特征值:           2,
2017-05-16 17:03:57.908184 GlucometerTestApp[65291:10885464] 讀取到特征值:         3
2017-05-16 17:03:57.923027 GlucometerTestApp[65291:10885464] 讀取到特征值:      ],

    "boole
2017-05-16 17:03:57.932465 GlucometerTestApp[65291:10885464] 讀取到特征值:an": true,

    "nul
2017-05-16 17:03:57.939771 GlucometerTestApp[65291:10885464] 讀取到特征值:l": null,

    "numb
2017-05-16 17:03:57.946587 GlucometerTestApp[65291:10885464] 讀取到特征值:er": 123,

    "obje
2017-05-16 17:03:57.953552 GlucometerTestApp[65291:10885464] 讀取到特征值:ct": {

        "a":
2017-05-16 17:03:57.959731 GlucometerTestApp[65291:10885464] 讀取到特征值: "b",

        "c":
2017-05-16 17:03:57.965410 GlucometerTestApp[65291:10885464] 讀取到特征值:"d",

        "e": "
2017-05-16 17:03:57.971610 GlucometerTestApp[65291:10885464] 讀取到特征值:f"

    },

    "str
2017-05-16 17:03:57.977920 GlucometerTestApp[65291:10885464] 讀取到特征值:ing": "Hello World"
2017-05-16 17:03:57.987575 GlucometerTestApp[65291:10885464] 讀取到特征值:
}
2017-05-16 17:03:57.994471 GlucometerTestApp[65291:10885464] 讀取到特征值:EOM
2017-05-16 17:03:58.001022 GlucometerTestApp[65291:10885464] didReceiveData:{
    array =     (
        1,
        2,
        3
    );
    boolean = 1;
    null = "<null>";
    number = 123;
    object =     {
        a = b;
        c = d;
        e = f;
    };
    string = "Hello World";
}

Central

Demo上的Central端截圖

ScreenShot_20170516174707.png

收包關(guān)鍵代碼:

//更新特征值后(調(diào)用readValueForCharacteristic:方法或者外圍設(shè)備在訂閱后更新特征值都會(huì)調(diào)用此代理方法)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (error) {
        NSLog(@"更新特征值時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
        [self writeToLog:[NSString stringWithFormat:@"更新特征值時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription]];
        return;
    }
    if (characteristic.value) {
        NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
        NSLog(@"讀取到特征值:%@",value);
        [self writeToLog:[NSString stringWithFormat:@"讀取到特征值:%@",value]];
            if ([value isEqualToString:@"EOM"]) {
                //處理數(shù)據(jù)
                [self.delegate didReceiveData:self.data complate:YES];
                
                //處理完畢,清空
                [self.data setLength:0];
            }
        else
        {
            [self.data appendData:characteristic.value];
            if(self.delegate)
            {
                [self.delegate didReceiveData:self.data complate:NO];
            }
        }
    }else{
        NSLog(@"未發(fā)現(xiàn)特征值.");
        [self writeToLog:@"未發(fā)現(xiàn)特征值."];
    }
}

Peripheral

Demo上的Peripheral端截圖

ScreenShot_20170516174355.png

發(fā)包關(guān)鍵代碼:

/** Sends the next amount of data to the connected central
 */
- (void)_sendData
{
    // First up, check if we're meant to be sending an EOM
    static BOOL sendingEOM = NO;
    
    if (sendingEOM) {
        
        // send it
        BOOL didSend = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.characteristicM onSubscribedCentrals:nil];
        
        // Did it send?
        if (didSend) {
            
            // It did, so mark it as sent
            sendingEOM = NO;
            
            NSLog(@"Sent: EOM");
        }
        
        // It didn't send, so we'll exit and wait for peripheralManagerIsReadyToUpdateSubscribers to call sendData again
        return;
    }
    
    // We're not sending an EOM, so we're sending data
    
    // Is there any left to send?
    
    if (self.sendDataIndex >= self.dataToSend.length) {
        
        // No data left.  Do nothing
        return;
    }
    
    // There's data left, so send until the callback fails, or we're done.
    
    BOOL didSend = YES;
    
    while (didSend) {
        
        // Make the next chunk
        
        // Work out how big it should be
        NSInteger amountToSend = self.dataToSend.length - self.sendDataIndex;
        
        // Can't be longer than 20 bytes
        if (amountToSend > NOTIFY_MTU) amountToSend = NOTIFY_MTU;
        
        // Copy out the data we want
        NSData *chunk = [NSData dataWithBytes:self.dataToSend.bytes+self.sendDataIndex length:amountToSend];
        
        // Send it
        didSend = [self.peripheralManager updateValue:chunk forCharacteristic:self.characteristicM onSubscribedCentrals:nil];
        
        // If it didn't work, drop out and wait for the callback
        if (!didSend) {
            return;
        }
        
        NSString *stringFromData = [[NSString alloc] initWithData:chunk encoding:NSUTF8StringEncoding];
        NSLog(@"Sent: %@", stringFromData);
        
        // It did send, so update our index
        self.sendDataIndex += amountToSend;
        
        // Was it the last one?
        if (self.sendDataIndex >= self.dataToSend.length) {
            
            // It was - send an EOM
            
            // Set this so if the send fails, we'll send it next time
            sendingEOM = YES;
            
            // Send it
            BOOL eomSent = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.characteristicM onSubscribedCentrals:nil];
            
            if (eomSent) {
                // It sent, we're all done
                sendingEOM = NO;
                
                NSLog(@"Sent: EOM");
            }
            
            return;
        }
    }
}

Demo

具體直接運(yùn)行Demo很直觀的可以看到結(jié)果,本Demo能夠展現(xiàn)出已收到。但是未處理未收到重發(fā)等,因?yàn)槟壳暗漠a(chǎn)品無此業(yè)務(wù)需求。
DemoGit地址。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,030評(píng)論 25 709
  • 藍(lán)牙簡(jiǎn)介 藍(lán)牙( Bluetooth? ):是一種無線技術(shù)標(biāo)準(zhǔn),可實(shí)現(xiàn)固定設(shè)備、移動(dòng)設(shè)備和樓宇個(gè)人域網(wǎng)之間的短距離...
    Chefil閱讀 2,160評(píng)論 2 19
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • 前言: 檢測(cè)密碼 郵箱 IP地址 手機(jī)號(hào)輸入是否正確 已經(jīng)封裝好了,拖入工程就可以使用! //郵箱正則表達(dá)式 + ...
    少年_如他閱讀 1,055評(píng)論 1 2
  • 今晚的活動(dòng)是繪畫,我和女兒各自一副。 花邊是女兒加上的。她的進(jìn)步是竟然顏色都沒有溢出,沒有框在固定思維里,色彩多,...
    淼淼的媽媽閱讀 235評(píng)論 0 0

友情鏈接更多精彩內(nèi)容