Firmware Upgrade "Image not valid!" only with Android (iOS version working)

Hello,

My colleague has posted this issue in Github (see https://github.com/NordicSemiconductor/Android-nRF-Connect-Device-Manager/issues/79) , but also want to post it here:

We use this library (1.4.1) in our mobile app to upgrade our BLE Device firmware (DFU, Zephyr, McuBoot). The iOS version of this library works as expected, but we have issues making this upgrade process work with the Android version. The device image used for both cases is the same.

This issue is also in the nRF Connect application (4.26.0) for Android. nRF Connect app for iOS works (2.5.3).

I have included below the code we use for iOS and Android and the messages from the BLE device during the upgrade process with the Android version. 

Could you please help? Thanks!

BLE Device Output

BLE Deviceuart:~$ *** Booting Zephyr OS version 2.3.99  ***
[00:00:00.003,601] <inf> mcuboot: Starting bootloader
[00:00:00.009,704] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[00:00:00.019,561] <inf> mcuboot: Boot source: none
[00:00:00.024,932] <inf> mcuboot: Swap type: perm
[00:00:08.675,231] <err> mcuboot: Image in the secondary slot is not valid!
[00:00:09.051,116] <inf> mcuboot: Bootloader chainload address offset: 0xe000
[00:00:09.058,654] <inf> mcuboot: Jumping to the first image slot

iOS Code (Swift)

private func startDfu(_ address: String, binary: FlutterStandardTypedData, result: @escaping FlutterResult) {
    guard let uuid = UUID(uuidString: address) else {
        result(FlutterError(code: "DEVICE_ADDRESS_ERROR", message: "Device address conversion failed.", details: "Device address \(address) conversion failed"))
        return
    }

    guard let imageData = getImageData(binary: binary) else {
        result(FlutterError(code: "FIRMWARE_NOT_FOUND", message: "Binary file not valid.", details: nil))
        return
    }

    let bleTransport = McuMgrBleTransport(uuid)
    let dfuManager = FirmwareUpgradeManager(transporter: bleTransport, delegate: self)
    dfuManager.logDelegate = UIApplication.shared.delegate as? McuMgrLogDelegate
    dfuManager.estimatedSwapTime = 20.0

    do {
        try dfuManager.start(data: imageData)
    } catch {
        print("Error reading hash: \(error)")
    }
}

Android Code (Kotlin)

fun startDfu(binary: ByteArray): Unit {
    getBleDevice(address) { device ->
        val transport: McuMgrTransport = McuMgrBleTransport(context, device);
        val dfuManager = FirmwareUpgradeManager(transport, this)

        dfuManager.setEstimatedSwapTime(20000)
        dfuManager.setMode(FirmwareUpgradeManager.Mode.CONFIRM_ONLY)

        val eraseStorage = false
        val imageData = getImageData(binary)

        dfuManager.start(imageData, eraseStorage)
    }
}


Parents
  • Hi , 

    The documents Flash_Android_nRF_App and Flash_Iphone_nRF_App contain partial flash readings from our BLE device after performing the upgrade from both Android and iOS versions. If you compare these two documents, you will see a pattern that repeats every 468 bytes: 459 are correct, the same in both versions, and the last 9 differ. We've also seen that the MTU is set to 498 in the library and cannot be set lower (see code below)

    McuMgrBleTransport.java

    @Override
    protected void initialize() {
        mSmpProtocol = new SmpProtocolSession(mHandler);
    
        // Request as high MTU as possible. As SMP protocol is fairly slow, requires a
        // notification for each packet sent, make sure the packets are as big as possible.
        // If Data Length Extension (DLE) is enabled, a single Link Layer packet can contain 251
        // bytes, which give 247 bytes for the payload. MTU equal to 498 allows sending max
        // data that fits into two LL packets of maximum size.
        // Maximum supported MTU is 517, but that would mean the third packet is small.
        // If the packet could not be sent in the same connection interval as 2 big packets,
        // that would waste the whole connection interval for just around 20 bytes.
        requestMtu(498)
                .with((device, mtu) -> mMaxPacketLength = Math.max(mtu - 3, mMaxPacketLength))
                .fail((device, status) -> {
                    if (getMinLogPriority() <= Log.WARN) {
                        log(Log.WARN, "Failed to negotiate MTU, disconnecting...");
                    }
                    disconnect().enqueue();
                }).enqueue();
                
                ...


    Could you please take a look and let us know how we can help you? This is becoming a pressing issue for us. We are open to an online debug session with your team to troubleshoot this if needed.

    Many thanks in advance,
    Juan Villanueva.

  • Hello,

    Regarding getting the hash of a firmware:

    1. Try nRF Connect Device Manager apps for Android and iOS. Switch to Image tab and click ADVANCED in the top.

    2. When you select a firmware in Firmware Upload section you'll see the hash and size

     - Compare it on Android and iOS version. 

     - Compare also the sizes in bytes.

    All values should be the same, as you're sending the same file.

    3. Upload and select Application Core as target.

    4. After upload, on the Images section click Read and compare the hash in the slot 1. Both to the previous hash and between Android and iOS.

    5. Select Confirm - this should set the "permanent" flag for the fw in slot 1.

    6. Click Send Reset Command button to reset.

    If all the hashes were the same (that is you were using the same file), the device should restart and boot the same way on Android and iOS. One thing to note is that the hash is taken from the header of the bin file, not calculated from it, so also when there are changes in the files, but headers are equal, the displayed hashes will be the same.

    I can't think of any reason why Android would send differently than iOS. Let's first exclude the differences in files, then we can compare logs from apps.

  • Hi Aleksander,

    We have tried this new version with a lower and valid MTU and it, indeed, works. Will this fix also be available in the Device Manager App?

    Many thanks for your time and support.

  • Hello,

    No, I'm not planning it. It is the device that should dictate the highest supported MTU, not the client.The MTU = 498 allows to reach higher upload speed for devices that support it. The value 498 is tailored so 2 full Link Layer packets contain single GATT payload. Anything longer would require 3rd, short packet, with lower payload/total bytes ratio.

    Do you have the devices already in production? Do you use your custom app with the library, or rely on nRF Connect Device Manager app only? I could make a hardcoded case for you in that case, e.g. "if device name starts with XXX, use MTU = 293".

  • Hi Aleksander,

    Sorry for the delay in my response. We had the nRF Connect Device Manager app as an addition (extra option) to our application. Now that we have the library working on both OS, there is no need to implement anything else on your side.

    Many thanks for your time and support!

Reply Children
No Data
Related