程序开发

nRF芯片设备DFU升级

2017-08-03  本文已影响882人  iLeooooo

前言

nRF-Toolbox项目包含BGM,HRM,HTM,DFU等多个模块,我们只用到了其中的DFU升级模块。打开项目,在对应的NORDFUViewController.swift中我们能够看到有三个引用库:
import UIKit,import CoreBluetooth,import iOSDFULibrary,这里的iOSDFULibrary就是DFU升级的库,也是解决DFU升级最重要的组件。我们只要把这个库集成到我们的项目中,就能够完成nRF设备的DFU升级了。

下面是对于OC引用DFU升级的操作步骤和我遇到的问题(使用的是蓝牙连接升级)。

集成iOSDFULibrary

我这里使用的是直接把库导入到项目里面。(最好还是用cocoaPod导入,不过好像会出现很多问题,我这里还没试过)

编译出framework然后把库导入项目
编译iOSDFULibrary.png

最后生成两个framework:

编译完成后,这两个文件在项目下面:pod-->products文件夹,右键在find里就可找到,直接放到你项目里面去就OK了。

导入到项目里面去了之后,更改一下项目配置:

在设置里面导入库文件.png Swift标准.png

把RunPath这里设置加一项:@executable_path/Frameworks


runpath.png

这时候在项目里面用头文件就可以使用库了:

#import <iOSDFULibrary/iOSDFULibrary-Swift.h>

运行程序,如果报错如下:

dyld: Library not loaded: @rpath/libswiftCore.dylib
Referenced from: /private/var/containers/Bundle/Application/02516D79-BB30-4278-81B8-3F86BF2AE2A7/XingtelBLE.app/Frameworks/iOSDFULibrary.framework/iOSDFULibrary
Reason: image not found 

[dyld: Library not loaded: @rpath/libswiftCore.dylib报错解决]

embed
这个默认设置是NO,设置为YES就可以了!!!

还有一种错误就是运行的时候,一直崩在这个地方

//这里一直崩溃,Message from debugger: Terminated due to signal 9
//如果出现这种错误,你用他们demo跑的时候,有可能也会出现这个问题
//这里报错就是编译的库有问题,重新换库,使用carthage可以打包的库可以解决这个问题
DFUFirmware *selectedFirmware = [[DFUFirmware alloc] initWithUrlToZipFile:url];

那一般情况是你编译的包有问题。下面提供2种重新打包的方法,可以尝试一下:

1、On your mac please install carthage (instructions)
2、Create a file named cartfile anywhere on your computer
3、add the following content to the file:

github "NordicSemiconductor/IOS-Pods-DFU-Library" ~> 2.1.2
github "marmelroy/Zip" ~> 0.6

1、Open a new terminal and cd to the directory where the file is
2、Enter the command carthage update --platform iOS
3、Carthage will now take care of building your frameworks, the produced .framework files will be found in a newly created directory called Carthage/Build/iOS,copy over iOSDFULibrary.framework and Zip.framework to your project and you are good to go.

注意

a. carthage是一种和cocoapods相似的的类库管理工具,如果不会使用的话可以参照carthage的使用,将framework文件导入到自己的项目。

b. 用这个方法导出的库,在你的电脑上跑是正确的,在你同事的电脑上跑可能就不行了~~~

调整打包设置.png

附加一些另外的解决方法:

打包上架时报ERROR IT MS-90087等问题

ERROR ITMS-90087: "Unsupported Architectures. The executable for ***.app/Frameworks/SDK.framework contains unsupported architectures '[x86_64, i386]'."
ERROR ITMS-90362: "Invalid Info.plist value. The value for the key 'MinimumOSVersion' in bundle ***.app/Frameworks/SDK.framework is invalid. The minimum value is 8.0"
ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at '***.app/Frameworks/SDK.framework/SDK' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
ERROR ITMS-90125: "The binary is invalid. The encryption info in the LC_ENCRYPTION_INFO load command is either missing or invalid, or the binary is already encrypted. This binary does not seem to have been built with Apple's linker."

解决方法,添加Run Script Phase


上架报错.png

Shell脚本内容填写如下内容,再次编译即可

 APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"

# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
    FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"

EXTRACTED_ARCHS=()

for ARCH in $ARCHS
do
echo "Extracting $ARCH from   $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done

echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"

echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged"   "$FRAMEWORK_EXECUTABLE_PATH"

done
上面的是集成IOSDFULibrary时遇到的一些问题,解决这些问题后就可以正常进行固件升级了。

下面了解一下使用这个库来进行固件升级。

步骤:

下面是对IOSDFULibrary的使用:

导入三个delegate:LoggerDelegateDFUServiceDelegateDFUProgressDelegate,它们的作用分别为打印状态日志,DFU升级及蓝牙连接状态,,监视DFU升级进度。

//DFU
@property (strong, nonatomic) DFUServiceController *dfuService;
@property (strong, nonatomic) DFUFirmware *selectedFirmware;


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions     {
    // Override point for customization after application launch.

    //在AppDelegate里面要设置这些默认的属性,否则文件传输会失败,直接报operation not  permitted或者Sending firwmare failed
    NSDictionary* defaults = [NSDictionary dictionaryWithObjects:@[@"2.3", [NSNumber numberWithInt:12], @NO] forKeys:@[@"key_diameter", @"dfu_number_of_packets", @"dfu_force_dfu"]];
    [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];

    return YES;
}

