最近幾個(gè)月都在做藍(lán)牙的項(xiàng)目,趁現(xiàn)在有空蝌戒,就把在藍(lán)牙開(kāi)發(fā)過(guò)程中的心得和踩過(guò)的坑給記錄下來(lái)设凹,分享給大家贸街,避免大家在藍(lán)牙開(kāi)發(fā)過(guò)程中能避免踩相同的坑。
本文是記錄的藍(lán)牙開(kāi)發(fā)系列第二篇狸相,想看第一篇關(guān)于藍(lán)牙開(kāi)發(fā)的使用請(qǐng)看這里
iOS 藍(lán)牙開(kāi)發(fā)(框架集成和數(shù)據(jù)交互)
正文:
概念:藍(lán)牙固件升級(jí)也叫空中升級(jí)薛匪,英文OTA,指在線(xiàn)下載好更新固件卷哩,在把固件文件發(fā)送到藍(lán)牙外設(shè)進(jìn)行升級(jí)蛋辈,類(lèi)似我們的手機(jī)系統(tǒng)更新。一般固件都是zip類(lèi)型的壓縮文件将谊,解壓后會(huì)有三個(gè)文件:
工具:使用開(kāi)源的第三方升級(jí)庫(kù)可以很方便地為藍(lán)牙外設(shè)進(jìn)行升級(jí)冷溶,這個(gè)庫(kù)有Android和iOS兩個(gè)版本:
IOS-Pods-DFU-Library
Android-DFU-Library
需要注意的是iOS是swift版本,如果你的項(xiàng)目是OC實(shí)現(xiàn)的尊浓,集成會(huì)有點(diǎn)麻煩,這里推薦用CocoaPod導(dǎo)入逞频,需要注意的是要在Podfile文件添加use_frameworks!
集成后添加swift橋接文件,為了省事栋齿,強(qiáng)烈建議在導(dǎo)入升級(jí)庫(kù)之前創(chuàng)建一個(gè)swift文件苗胀,Xcode會(huì)在你第一次創(chuàng)建swift文件時(shí)自動(dòng)詢(xún)問(wèn)你是否創(chuàng)建橋接文件,只要點(diǎn)擊“是”就完事了瓦堵。
這時(shí)候你會(huì)發(fā)現(xiàn)Xcode自動(dòng)創(chuàng)建一個(gè).h文件基协,文件名為
"你的項(xiàng)目名-Bridging-Header.h"
接下來(lái)在橋接文件里import你在oc類(lèi)需要用到的swift文件即可
使用方法,第一步,現(xiàn)在服務(wù)器下載好固件菇用,存入本地
NSURL *url = [NSURL URLWithString:@"https://**********"];
AFHTTPSessionManager *mange = [AFHTTPSessionManager manager];
[[mange downloadTaskWithRequest:[NSURLRequest requestWithURL:url] progress:^(NSProgress * _Nonnull downloadProgress) {
NSString *progress = [NSString stringWithFormat:@"%.0f%%",1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount *100];
dispatch_async(dispatch_get_main_queue(), ^{
self.hud.labelText = NSStringFormat(@"下載進(jìn)度...%@",progress);
self.hud.progress = 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount;
});
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSURL *downloadURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [downloadURL URLByAppendingPathComponent:@"zipFile.zip"];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
//藍(lán)牙升級(jí)
[self uploadFileToBlueDevice:filePath];
}] resume];
}];
第二步澜驮,執(zhí)行藍(lán)牙升級(jí)
/**
執(zhí)行升級(jí)文件發(fā)送到固件操作
*/
- (void)uploadFileToBlueDevice:(NSURL *)filePath{
CBPeripheral *peripheral = [AYBlueHelp shareBlue].peripheral;
DFUFirmware *selectedFirmware = [[DFUFirmware alloc] initWithUrlToZipFile:filePath];
DFUServiceInitiator *initiator = [[DFUServiceInitiator alloc] initWithCentralManager: self.manage target:peripheral];
[initiator withFirmware:selectedFirmware];
initiator.delegate = self; // - to be informed about current state and errors
initiator.logger = self;
initiator.progressDelegate = self;
[initiator start];
}
最后一步,遵守協(xié)議惋鸥,實(shí)現(xiàn)代理方法杂穷,監(jiān)聽(tīng)升級(jí)狀態(tài)
#pragma mark - LoggerDelegate
- (void)logWith:(enum LogLevel)level message:(NSString *)message{
DLog(@"logWith---------level = %d,-------message,%@",level,message);
}
#pragma mark - DFUServiceDelegate
#pragma mark - DFUProgressDelegate
- (void)dfuProgressDidChangeFor:(NSInteger)part outOf:(NSInteger)totalParts to:(NSInteger)progress currentSpeedBytesPerSecond:(double)currentSpeedBytesPerSecond avgSpeedBytesPerSecond:(double)avgSpeedBytesPerSecond{
self.hud.labelText = NSStringFormat(@"升級(jí)中...%zd %%",progress);
self.hud.progress = progress * 0.01;
}
- (void)dfuStateDidChangeTo:(enum DFUState)state{
DLog(@"dfuStateDidChangeTo-----------state = %d",state);
if (state == 6) {
[self.hud hide:YES];
[MBProgressHUD wj_showSuccess:@"升級(jí)成功!"];
//重新連接
CBPeripheral *peripheral = [AYBlueHelp shareBlue].peripheral;
[self.manage cancelPeripheralConnection:peripheral];
self.manage = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
[AYBlueHelp shareBlue].manage = self.manage;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.manage scanForPeripheralsWithServices:nil options:nil];
});
}
}
- (void)dfuError:(enum DFUError)error didOccurWithMessage:(NSString *)message{
// DLog(@"dfuError-----------error = %d,-------------message = %@",error,message);
[self.hud hide:YES];
[MBProgressHUD wj_showError:message];
}
這里三個(gè)坑非常有必要重點(diǎn)強(qiáng)調(diào)一下:
第一個(gè)坑:
服務(wù)器存放的固件名字為01&02&20180501,如果你把這個(gè)名字作為你的文件名存放在本地你會(huì)發(fā)現(xiàn)文件永遠(yuǎn)都是不對(duì)的卦绣,這個(gè)坑我前后弄了2個(gè)小時(shí)才發(fā)現(xiàn)原來(lái)文件名包含“&”特殊字符耐量,需要進(jìn)行轉(zhuǎn)義,這里建議直接以zipFile.zip等自定義的名字命名即可滤港±妊眩坑死了···**
第二個(gè)坑:
前面我們把固件下載后存在本地,本來(lái)以為下次再進(jìn)行下載的時(shí)候以相同的路徑和文件名存放會(huì)覆蓋掉前面的文件蜗搔,這樣每次下載下來(lái)的固件都是最新的劲藐,然而事實(shí)上并不會(huì),沙盒里還是你上一次的文件樟凄,需要我們手動(dòng)舊文件刪除再存新文件聘芜!
第三個(gè)坑:
我們進(jìn)行藍(lán)牙升級(jí)后,流程是要把固件重啟后再次自動(dòng)連接缝龄。因?yàn)槲覀兊挠布こ處煾嬖V我只要在升級(jí)前發(fā)送個(gè)協(xié)議命令汰现,升級(jí)后設(shè)備會(huì)自動(dòng)重啟挂谍,我只需要做好自動(dòng)連接就可以了。問(wèn)題就在于貌似是重新連接成功了瞎饲,設(shè)備也顯示已連接狀態(tài)口叙,手機(jī)也顯示已連接,但是接下來(lái)卻一直收不到藍(lán)牙數(shù)據(jù)嗅战,也無(wú)法發(fā)送藍(lán)牙數(shù)據(jù)交互妄田,一開(kāi)始是手動(dòng)先斷開(kāi)連接,再重新掃描驮捍,再連接疟呐。然而發(fā)現(xiàn)執(zhí)行斷開(kāi)連接:[self.manage cancelPeripheralConnection:self.peripheral];并不會(huì)執(zhí)行didDisconnectPeripheral代理方法,重新掃描也不會(huì)收到廣播數(shù)據(jù)...
最后分析有可能是升級(jí)完畢后我們的藍(lán)牙管理類(lèi)centerManage可能已經(jīng)失效或者被銷(xiāo)毀东且?因此嘗試重新創(chuàng)建启具,再進(jìn)行掃描操作,問(wèn)題解決I河尽B撤搿!
self.manage = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.manage scanForPeripheralsWithServices:nil options:nil];
});
到此為止色查,有關(guān)藍(lán)牙升級(jí)的操作已經(jīng)寫(xiě)完了薯演,下一篇,我會(huì)總結(jié)一下在藍(lán)牙開(kāi)發(fā)過(guò)程中遇到的一些坑??
喜歡的點(diǎn)個(gè)紅心秧了,有什么問(wèn)題歡迎評(píng)論區(qū)討論~~