This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How to use external app firmware update

Hi,

I would like to use external app firmware to update over BLE a secondary microncontroler behind a NRF52832. I read that it's an experimental feature but it's very intersting because this avoids to implement an other mecanism in mobile application to update the other MCU.

I already developped the DFU over BLE for application, bootloader and soft device. I now try to make work the external app update.

SDK 17.0.2, SoftDevice s112

The process I imagine :

  • Run the NRF52 in DFU mode
  • Upload the external firmware (in Bank 1 to preserve app in Bank 0)
  • Once done, run boot the secondary MCU by driving GPIO (nReset/Boot)
  • Send the firmware over UART
  • Restart in normal mode

I already created a zip package using nrfutil : nrfutil pkg generate --hw-version 52 --application-version-string 1.0.0 --application XYZ_Project.hex --external-app --key-file private.key XYZ_Project.zip

where XYZ_Project.hex is a STM32 hex file.

I succeed in downloading the zip file in the NRF52 and I check the internal memory of the MCU. Everythong looks good in Bank 1 ;)

Now, I want to push the Bank 1 data to the other MCU. Searhing the code, I saw I have to implement this method :

nrf_dfu_result_t nrf_dfu_validation_post_external_app_execute(dfu_init_command_t const * p_init, bool is_trusted)

However, it's never called; I ran the code in debbuger mode and saw this code section in postvalidate function nrf_dfu_validation.c but don't really understand how it works :

#if NRF_DFU_SUPPORTS_EXTERNAL_APP
        else if (p_init->type == DFU_FW_TYPE_EXTERNAL_APPLICATION)
        {
            if (!is_trusted)
            {
                // This function must be implemented externally
                ret_val = nrf_dfu_validation_post_external_app_execute(p_init, is_trusted);
            }
            else
            {
                s_dfu_settings.bank_1.bank_code = NRF_DFU_BANK_VALID_EXT_APP;
            }
        }
#endif // NRF_DFU_SUPPORTS_EXTERNAL_APP

The method nrf_dfu_validation_post_external_app_execute is never executed (in debugger mode, I put a breakpoint on the function).

Help is welcome Smiley

