iOS 藍(lán)牙固件升級(jí)

升級(jí)介紹

藍(lán)牙固件升級(jí)是使用手機(jī)給固件進(jìn)行版本升級(jí)吹由,以達(dá)到修復(fù)bug或者添加新功能的作用呛讲。升級(jí)的大概流程是:首先,當(dāng)藍(lán)牙固件需要升級(jí)時(shí)恩商,由嵌入式開(kāi)發(fā)人員提供新的固件变逃,由服務(wù)器管理人員將固件放到服務(wù)器上,此時(shí)怠堪,用戶打開(kāi)手機(jī)APP的時(shí)候會(huì)檢測(cè)到服務(wù)器有更新揽乱,請(qǐng)求最新的藍(lán)牙固件,確認(rèn)更新后粟矿,手機(jī)會(huì)從服務(wù)器下載固件凰棉。下載完畢后,APP會(huì)讀取固件內(nèi)容陌粹,并根據(jù)升級(jí)協(xié)議將內(nèi)容傳到藍(lán)牙固件里撒犀,完成升級(jí)。

DFU = Device Firmware Update (設(shè)備固件更新)

OTA = Over The Air (空中升級(jí))

升級(jí)流程

各個(gè)藍(lán)牙設(shè)備不盡相同掏秩,以下是我測(cè)試設(shè)備的升級(jí)流程:升級(jí)固件為.bin后綴的文件或舞,文件名會(huì)有一定的格式。

我司的硬件工程師一般發(fā)送給我們的升級(jí)軟件是hex文件蒙幻,然后升級(jí)的時(shí)候需要的是bin文件這就需要我們把hex文件轉(zhuǎn)換為bin文件嚷那。轉(zhuǎn)換bin文件有兩種方式:

1.通過(guò)軟件的方式,軟件的方式我是通過(guò)軟件J-Flash來(lái)轉(zhuǎn)換的杆煞。具體操作流程如下:

(1)打開(kāi)J-Flash選擇Create a new project魏宽。

(2)把hex文件拖入J-Flash 。

(3)找到hex文件對(duì)應(yīng)的結(jié)束的最后一位的位置决乎。

(4)選擇Save data file as 保存類型選bin類型队询,然后點(diǎn)擊保存之后彈出Enter address range框 start address 保持不變,End address 輸入你想要轉(zhuǎn)換文件的結(jié)束地址构诚,然后點(diǎn)擊OK hex轉(zhuǎn)bin文件轉(zhuǎn)換成功蚌斩。

2.通過(guò)到代碼的方式。轉(zhuǎn)換的代碼如下:

//

//? LBHexToBin.m

//

//? Created by lingbing on 2020/9/18.

//

#import

NS_ASSUME_NONNULL_BEGIN

@interface LBHexToBin : NSObject

+ (NSData*)convert:(NSData*)hex;

@end

NS_ASSUME_NONNULL_END

//

//? LBHexToBin.m

//

//? Created by lingbing on 2020/9/18.

//

#import "LBHexToBin.h"

@implementation LBHexToBin

+ (constByte)ascii2char:(constByte*)ascii

{

? ? if(*ascii >='A')

? ? ? ? return*ascii -0x37;


? ? if(*ascii >='0')

? ? ? ? return*ascii -'0';

? ? return-1;

}

+ (constByte)readByte:(constByte*)pointer

{

? ? Bytefirst = [LBHexToBinascii2char:pointer];

? ? Bytesecond = [LBHexToBinascii2char:pointer +1];


? ? return(first <<4) | second;

}

+ (constUInt16)readAddress:(constByte*)pointer

{

? ? Bytemsb = [LBHexToBinreadByte:pointer];

? ? Bytelsb = [LBHexToBinreadByte:pointer +2];


? ? return(msb <<8) | lsb;

}

+ (NSUInteger)calculateBinLength:(NSData*)hex

