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

背景

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

問題

傳輸大一點的數(shù)據(jù)就會被截斷,也就是說每次傳輸?shù)臄?shù)據(jù)包大小很小(測試是一次155個诲泌,但是ble上說是20個盲赊,appleDemo也是分包20個字節(jié))。

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

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

結(jié)果明顯被截斷了:

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)

因為ble傳輸包大小限制角钩,主流做法還有直接操作字節(jié)的方式。但是對要傳輸復雜數(shù)據(jù)明顯用字節(jié)形式不夠用呻澜。

解決方案

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

packge.png

如上圖所示,模擬命令分成15個包傳輸過來恭朗,每個包大小固定位最大20個字符,第1-14包為模擬命令數(shù)據(jù)依疼,第十五個包只發(fā)送了一個EOM字符串痰腮,接收端每接收到一個包則把這個數(shù)據(jù)追加到上一個包數(shù)據(jù)上,直到收到EOM標識律罢,則把當前收到的所有數(shù)據(jù)進行解析膀值,解析完成就可以進行下一步業(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è)備在訂閱后更新特征值都會調(diào)用此代理方法)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (error) {
        NSLog(@"更新特征值時發(fā)生錯誤误辑,錯誤信息:%@",error.localizedDescription);
        [self writeToLog:[NSString stringWithFormat:@"更新特征值時發(fā)生錯誤沧踏,錯誤信息:%@",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

具體直接運行Demo很直觀的可以看到結(jié)果,本Demo能夠展現(xiàn)出已收到巾钉。但是未處理未收到重發(fā)等翘狱,因為目前的產(chǎn)品無此業(yè)務(wù)需求。
DemoGit地址砰苍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潦匈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子师骗,更是在濱河造成了極大的恐慌历等,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辟癌,死亡現(xiàn)場離奇詭異寒屯,居然都是意外死亡,警方通過查閱死者的電腦和手機黍少,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門寡夹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厂置,你說我怎么就攤上這事菩掏。” “怎么了昵济?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵智绸,是天一觀的道長。 經(jīng)常有香客問我访忿,道長瞧栗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任海铆,我火速辦了婚禮迹恐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卧斟。我一直安慰自己殴边,他們只是感情好憎茂,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锤岸,像睡著了一般竖幔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上能耻,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天赏枚,我揣著相機與錄音亡驰,去河邊找鬼晓猛。 笑死,一個胖子當著我的面吹牛凡辱,可吹牛的內(nèi)容都是我干的戒职。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼透乾,長吁一口氣:“原來是場噩夢啊……” “哼洪燥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乳乌,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤捧韵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汉操,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體再来,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年磷瘤,在試婚紗的時候發(fā)現(xiàn)自己被綠了芒篷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡采缚,死狀恐怖针炉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扳抽,我是刑警寧澤篡帕,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站贸呢,受9級特大地震影響镰烧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贮尉,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一拌滋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猜谚,春花似錦败砂、人聲如沸赌渣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坚芜。三九已至,卻和暖如春斜姥,著一層夾襖步出監(jiān)牢的瞬間鸿竖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工铸敏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缚忧,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓杈笔,卻偏偏與公主長得像闪水,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蒙具,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容

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