Parents
  • Hi,

    The nrf_dfu_validation_post_external_app_execute() that you implement is called if the update is for an external app, and that is enabled in the bootloader and the whole firmware is received and the command is NRF_DFU_OP_OBJECT_EXECUTE. Generally, what the external app feature gives you is a way to transfer the image to the nRF, and then call your nrf_dfu_validation_post_external_app_execute(). From then you are responsible for all the rest.

    Regarding the code snippet you refer to, NRF_DFU_SUPPORTS_EXTERNAL_APP is an sdk_config.h macro that must be enabled. The is_trusted parameter is used to signal if the function can make permanent changes on the nRF state (has been validated etc.). You can see how it is used in the various post_validate functions etc.

    I think to understand the issue here it would be good if you enable debug logging so that we can see what happens in the bootloader, and compare that with what you would expected to happen. 

  • The full log :

    <info> app: Inside main
    <debug> app: In nrf_bootloader_init
    <debug> nrf_dfu_settings: Calling nrf_dfu_settings_init()...
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
    <warning> nrf_dfu_settings: Resetting bootloader settings since neither the settings page nor the backup are valid (CRC error).
    <debug> nrf_dfu_settings: Writing settings...
    <debug> nrf_dfu_settings: Erasing old settings at: 0x0007F000
    <debug> nrf_dfu_flash: Flash erase success: addr=0x0007F000, pending 0
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x0007F000, src=0x2000787C, len=896 bytes), queue usage: 1
    <debug> nrf_dfu_flash: Flash write success: addr=0x0007F000, pending 0
    <debug> app: Initializing transports (found: 1)
    <debug> nrf_dfu_ble: Initializing BLE DFU transport
    <debug> nrf_dfu_ble: Setting up vector table: 0x00072000
    <debug> nrf_dfu_ble: Enabling SoftDevice.
    <debug> nrf_dfu_ble: Configuring BLE stack.
    <debug> nrf_dfu_ble: Enabling the BLE stack.
    <debug> nrf_dfu_ble: Advertising...
    <debug> nrf_dfu_ble: Connected
    <debug> nrf_dfu_ble: Received BLE_GAP_EVT_CONN_PARAM_UPDATE
    <debug> nrf_dfu_ble: max_conn_interval: 12
    <debug> nrf_dfu_ble: min_conn_interval: 12
    <debug> nrf_dfu_ble: slave_latency: 0
    <debug> nrf_dfu_ble: conn_sup_timeout: 600
    <debug> nrf_dfu_ble: Finished handling conn sec update
    <debug> nrf_dfu_ble: Set receipt notif
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_RECEIPT_NOTIF_SET
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_ble: Received BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST (request: 247, reply: 247).
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_SELECT (command)
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> app: Shutting down transports (found: 1)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_CREATE (command)
    <debug> app: timer_stop (0x20005984)
    <debug> app: timer_activate (0x20005984)
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 143 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (command)
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_CRC_GET (command)
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_EXECUTE (command)
    <debug> nrf_dfu_validation: PB: Init packet data len: 66
    <info> nrf_dfu_validation: Signature required. Checking signature.
    <info> nrf_dfu_validation: Calculating hash (len: 66)
    <info> nrf_dfu_validation: Verify signature
    <info> nrf_dfu_validation: Image verified
    <debug> app: Enter nrf_dfu_cache_prepare()
    <debug> app: required_size: 0xD1A8.
    <debug> app: single_bank: false.
    <debug> app: keep_app: true.
    <debug> app: keep_softdevice: true.
    <debug> app: SD_PRESENT: true.
    <debug> app: Bank contents:
    <debug> app: Bank 0 code: 0x00: Size: 0x0
    <debug> nrf_dfu_flash: Flash erase success: addr=0x0007F000, pending 4
    <debug> nrf_dfu_flash: Flash write success: addr=0x0007F000, pending 3
    <debug> nrf_dfu_flash: Flash erase success: addr=0x0007E000, pending 2
    <debug> nrf_dfu_flash: Flash write success: addr=0x0007E000, pending 1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_SELECT (data)
    <debug> nrf_dfu_req_handler: crc = 0x0, offset = 0x0, max_size = 0x1000
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_SELECT (data)
    <debug> nrf_dfu_req_handler: crc = 0x0, offset = 0x0, max_size = 0x1000
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_CREATE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_erase(addr=0x0x00019000, len=1 pages), queue usage: 1
    <debug> nrf_dfu_req_handler: Creating object with size: 4096. Offset: 0x00000000, CRC: 0x00000000
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 244 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x00019000, src=0x200067E0, len=244 bytes), queue usage: 2
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash erase success: addr=0x00019000, pending 2
    <debug> nrf_dfu_flash: Flash write success: addr=0x00019000, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 244 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x000190F4, src=0x200067E0, len=244 bytes), queue usage: 1
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash write success: addr=0x000190F4, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 244 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x000191E8, src=0x200067E0, len=244 bytes), queue usage: 1
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash write success: addr=0x000191E8, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    
    
    ...
    
    
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x00025E4C, src=0x200067E0, len=244 bytes), queue usage: 1
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash write success: addr=0x00025E4C, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 192 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x00025F40, src=0x200067E0, len=192 bytes), queue usage: 1
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash write success: addr=0x00025F40, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_CRC_GET (data)
    <debug> nrf_dfu_req_handler: Offset:53248, CRC:0x672E882D
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_EXECUTE (data)
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> app: timer_stop (0x20005984)
    <debug> app: timer_activate (0x20005984)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_CREATE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_erase(addr=0x0x00026000, len=1 pages), queue usage: 1
    <debug> nrf_dfu_req_handler: Creating object with size: 424. Offset: 0x0000D000, CRC: 0x672E882D
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash erase success: addr=0x00026000, pending 1
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 244 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x00026000, src=0x200067E0, len=244 bytes), queue usage: 1
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash write success: addr=0x00026000, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_ble: Buffer 0x200067E0 acquired, len 180 (244)
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_WRITE (data)
    <debug> nrf_dfu_flash: nrf_fstorage_write(addr=0x000260F4, src=0x200067E0, len=180 bytes), queue usage: 1
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_flash: Flash write success: addr=0x000260F4, pending 1
    <debug> nrf_dfu_ble: Freeing buffer 0x200067E0
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_CRC_GET (data)
    <debug> nrf_dfu_req_handler: Offset:53672, CRC:0x37073ED7
    <debug> nrf_dfu_req_handler: Request handling complete. Result: 0x1
    <debug> nrf_dfu_req_handler: Handle NRF_DFU_OP_OBJECT_EXECUTE (data)
    <debug> nrf_dfu_req_handler: Whole firmware image received. Postvalidating.
    <debug> nrf_dfu_validation: Hash verification. start address: 0x19000, size: 0xD1A8
    <debug> nrf_dfu_settings: Writing settings...
    <debug> nrf_dfu_settings: Erasing old settings at: 0x0007F000
    <debug> nrf_dfu_flash: nrf_fstorage_erase(addr=0x0x0007F000, len=1 pages), queue usage: 1
    <info> nrf_dfu_settings: Backing up settings page to address 0x7E000.
    <debug> app: timer_activate (0x20005984)
    <debug> nrf_dfu_flash: Flash erase success: addr=0x0007F000, pending 4
    <debug> nrf_dfu_flash: Flash write success: addr=0x0007F000, pending 3
    <debug> nrf_dfu_flash: Flash erase success: addr=0x0007E000, pending 2
    <debug> nrf_dfu_flash: Flash write success: addr=0x0007E000, pending 1
    <debug> nrf_dfu_req_handler: All flash operations have completed. DFU completed.
    <debug> app: Shutting down transports (found: 1)
    <debug> nrf_dfu_ble: Shutting down BLE transport.
    <debug> nrf_df<info> app: Inside main
    <debug> app: In nrf_bootloader_init
    <debug> nrf_dfu_settings: Calling nrf_dfu_settings_init()...
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
    <debug> nrf_dfu_settings: Using settings page.
    <debug> nrf_dfu_settings: Copying forbidden parts from backup page.
    <debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
    <info> nrf_dfu_settings: Backing up settings page to address 0x7E000.
    <debug> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <info> app: Boot validation failed. No valid app to boot.
    <debug> app: DFU mode because app is not valid.
    <info> nrf_bootloader_wdt: WDT is not enabled
    <debug> app: Enter main loop

    Regards,

    Thib

  • Hi,

    It looks like this is correctly written to bank 1 as you write, and then the code is set to NRF_DFU_BANK_VALID_EXT_APP. Then the nrf_dfu_validation_post_external_app_execute() function is never called, and you normally don't need it. So now after successfully performing this operation you have the external app in flash (with bank 1 code indicating that). Then you can check that using nrf_dfu_validation_valid_external_app() and update your external MCU.

    Note that the external app code is not used much which is probably why it does not always make sense out of the box. You can see an example of external app in the Thread and ZigBee SDK (though I do not think it is very relevant, but it is the only example I am aware of).