{

? ? if(hex ==nil|| hex.length==0)

? ? {

? ? ? ? return0;

? ? }


? ? NSUIntegerbinLength =0;

? ? constNSUIntegerhexLength = hex.length;

? ? constByte* pointer = (constByte*)hex.bytes;

? ? UInt32lastBaseAddress =0;


? ? do

? ? {

? ? ? ? constBytesemicollon = *pointer++;


? ? ? ? // Validate - each line of the file must have a semicollon as a firs char

? ? ? ? if(semicollon !=':')

? ? ? ? {

? ? ? ? ? ? return0;

? ? ? ? }


? ? ? ? constUInt8reclen = [LBHexToBinreadByte:pointer]; pointer +=2;

? ? ? ? constUInt16offset = [LBHexToBinreadAddress:pointer]; pointer +=4;

? ? ? ? constUInt8rectype = [LBHexToBinreadByte:pointer]; pointer +=2;


? ? ? ? switch(rectype) {

? ? ? ? ? ? case0x04: {

? ? ? ? ? ? ? ? // Only consistent hex files are supported. If there is a jump to non-following ULBA address skip the rest of the file

? ? ? ? ? ? ? ? constUInt32newULBA = [LBHexToBinreadAddress:pointer];

? ? ? ? ? ? ? ? if(binLength >0&& newULBA != (lastBaseAddress >>16) +1)

? ? ? ? ? ? ? ? ? ? returnbinLength;

? ? ? ? ? ? ? ? lastBaseAddress = newULBA <<16;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? ? ? case0x02: {

? ? ? ? ? ? ? ? // The same with Extended Segment Address. The calculated ULBA must not be greater than the last one + 1

? ? ? ? ? ? ? ? constUInt32newSBA = [LBHexToBinreadAddress:pointer] <<4;

? ? ? ? ? ? ? ? if(binLength >0&& (newSBA >>16) != (lastBaseAddress >>16) +1)

? ? ? ? ? ? ? ? ? ? returnbinLength;

? ? ? ? ? ? ? ? lastBaseAddress = newSBA;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? ? ? case0x00:

? ? ? ? ? ? ? ? // If record type is Data Record (rectype = 0), add it's length (only it the address is >= 0x1000, MBR is skipped)

? ? ? ? ? ? ? ? if(lastBaseAddress + offset >=0x1000)

? ? ? ? ? ? ? ? ? ? binLength += reclen;

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? break;

? ? ? ? }


? ? ? ? pointer += (reclen <<1);? // Skip the data when calculating length

? ? ? ? pointer +=2;? // Skip the checksum

? ? ? ? // Skip new line

? ? ? ? if(*pointer =='\r') pointer++;

? ? ? ? if(*pointer =='\n') pointer++;

? ? }while(pointer != hex.bytes+ hexLength);


? ? returnbinLength;

}

+ (NSData*)convert:(NSData*)hex

{

? ? constNSUIntegerbinLength = [LBHexToBincalculateBinLength:hex];

? ? constNSUIntegerhexLength = hex.length;

? ? constByte* pointer = (constByte*)hex.bytes;

? ? NSUIntegerbytesCopied =0;

? ? UInt32lastBaseAddress =0;


? ? Byte* bytes =malloc(sizeof(Byte) * binLength);

? ? Byte* output = bytes;


? ? do

? ? {

? ? ? ? constBytesemicollon = *pointer++;


? ? ? ? // Validate - each line of the file must have a semicollon as a firs char

? ? ? ? if(semicollon !=':')

? ? ? ? {

? ? ? ? ? ? free(bytes);

? ? ? ? ? ? returnnil;

? ? ? ? }


? ? ? ? constUInt8reclen = [LBHexToBinreadByte:pointer]; pointer +=2;

? ? ? ? constUInt16offset = [LBHexToBinreadAddress:pointer]; pointer +=4;

? ? ? ? constUInt8rectype = [LBHexToBinreadByte:pointer]; pointer +=2;


? ? ? ? switch(rectype) {

? ? ? ? ? ? case0x04: {

? ? ? ? ? ? ? ? constUInt32newULBA = [LBHexToBinreadAddress:pointer]; pointer +=4;

? ? ? ? ? ? ? ? if(bytesCopied >0&& newULBA != (lastBaseAddress >>16) +1)

? ? ? ? ? ? ? ? ? ? return[NSDatadataWithBytesNoCopy:byteslength:bytesCopied];

? ? ? ? ? ? ? ? lastBaseAddress = newULBA <<16;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? ? ? case0x02: {

? ? ? ? ? ? ? ? constUInt32newSBA = [LBHexToBinreadAddress:pointer] <<4; pointer +=4;

? ? ? ? ? ? ? ? if(bytesCopied >0&& (newSBA >>16) != (lastBaseAddress >>16) +1)

? ? ? ? ? ? ? ? ? ? return[NSDatadataWithBytesNoCopy:byteslength:bytesCopied];

? ? ? ? ? ? ? ? lastBaseAddress = newSBA;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? ? ? case0x00:

? ? ? ? ? ? ? ? // If record type is Data Record (rectype = 0), copy data to output buffer

? ? ? ? ? ? ? ? // Skip data below 0x1000 address (MBR)

? ? ? ? ? ? ? ? if(lastBaseAddress + offset >=0x1000)

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? for(inti =0; i < reclen; i++)

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? *output++ = [LBHexToBinreadByte:pointer]; pointer +=2;

? ? ? ? ? ? ? ? ? ? ? ? bytesCopied++;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? else

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? pointer += (reclen <<1);? // Skip the data

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? pointer += (reclen <<1);? // Skip the irrelevant data

? ? ? ? ? ? ? ? break;

? ? ? ? }


? ? ? ? pointer +=2;? // Skip the checksum

? ? ? ? // Skip new line

? ? ? ? if(*pointer =='\r') pointer++;

? ? ? ? if(*pointer =='\n') pointer++;

? ? }while(pointer != hex.bytes+ hexLength);


? ? return[NSDatadataWithBytesNoCopy:byteslength:bytesCopied];

}

@end

升級(jí)過(guò)程:

1.掃描并連接要升級(jí)的藍(lán)牙設(shè)備

2.發(fā)送給外設(shè)升級(jí)前的驗(yàn)證指令范嘱,驗(yàn)證是否是合法升級(jí)

3.外設(shè)驗(yàn)證返回成功之后送膳,發(fā)送授權(quán)升級(jí)指令员魏,指令返回成功之后開(kāi)始固件升級(jí)

3.判斷隨機(jī)數(shù)無(wú)誤,準(zhǔn)備發(fā)送打包好的數(shù)據(jù)

4.真正發(fā)送打包好的數(shù)據(jù)(每次發(fā)送10包叠聋,一包20個(gè)字節(jié))撕阎,這里會(huì)重復(fù)N多次,看你的原數(shù)據(jù)包有多大碌补;每次接到我發(fā)的包后虏束,外設(shè)都會(huì)給我會(huì)OK否,我收到OK后才會(huì)發(fā)一下個(gè)數(shù)據(jù)包

5.告訴外設(shè)我數(shù)據(jù)發(fā)送完畢厦章,并發(fā)送一段指令(包括本次空中升級(jí)數(shù)據(jù)包的大小镇匀,還有加密參數(shù)什么的)

6.外設(shè)給我回OK無(wú)誤后,才算真正升級(jí)完成

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袜啃,一起剝皮案震驚了整個(gè)濱河市汗侵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌群发,老刑警劉巖晰韵,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異也物,居然都是意外死亡宫屠,警方通過(guò)查閱死者的電腦和手機(jī)列疗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)滑蚯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人抵栈,你說(shuō)我怎么就攤上這事告材。” “怎么了古劲?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵斥赋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我产艾,道長(zhǎng)疤剑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任闷堡,我火速辦了婚禮隘膘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杠览。我一直安慰自己弯菊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布踱阿。 她就那樣靜靜地躺著管钳,像睡著了一般钦铁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上才漆,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天牛曹,我揣著相機(jī)與錄音,去河邊找鬼栽烂。 笑死躏仇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腺办。 我是一名探鬼主播焰手,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怀喉!你這毒婦竟也來(lái)了书妻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤躬拢,失蹤者是張志新(化名)和其女友劉穎躲履,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體聊闯,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡工猜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菱蔬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篷帅。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拴泌,靈堂內(nèi)的尸體忽然破棺而出魏身,到底是詐尸還是另有隱情,我是刑警寧澤蚪腐,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布箭昵,位于F島的核電站,受9級(jí)特大地震影響回季,放射性物質(zhì)發(fā)生泄漏家制。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一泡一、第九天 我趴在偏房一處隱蔽的房頂上張望颤殴。 院中可真熱鬧,春花似錦瘾杭、人聲如沸诅病。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贤笆。三九已至蝇棉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芥永,已是汗流浹背篡殷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留埋涧,地道東北人板辽。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像棘催,于是被迫代替她去往敵國(guó)和親劲弦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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