You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
617 lines
23 KiB
617 lines
23 KiB
![]()
2 years ago
|
//
|
||
|
// FRIUpdateOTAManager.m
|
||
|
// FRQBluetoothKit
|
||
|
//
|
||
|
// Created by chunhai xu on 2021/1/3.
|
||
|
//
|
||
|
|
||
|
#import "FRIUpdateOTAManager.h"
|
||
|
#import "FRIConvert.h"
|
||
|
#import "FRBleAbility.h"
|
||
|
#import "FRIBinFileCheck.h"
|
||
|
#import "FRIDefine.h"
|
||
![]()
1 year ago
|
#import "BluetoothFireBoltt.h"
|
||
![]()
1 year ago
|
#import "FireBoltt-Swift.h"
|
||
![]()
2 years ago
|
|
||
|
#define kFRQCmdHeadLen 3 //cmd head = 3
|
||
|
#define kFRQRspHeadLen 4 //rsp head = 4
|
||
|
#define kFRQBaseAddressResultLen 4 //base addr len 4
|
||
|
#define kFRQUpdateAddressLen 4
|
||
|
#define kFRQRspResultCodeLen 1 //response result head code
|
||
|
|
||
|
#define ERASE_PAGE_SIZE 4096 //erase out page offset
|
||
|
|
||
|
#define BLE_SEND_MAX_LEN 20 //最大发送20字节
|
||
|
#define OTA_SUBPACKAGE_LEN 235 //ota subpackage len 235
|
||
|
|
||
|
// 计时器
|
||
|
#define FRIConnectSuccessNotify @"FRIUpdateOTAManager_connectSuccessNotify"
|
||
|
#define FRIHandlerTimeOutSuccess @"FRIUpdateOTAManager_handlerTimeOutSuccess"
|
||
|
|
||
|
typedef NS_ENUM(NSUInteger, FRQRspHeadCode) {
|
||
|
FRQRspHeadCodeOk = 0, //ok
|
||
|
FRQRspHeadCodeFail = 1, //fail
|
||
|
};
|
||
|
|
||
|
//响应数据
|
||
|
@interface FRITrunk : NSObject
|
||
|
|
||
|
@property(nonatomic, assign) uint8_t rsp_code; //response code 0: ok; 1: fail
|
||
|
@property(nonatomic, assign) uint32_t base_addr; //base address
|
||
|
@property(nonatomic, assign) uint32_t erase_base_addr; //erase base address
|
||
|
|
||
|
@property(nonatomic, strong) NSData *rsp_data; //rsp data
|
||
|
|
||
|
@property(nonatomic, assign) uint32_t update_addr; //ota update base address
|
||
|
|
||
|
|
||
|
@end
|
||
|
|
||
|
@implementation FRITrunk
|
||
|
@end
|
||
|
|
||
|
|
||
|
@interface FRIUpdateOTAManager ()
|
||
|
|
||
|
@property(nonatomic, assign, readwrite) FRIOTAStatus otaStatus; //!< ota操作状态
|
||
|
@property(nonatomic, strong, readwrite) CBPeripheral *curPeripheral; //蓝牙外设
|
||
|
|
||
|
@property(nonatomic, strong) FRITrunk *otaTrunk; //ota trunk
|
||
|
|
||
|
|
||
|
@property(nonatomic, strong) CBCharacteristic *writeCharacteristic; //写特征值
|
||
|
//@property(nonatomic, strong) CBCharacteristic *readCharacteristic; //读特征值
|
||
|
|
||
|
@end
|
||
|
|
||
|
|
||
|
@implementation FRIUpdateOTAManager
|
||
|
|
||
|
- (instancetype)init
|
||
|
{
|
||
|
self = [super init];
|
||
|
if (self) {
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readCharacterNotify:) name:FRQReadCharacteristicUUIDNotify object:nil];
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectSuccessNotify) name:BluetoothNotificationAtConnectSuccess object:nil];
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disconnectNotify) name:BluetoothNotificationAtDisconnect object:nil];
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
static NSThread *shareThread = nil;
|
||
|
#pragma mark -- thread
|
||
|
- (NSThread *)otaThread {
|
||
|
@synchronized (self) {
|
||
|
if (!shareThread) {
|
||
|
shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(onOTAThreadStart) object:nil];
|
||
|
shareThread.name = @"com.frq.ota.thread";
|
||
|
}
|
||
|
}
|
||
|
return shareThread;
|
||
|
}
|
||
|
|
||
|
-(void)onOTAThreadStart{
|
||
|
@autoreleasepool {
|
||
|
|
||
|
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
|
||
|
//保持循环执行
|
||
|
while ((self.otaStatus != FRIOTAStatusCanceled && self.otaStatus != FRIOTAStatusFinish && self.otaStatus !=FRIOTAStatusFailure)
|
||
|
&& [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
|
||
|
|
||
|
shareThread = nil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#pragma mark -- getter and setter
|
||
|
- (FRITrunk *)otaTrunk{
|
||
|
if (!_otaTrunk) {
|
||
|
_otaTrunk = [[FRITrunk alloc] init];
|
||
|
}
|
||
|
return _otaTrunk;
|
||
|
}
|
||
|
|
||
|
-(void)resetOTAStatus{
|
||
|
|
||
|
self.otaStatus = FRIOTAStatusNotStart;
|
||
|
}
|
||
|
|
||
|
//写数据
|
||
|
-(void)_writeDataToPeripheral:(CBPeripheral *)peripheral
|
||
|
characteristic:(CBCharacteristic *)characteristic
|
||
|
value:(NSData *)data{
|
||
|
//开始处理交互数据
|
||
|
if (!self.writeCharacteristic) {
|
||
|
[self postErrorWithCode:FRQErrorCode_connect msg:@"当前未找到蓝牙读写设备!"];
|
||
|
return;
|
||
|
}
|
||
|
FRILogS(@"😀😀😀😀send data<%@> = %@ to <%@>!!",@(data.length),[FRIConvert prexHexStrFromData:data], characteristic.UUID.UUIDString);
|
||
|
|
||
|
//这是一个NS_OPTIONS,就是可以同时用于好几个值,常见的有read,write,notify,indicate,知知道这几个基本就够用了,前连个是读写权限,后两个都是通知,两种不同的通知方式。
|
||
|
/*
|
||
|
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
|
||
|
CBCharacteristicPropertyBroadcast = 0x01,
|
||
|
CBCharacteristicPropertyRead = 0x02,
|
||
|
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
|
||
|
CBCharacteristicPropertyWrite = 0x08,
|
||
|
CBCharacteristicPropertyNotify = 0x10,
|
||
|
CBCharacteristicPropertyIndicate = 0x20,
|
||
|
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
|
||
|
CBCharacteristicPropertyExtendedProperties = 0x80,
|
||
|
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
|
||
|
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
|
||
|
};
|
||
|
|
||
|
*/
|
||
|
//只有 characteristic.properties 有write的权限才可以写
|
||
|
if(characteristic.properties & CBCharacteristicPropertyWrite){
|
||
|
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
|
||
|
}
|
||
|
else if (characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
|
||
|
|
||
|
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
|
||
|
}
|
||
|
else{
|
||
|
FRILogS(@"该字段不可写!");
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//分包发送蓝牙数据
|
||
|
-(void)sendMsgWithSubPackage:(NSData*)msgData
|
||
|
Peripheral:(CBPeripheral*)peripheral
|
||
|
Characteristic:(CBCharacteristic*)character
|
||
|
{
|
||
|
|
||
|
for (int i = 0; i < msgData.length; i += BLE_SEND_MAX_LEN) {
|
||
|
|
||
|
NSData *subData = nil;
|
||
|
// 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小
|
||
|
if ((i + BLE_SEND_MAX_LEN) < [msgData length]) {
|
||
|
subData = [msgData subdataWithRange:NSMakeRange(i, BLE_SEND_MAX_LEN)];
|
||
|
}
|
||
|
else {
|
||
|
subData = [msgData subdataWithRange:NSMakeRange(i, [msgData length] - i)];
|
||
|
}
|
||
|
|
||
|
[self _writeDataToPeripheral:peripheral
|
||
|
characteristic:character
|
||
|
value:subData];
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark -- build head cmd
|
||
|
-(NSData *)buildCMDHeaderDataWithOpcode:(NSString *)opcodeHex len:(NSString *)lenHex {
|
||
|
|
||
|
NSMutableData *headData = [NSMutableData data];
|
||
|
NSData *opcodeData = [FRIConvert prexHexStrToData:opcodeHex];
|
||
|
[headData appendBytes:opcodeData.bytes length:1];
|
||
|
|
||
|
//大小端数据转化
|
||
|
NSString *bigEndianStr = [FRIConvert convertHexEndianToEndian:lenHex];
|
||
|
NSData *bigEndianData = [FRIConvert prexHexStrToData:bigEndianStr];
|
||
|
[headData appendBytes:bigEndianData.bytes length:2];
|
||
|
|
||
|
return headData;
|
||
|
}
|
||
|
|
||
|
-(NSData *)buildCmdDataForBaseAddress{
|
||
|
|
||
|
NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x01" len:@"0x0000"];
|
||
|
|
||
|
//build
|
||
|
NSMutableData *cmdData = [NSMutableData dataWithLength:kFRQCmdHeadLen + 6];
|
||
|
[cmdData replaceBytesInRange:NSMakeRange(0, kFRQCmdHeadLen) withBytes:cmdHead.bytes];//replace 3 bytes
|
||
|
|
||
|
return cmdData;
|
||
|
}
|
||
|
|
||
|
-(NSData *)buildCmdDataForErase{
|
||
|
|
||
|
NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x03" len:@"0x0004"];
|
||
|
|
||
|
NSMutableData *cmdData = [NSMutableData dataWithLength:kFRQCmdHeadLen + 4];
|
||
|
[cmdData replaceBytesInRange:NSMakeRange(0, kFRQCmdHeadLen) withBytes:cmdHead.bytes];//replace 3 bytes
|
||
|
|
||
|
//base address
|
||
|
|
||
|
NSString *hex = [FRIConvert noPrexHexStrFromDecimal:self.otaTrunk.erase_base_addr];
|
||
|
NSString *newHexStr = [FRIConvert addString:@"0" length:4*2 onString:hex]; //补位
|
||
|
|
||
|
//数据大小端转化
|
||
|
NSString *bigEndianStr = [FRIConvert convertHexEndianToEndian:newHexStr];
|
||
|
NSData *baseAddrData = [FRIConvert prexHexStrToData:bigEndianStr];
|
||
|
|
||
|
[cmdData replaceBytesInRange:NSMakeRange(kFRQCmdHeadLen, 4) withBytes:baseAddrData.bytes];
|
||
|
|
||
|
return cmdData;
|
||
|
}
|
||
|
|
||
|
|
||
|
-(NSData *)buildCmdUpdateData:(NSData *)subPackage fromAddr:(NSString *)addressHex{
|
||
|
|
||
|
NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x05" len:@"0x0241"];
|
||
|
NSMutableData *cmdData = [NSMutableData data];
|
||
|
[cmdData appendData:cmdHead]; //add head
|
||
|
|
||
|
//base addr
|
||
|
NSData *baseAddrData = [FRIConvert prexHexStrToData:addressHex];
|
||
|
[cmdData appendBytes:baseAddrData.bytes length:kFRQUpdateAddressLen]; // 4 bytes
|
||
|
|
||
|
//content len
|
||
|
NSString *lenHexStr = [FRIConvert noPrexHexStrFromDecimal:235];
|
||
|
NSString *newHexStr = [FRIConvert addString:@"0" length:2*2 onString:lenHexStr]; //补位
|
||
|
NSString *bigEndianLenStr = [FRIConvert convertHexEndianToEndian:newHexStr];
|
||
|
NSData *contentLenData = [FRIConvert prexHexStrToData:bigEndianLenStr];
|
||
|
[cmdData appendBytes:contentLenData.bytes length:2]; //2 bytes
|
||
|
|
||
|
//content
|
||
|
[cmdData appendData:subPackage]; //package data
|
||
|
|
||
|
return cmdData;
|
||
|
}
|
||
|
|
||
|
-(NSData *)buildRebootCmdData{
|
||
|
|
||
|
// NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x09" len:@"0x0000"];
|
||
|
|
||
|
//此处需要特殊处理
|
||
|
uint32_t crc32 = [FRIBinFileCheck crc32ForBinFile:self.binData];
|
||
|
uint32_t length = (uint32_t)self.binData.length;
|
||
|
|
||
|
Byte rebootCmd[11];
|
||
|
rebootCmd[0] = (Byte) (9 & 0xff);
|
||
|
rebootCmd[1] = 0xa;
|
||
|
rebootCmd[2] = 0x00;
|
||
|
rebootCmd[3] = (Byte) (length & 0xff);
|
||
|
rebootCmd[4] = (Byte) ((length & 0xff00) >> 8);
|
||
|
rebootCmd[5] = (Byte) ((length & 0xff0000) >> 16);
|
||
|
rebootCmd[6] = (Byte) ((length & 0xff000000) >> 24);
|
||
|
rebootCmd[7] = (Byte) (crc32 & 0xff);
|
||
|
rebootCmd[8] = (Byte) ((crc32 & 0xff00) >> 8);
|
||
|
rebootCmd[9] = (Byte) ((crc32 & 0xff0000) >> 16);
|
||
|
rebootCmd[10] = (Byte) ((crc32 & 0xff000000) >> 24);
|
||
|
|
||
|
// [cmdData replaceBytesInRange:NSMakeRange(0, kFRQCmdHeadLen) withBytes:cmdHead.bytes]; //replace 3 bytes
|
||
|
return [NSData dataWithBytes:rebootCmd length:11];
|
||
|
}
|
||
|
|
||
|
#pragma mark --check bin data
|
||
|
|
||
|
#define FRICHECKHEXSTR @"0x0167"
|
||
|
#define FRIFlAGHEXSTR @"0x51525251"
|
||
|
|
||
|
-(BOOL)checkBinValidate:(NSData *)binData{
|
||
|
|
||
|
//data empty
|
||
|
if (binData.length <= 0) {
|
||
|
return NO;
|
||
|
}
|
||
|
|
||
|
int flagByteSize = 4;
|
||
|
|
||
|
//convert 16 to 10
|
||
|
uint32_t checkByteIndex = 0;
|
||
|
NSScanner* scanner = [NSScanner scannerWithString:FRICHECKHEXSTR];
|
||
|
[scanner scanHexInt:&checkByteIndex];
|
||
|
|
||
|
if (binData.length > checkByteIndex + flagByteSize) {
|
||
|
//read last 4 Bytes data
|
||
|
NSData *flagData = [binData subdataWithRange:NSMakeRange(checkByteIndex, flagByteSize)];
|
||
|
NSString *flagHexStr = [FRIConvert prexHexStrFromData:flagData];
|
||
|
if ([flagHexStr isEqualToString:FRIFlAGHEXSTR]) { //check flag byte
|
||
|
//bin is validate
|
||
|
return YES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NO;
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark -- handle response data
|
||
|
- (void)_sendOTARebootCmd {
|
||
|
|
||
|
NSData *rebootData = [self buildRebootCmdData];
|
||
|
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:rebootData];
|
||
|
|
||
|
}
|
||
|
|
||
|
- (void)_sendOTAFileDataCmd {
|
||
|
|
||
|
NSData *packageData = nil;
|
||
|
// 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小
|
||
|
int fromDataOffset = self.otaTrunk.update_addr - self.otaTrunk.base_addr;
|
||
|
if (self.otaTrunk.update_addr+OTA_SUBPACKAGE_LEN < self.otaTrunk.base_addr + self.binData.length) {
|
||
|
packageData = [self.binData subdataWithRange:NSMakeRange(fromDataOffset, OTA_SUBPACKAGE_LEN)];
|
||
|
}
|
||
|
else {
|
||
|
packageData = [self.binData subdataWithRange:NSMakeRange(fromDataOffset, self.otaTrunk.base_addr + self.binData.length - self.otaTrunk.update_addr)];
|
||
|
}
|
||
|
|
||
|
|
||
|
NSString *baseAddrHexStr = [FRIConvert noPrexHexStrFromDecimal:self.otaTrunk.update_addr];
|
||
|
NSString *newHexStr = [FRIConvert addString:@"0" length:kFRQUpdateAddressLen*2 onString:baseAddrHexStr]; //补位
|
||
|
//数据大小端转化
|
||
|
NSString *bigEndianStr = [FRIConvert convertHexEndianToEndian:newHexStr];
|
||
|
|
||
|
NSData *otaPackageData = [self buildCmdUpdateData:packageData fromAddr:bigEndianStr];
|
||
|
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:otaPackageData];
|
||
|
|
||
|
self.otaTrunk.update_addr += OTA_SUBPACKAGE_LEN;
|
||
|
|
||
|
}
|
||
|
|
||
|
- (void)_sendOTAEraseCmd {
|
||
|
|
||
|
NSData *eraseCmd = [self buildCmdDataForErase];
|
||
|
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:eraseCmd];
|
||
|
|
||
|
self.otaTrunk.erase_base_addr += ERASE_PAGE_SIZE;
|
||
|
|
||
|
}
|
||
|
|
||
|
- (void)_sendBaseAddrCmd {
|
||
|
|
||
|
NSData *baseAddrCmd = [self buildCmdDataForBaseAddress];
|
||
|
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:baseAddrCmd];
|
||
|
}
|
||
|
|
||
|
|
||
|
/// 接受到OTA上报数据通知
|
||
|
- (void)readCharacterNotify:(NSNotification *)notify {
|
||
|
NSData *data = notify.object;
|
||
|
[self performSelector:@selector(_handleCharacteristicResponseData:) onThread:[self otaThread] withObject:data waitUntilDone:NO];
|
||
|
}
|
||
|
|
||
|
/// 连接成功
|
||
|
- (void)connectSuccessNotify {
|
||
|
if (self.otaStatus != FRIOTAStatusReboot) {
|
||
|
return;
|
||
|
}
|
||
|
FRILogS(@"[FRQOTA] connectSuccessNotify");
|
||
|
__weak typeof(self) weakSelf = self;
|
||
|
[GCDTimer.ocShareInstance scheduledDispatchTimerWithTimerName:FRIConnectSuccessNotify timeInterval:2.0 queue:dispatch_get_main_queue() repeats:false immediately:false action:^{
|
||
|
if (weakSelf.otaStatus != FRIOTAStatusReboot) {
|
||
|
return;
|
||
|
}
|
||
|
// 判断版本号,确认是否升级完成
|
||
|
NSString *currenVersion = [GlobalDeviceProfileModel.ocShareInstance currenVersion];
|
||
|
FRILogS(@"[FRQOTA] connectSuccessNotify currenVersion:%@ updateVersion:%@",currenVersion,self.updateVersion);
|
||
|
if ([currenVersion isEqualToString:self.updateVersion]) {
|
||
|
//completion
|
||
|
weakSelf.otaStatus = FRIOTAStatusFinish;
|
||
|
[weakSelf postOTAUpdateProgressChanged:100.0];
|
||
|
FRILogS(@"[FRQOTA] connectSuccessNotify 🏆🏆 重启完成 !!");
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
if ([weakSelf.delegate respondsToSelector:@selector(onOTAUpdateStatusCompletion:)]) {
|
||
|
[weakSelf.delegate onOTAUpdateStatusCompletion:weakSelf];
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
/// 断开连接
|
||
|
- (void)disconnectNotify {
|
||
|
if (self.otaStatus == FRIOTAStatusNotStart ||
|
||
|
self.otaStatus == FRIOTAStatusGetBaseAddr ||
|
||
|
self.otaStatus == FRIOTAStatusEraseOut ||
|
||
|
self.otaStatus == FRIOTAStatusFileTransform ||
|
||
|
self.otaStatus == FRIOTAStatusNotStart ||
|
||
|
self.otaStatus == FRIOTAStatusCanceled) {
|
||
|
[self postErrorWithCode:FRQErrorCode_connect msg:@"升级过程断开连接"];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-(void)_handleCharacteristicResponseData:(NSData *)rspData{
|
||
|
|
||
|
FRILogS(@"[FRQOTA] >>> handle rsp hexString = %@ in thread: %@!!!",[FRIConvert prexHexStrFromData:rspData],[NSThread currentThread].name);
|
||
|
|
||
|
//base address data
|
||
|
if (rspData.length >= kFRQRspHeadLen + kFRQBaseAddressResultLen && (self.otaStatus == FRIOTAStatusGetBaseAddr || self.otaStatus == FRIOTAStatusEraseOut || self.otaStatus == FRIOTAStatusFileTransform || self.otaStatus == FRIOTAStatusReboot)) {
|
||
|
self.otaTrunk.rsp_data = [rspData subdataWithRange:NSMakeRange(kFRQRspHeadLen, kFRQBaseAddressResultLen)];
|
||
|
|
||
|
NSData *newRsp = [rspData subdataWithRange:NSMakeRange(0, 1)];
|
||
|
FRQRspHeadCode resultCode = [FRIConvert decimalFromData:newRsp].integerValue;
|
||
|
if (resultCode == FRQRspHeadCodeFail) { //success
|
||
|
FRILogS(@"[FRQOTA] !!!!!!!!! %@ -> %@ !!!!!!",newRsp,[FRIConvert decimalFromData:newRsp]);
|
||
|
|
||
|
[self postErrorWithCode:FRQErrorCode_ota msg:@"response data code failed"];
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
self.otaTrunk.rsp_data = nil;
|
||
|
}
|
||
|
|
||
|
switch (self.otaStatus) {
|
||
|
case FRIOTAStatusNotStart:
|
||
|
{
|
||
|
[self postOTAUpdateProgressChanged:5.0];
|
||
|
|
||
|
FRILogS(@"[FRQOTA] 🏆🏆 开始获取基地址操作 !!");
|
||
|
[self _sendBaseAddrCmd];
|
||
|
self.otaStatus = FRIOTAStatusGetBaseAddr;
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
case FRIOTAStatusGetBaseAddr://step 1 get baseAddr
|
||
|
{
|
||
|
|
||
|
//基地址进行大端转小端
|
||
|
NSString *bigEndianHex = [FRIConvert noPrexHexStrFromData:self.otaTrunk.rsp_data];
|
||
|
NSString *fillEndianHexStr = [FRIConvert addString:@"0" length:kFRQBaseAddressResultLen*2 onString:bigEndianHex];
|
||
|
NSString *littleEndianHex = [FRIConvert convertHexEndianToEndian:fillEndianHexStr];
|
||
|
|
||
|
self.otaTrunk.base_addr = (uint32_t)[FRIConvert decimalFromHexStr:littleEndianHex].integerValue;
|
||
|
self.otaTrunk.erase_base_addr = self.otaTrunk.base_addr;
|
||
|
self.otaTrunk.update_addr = self.otaTrunk.base_addr;
|
||
|
|
||
|
|
||
|
[self postOTAUpdateProgressChanged:10.0];
|
||
|
|
||
|
FRILogS(@"[FRQOTA] 🥇 已获取基地址: %@",littleEndianHex);
|
||
|
|
||
|
self.otaStatus = FRIOTAStatusEraseOut;
|
||
|
|
||
|
//send erase cmd
|
||
|
FRILogS(@"[FRQOTA] 🏆🏆 开始擦除操作 !!");
|
||
|
|
||
|
[self _sendOTAEraseCmd];
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
case FRIOTAStatusEraseOut: //step 2 erase data
|
||
|
{
|
||
|
if(self.otaTrunk.erase_base_addr < self.otaTrunk.base_addr + self.binData.length) {
|
||
|
|
||
|
float eraseProgress = (float)self.otaTrunk.erase_base_addr/(self.otaTrunk.base_addr + self.binData.length);
|
||
|
[self postOTAUpdateProgressChanged:eraseProgress * 20.0 + 10.0];
|
||
|
FRILogS(@"🥈 开始擦除地址: %@, 进度 %.2f",[FRIConvert noPrexHexStrFromDecimal:self.otaTrunk.erase_base_addr],eraseProgress);
|
||
|
|
||
|
[self _sendOTAEraseCmd]; //每次擦除后增加4096个偏移
|
||
|
|
||
|
}
|
||
|
else{
|
||
|
|
||
|
//发送文件传输操作
|
||
|
self.otaStatus =FRIOTAStatusFileTransform;
|
||
|
|
||
|
FRILogS(@"[FRQOTA] 🏆🏆 开始传输文件操作 !!");
|
||
|
[self _sendOTAFileDataCmd];
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case FRIOTAStatusFileTransform://step 3 transform file data
|
||
|
{
|
||
|
//分块传输
|
||
|
if (self.otaTrunk.update_addr < self.otaTrunk.base_addr + self.binData.length) {
|
||
|
|
||
|
|
||
|
FRILogS(@"[FRQOTA] 🥉 已传输文件传输大小 %d byte, bin文件总大小 %ld ",self.otaTrunk.update_addr - self.otaTrunk.base_addr,self.binData.length);
|
||
|
float transformProgress = (float)(self.otaTrunk.update_addr - self.otaTrunk.base_addr)/self.binData.length;
|
||
|
[self postOTAUpdateProgressChanged:transformProgress * 60.0 + 30.0];
|
||
|
|
||
|
[self _sendOTAFileDataCmd];
|
||
|
|
||
|
}
|
||
|
else{
|
||
|
self.otaStatus = FRIOTAStatusReboot;
|
||
|
|
||
|
FRILogS(@"[FRQOTA] 🏆🏆 开始重启操作 !!");
|
||
|
[self postOTAUpdateProgressChanged:100.0];
|
||
|
|
||
|
//send reboot cmd
|
||
|
[self _sendOTARebootCmd];
|
||
|
|
||
|
// 升级成功后立即重连会出现断开,因此重连延时
|
||
![]()
1 year ago
|
BluetoothFireBoltt.shareInstance.reconnectTime = 3;
|
||
![]()
2 years ago
|
|
||
|
// 容错处理,超过15s后未重连判定为升级成功
|
||
|
__weak typeof(self) weakSelf = self;
|
||
|
[GCDTimer.ocShareInstance scheduledDispatchTimerWithTimerName:FRIHandlerTimeOutSuccess timeInterval:15.0 queue:dispatch_get_main_queue() repeats:false immediately:false action:^{
|
||
|
if (weakSelf.otaStatus != FRIOTAStatusReboot) {
|
||
|
return;
|
||
|
}
|
||
|
FRILogS(@"[FRQOTA] FRIHandlerTimeOutSuccess 超时未重连判断升级成功");
|
||
|
weakSelf.otaStatus = FRIOTAStatusFinish;
|
||
|
[weakSelf postOTAUpdateProgressChanged:100.0];
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
if ([weakSelf.delegate respondsToSelector:@selector(onOTAUpdateStatusCompletion:)]) {
|
||
|
[weakSelf.delegate onOTAUpdateStatusCompletion:weakSelf];
|
||
|
}
|
||
|
});
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
case FRIOTAStatusReboot: //step 4 reboot
|
||
|
{
|
||
|
//completion
|
||
|
self.otaStatus = FRIOTAStatusFinish;
|
||
|
|
||
|
[self postOTAUpdateProgressChanged:100.0];
|
||
|
|
||
|
FRILogS(@"[FRQOTA] 🏆🏆 重启完成 !!");
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStatusCompletion:)]) {
|
||
|
[self.delegate onOTAUpdateStatusCompletion:self];
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
#pragma mark --- 错误处理
|
||
|
-(void)postErrorWithCode:(FRQErrorCode)errCode msg:(NSString *)errMsg{
|
||
|
FRILogS(@"[FRQOTA] postErrorWithCode errCode:%ld errMsg:%@",errCode,errMsg);
|
||
|
self.otaStatus = FRIOTAStatusFailure;
|
||
|
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStatusFailure:error:)]) {
|
||
|
[self.delegate onOTAUpdateStatusFailure:self error:[self errorWithCode:errCode msg:errMsg]];
|
||
|
}
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
-(NSError *)errorWithCode:(NSInteger)code msg:(NSString *)errMsg{
|
||
|
|
||
|
NSString *localDescriptionStr = [NSString stringWithFormat:@"Code: %@, %@",@(code),errMsg];
|
||
|
return [NSError errorWithDomain:errMsg code:code userInfo:@{NSLocalizedFailureReasonErrorKey:errMsg,NSLocalizedDescriptionKey:localDescriptionStr}];
|
||
|
}
|
||
|
|
||
|
-(void)postOTAUpdateProgressChanged:(float)progress{
|
||
|
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStatusDidChange:withProgress:)]) {
|
||
|
[self.delegate onOTAUpdateStatusDidChange:self withProgress:progress];
|
||
|
}
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark -- public
|
||
|
-(void)startUpdateOTA:(CBPeripheral *)peripheral writeCharacteristic:(CBCharacteristic *)writeCharacteristic {
|
||
|
|
||
|
self.writeCharacteristic = writeCharacteristic;
|
||
|
self.otaStatus = FRIOTAStatusNotStart;
|
||
|
self.curPeripheral = peripheral;
|
||
|
|
||
|
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStart:)]) {
|
||
|
[self.delegate onOTAUpdateStart:self];
|
||
|
}
|
||
|
//启动线程
|
||
|
if (![self otaThread].isExecuting) {
|
||
|
[[self otaThread] start];
|
||
|
}
|
||
|
//获取详情
|
||
|
[self performSelector:@selector(_handleCharacteristicResponseData:) onThread:[self otaThread] withObject:nil waitUntilDone:NO];
|
||
|
}
|
||
|
|
||
|
-(void)cancelOTAUpdate{
|
||
|
|
||
|
if (self.otaStatus != FRIOTAStatusNotStart
|
||
|
&& self.otaStatus != FRIOTAStatusFailure
|
||
|
&& self.otaStatus != FRIOTAStatusCanceled
|
||
|
&& self.otaStatus != FRIOTAStatusFinish) {
|
||
|
self.otaStatus = FRIOTAStatusCanceled; //状态设置后otaThread线程退出
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
@end
|