Reply
  • Hi,

    It looks like this is correctly written to bank 1 as you write, and then the code is set to NRF_DFU_BANK_VALID_EXT_APP. Then the nrf_dfu_validation_post_external_app_execute() function is never called, and you normally don't need it. So now after successfully performing this operation you have the external app in flash (with bank 1 code indicating that). Then you can check that using nrf_dfu_validation_valid_external_app() and update your external MCU.

    Note that the external app code is not used much which is probably why it does not always make sense out of the box. You can see an example of external app in the Thread and ZigBee SDK (though I do not think it is very relevant, but it is the only example I am aware of).

Children
  • Hi Einar,

    Thanks a lot, this is clearer now.

    When or where I have to execute nrf_dfu_validation_valid_external_app() and update the external MCU ? I don't really understand how to catch the end of the upload and launch the update.

    I believed it's in nrf_dfu_validation_post_external_app_execute() I had to do this.

    Thanks a lot.

  • Hi,

    No, you don't need nrf_dfu_validation_post_external_app_execute(). That was my bad. To be honest the last time I looked at this was a few years ago, as it is not a commonly used feature. 

    Essentially once the app is in bank 1 it is up to you. If you want to do this in the nRF bootloader, then you could modify <SDK_17.02>/components/libraries/bootloader/dfu/nrf_dfu_validation.c around line 1010-1020 so that you call your implementation for updating the other MCU from there. Alternatively you could let the bootloader reset and start your application, and do the rest from your application. The app would know that a image is ready by seeing that the bank 1 code is NRF_DFU_BANK_VALID_EXT_APP. (Once the external MCU is successfully updated  you probably want to invalidate that in this case so that you don not attempt to update again).

  • Hi Einar!

    Thanks a lot for your previous help, this is now working like a charm ;) the external MCU can be updated from my app.

    I now try to manage external app versionning and I'm exploring the NRF_DFU_EXTERNAL_APP_VERSIONING option in my sdk_config.h on the BL project.

    I searched the libraries and I saw that there are some checks if this option is activated (extract from nrf_dfu_ver_validation.c / fw_version_ok :

    #if NRF_DFU_EXTERNAL_APP_VERSIONING
        else if (p_init->type == DFU_FW_TYPE_EXTERNAL_APPLICATION)
        {
            return (p_init->fw_version >= s_dfu_settings.app_version);
        }
    #else
        else if(p_init->type == DFU_FW_TYPE_EXTERNAL_APPLICATION)
        {
            return true;
        }
    #endif // NRF_DFU_EXTERNAL_APP_VERSIONING
    #endif // NRF_DFU_SUPPORTS_EXTERNAL_APP

    What I understand is that p_init is linked to the new package sent by BLE and dfu_settings contains the information of the previous one.

    However, I don't understand how the bootloader can differantiate app_version for application and app_version for external application... 

    When I dumped the flash content and ran nrfutil settings display, I only saw app_version but nothing about external_app_version.

    Maybe it is not already managed as external app is not often used ?

  • Hi Thib,

    As you know you use the external-app flag when you generate the DFU package, and this will become part of the init packet. You cannot have both an external app and normal/local app in the same DFU operation, so here the specified app version will apply to the external app. There is no need to differentiate between external and normal app, as the updates never occur at the same time. See example from generating external app update, and display of the generated package (which is essentially what is in the init packet, which is pointed to by p_init in your code snippet from the bootloader.

    C:\Users\eith\SDK\nRF5_SDK_17.0.2_d674dde\examples\dfu>nrfutil pkg generate --application secure_dfu_test_images\ble\nrf52832\hrs_application_s132.hex --application-version 123 --hw-version 5 --sd-req 0 --external-app --key-file c:\vault\priv.pem update_external.zip
    Zip created at update_external.zip
    
    C:\Users\eith\SDK\nRF5_SDK_17.0.2_d674dde\examples\dfu>nrfutil pkg display update_external.zip 
    
    DFU Package: <update_external.zip>:
    |
    |- Image count: 1
    |
    |- Image #0:
       |- Type: application
       |- Image file: hrs_application_s132.bin
       |- Init packet file: hrs_application_s132.dat
          |
          |- op_code: INIT
          |- signature_type: ECDSA_P256_SHA256
          |- signature (little-endian): b'025356933b17ff646eb2d7d536f82ee6ace8f62284521e6cccb53a8730e878599cb13d885e88438b6816c1eb74f6b4282111c9e0337f3f7eec3fb0da591dd0e1'
          |
          |- fw_version: 0x0000007B (123)
          |- hw_version 0x00000005 (5)
          |- sd_req: 0x00
          |- type: EXTERNAL_APPLICATION
          |- sd_size: 0
          |- bl_size: 0
          |- app_size: 85500
          |
          |- hash_type: SHA256
          |- hash (little-endian): b'e61a6b2fe616bfb0e3da756774a781e49db4dc8cab5732adf216723613bbe677'
          |
          |- boot_validation_type: ['VALIDATE_GENERATED_CRC']
          |- boot_validation_signature (little-endian): [b'']
          |
          |- is_debug: False
    

    Einar

  • Hi,

    I understand well the content of the p_init pointer.

    What I don't understand is the content of the BL settings. The externall app version is not mentionned in the BL settings so I don't know how the version comparison can work :

    p_init->fw_version >= s_dfu_settings.app_version

    Maybe the BL setting store alternatively the app version or the external app version (make no sense?)

    Moreover, once the external app is updated in flash and the application boot, the p_init pointer is lost and I can only trust the BL settings loaded in nrf_dfu_settings.

    sorry if I'm not clear Smiley

Related