- (void)performDFU
{
    // To start the DFU operation the DFUServiceInitiator must be used
    DFUServiceInitiator *initiator = [[DFUServiceInitiator alloc] initWithCentralManager: self.manager target:_val_peripheral];
    //注意这里要在AppDelegate里面设置
    initiator.forceDfu = [[[NSUserDefaults standardUserDefaults] valueForKey:@"dfu_force_dfu"] boolValue];
    //注意这里要在AppDelegate里面设置
    initiator.packetReceiptNotificationParameter = [[[NSUserDefaults standardUserDefaults] valueForKey:@"dfu_number_of_packets"] intValue];
    initiator.enableUnsafeExperimentalButtonlessServiceInSecureDfu = YES;
    initiator.logger = self;
    initiator.delegate = self;
    initiator.progressDelegate = self;
    //下载网络文件升级
    NSString *firmwaresPath = [[verManager getFirmwaresPath] stringByAppendingPathComponent:@"firmWareVersion.zip"];
    NSURL *url = [NSURL fileURLWithPath:firmwaresPath];
    _selectedFirmware = [[DFUFirmware alloc] initWithUrlToZipFile:url];
    //本地zip文件升级
    //NSURL *filePath = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"app_only.zip" ofType:nil]];
    //_selectedFirmware = [[DFUFirmware alloc] initWithUrlToZipFile:filePath];
    //开始发送文件  如果这里没有文件会报错:Firmare  not  specified
    _dfuService = [[initiator withFirmware:_selectedFirmware] start];     
}


#pragma mark - LoggerDelegate
-(void)logWith:(enum LogLevel)level message:(NSString *)message
{
    NSLog(@"%ld: %@", (long) level, message);
}


#pragma mark - DFUServiceDelegate
//监听状态
- (void)dfuStateDidChangeTo:(enum DFUState)state
{
    switch (state) {
        case DFUStateConnecting:
             NSLog(@"Connecting...");
             break;
        case DFUStateStarting:
             NSLog(@"Starting...");
             break;
        case DFUStateEnablingDfuMode:
             NSLog(@"EnablingDfuMode...");
             break;
        case DFUStateUploading:
             NSLog(@"Uploading...");
             break;
        case DFUStateValidating:
             NSLog(@"Validating...");
             break;
        case DFUStateDisconnecting:
             NSLog(@"Disconnecting...");
             break;
        case DFUStateCompleted:
        {
            //升级成功   Upload complete
            //重新连接蓝牙
            self.manager = nil;
            //初始化并设置委托和线程队列
            //重新给蓝牙连接Manager赋值,不然有可能升级后连接不上蓝牙
            self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:@{CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}];
            break;
        }
        case DFUStateAborted:
            NSLog(@"Aborted...");
            break;
        default:
            break;
    }
}

- (void)dfuError:(enum DFUError)error didOccurWithMessage:(NSString * _Nonnull)message
{
    NSLog(@"Error %ld: %@", (long) error, message);
}



#pragma mark - DFUProgressDelegate
//进度
- (void)dfuProgressDidChangeFor:(NSInteger)part outOf:(NSInteger)totalParts to:(NSInteger)progresscurrentSpeedBytesPerSecond:(double)currentSpeedBytesPerSecond avgSpeedBytesPerSecond:(double)avgSpeedBytesPerSecond;
{
    [SVProgressHUD showProgress:(float) progress / 100.0f status:[NSString stringWithFormat:@"升级固件中%ld%%",(long)progress]];
//    progress.progress = (float) percentage / 100.0f;
//    progressLabel.text = [NSString stringWithFormat:@"%ld%% (%ld/%ld)", (long) percentage, (long) part, (long) totalParts];
}

补充:

后面在测试的时候发现:第一次运行程序,升级的时候会出现崩溃。崩溃的位置:

第一次运行升级崩溃在库里.png

解决方案:
崩溃的原因是firmwareRanges是nil,在前面添加代码进行判断就行了:

if firmwareRanges == nil {
    // Split firmware into smaller object of at most maxLen bytes, if firmware is bigger than maxLen
    return
}
解决崩溃.png

注意

改代码的时候,不能直接改项目里面崩溃的那个位置的代码,那里改了没用,要改在编译成库的地方的代码,然后重新编译,导出库文件,把库文件重新加到项目里面。如果用的是carthage,使用流程如下:


打开项目 导出库文件流程.png

重新导入包之后要更新一下项目配置,在Build Phases里面的Embed Frameworks里面把IOSDFULibrary库加进了。这里不需要重新更换Zip库,只用更改IOSDFULibrary库就OK~

注意:有朋友反映升级的时候会断开连接,解决方法:
在APPDelegate文件里加上:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions     {
    // Override point for customization after application launch.

    //在AppDelegate里面要设置这些默认的属性,否则文件传输会失败,直接报operation not  permitted或者Sending firwmare failed
    NSDictionary* defaults = [NSDictionary dictionaryWithObjects:@[@"2.3", [NSNumber numberWithInt:12], @NO] forKeys:@[@"key_diameter", @"dfu_number_of_packets", @"dfu_force_dfu"]];
    [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];

    return YES;
}

谢谢~

慢慢来,一步一个巴掌印。。。。。

上一篇 下一篇

猜你喜欢

热点阅读