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

Setting up OTA for SDK12

Hi,

I have the ble_app_uart with OTA functionality on SDK11 that works.
Now I want to do the same with SDK12, but I see things have changed now.

I've found this tutorial:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.0.0%2Fble_sdk_app_dfu_bootloader.html


But some things are not clear to me:

The bootloader_secure example, what does it do exactly?
On SDK11 I just had a hex file that I wrote with nRF studio as bootloader, and in the ble_app_uart I had a bit of code to jump to it.
How does that now work with  bootloader_secure? Also what does it mean "Button 4: Holding this button during startup makes the device enter DFU mode."

Which button is that? How do I get it into DFU mode directly from the ble_app_uart without using any buttons?

Do I really need to build my own secure_bootloader, or is there one prebuilt with the public keys that I can use for testing purposes?

It says: "You can either create your own firmware package for testing or use one of the provided packages that are located in subfolders of <InstallFolder>\examples\dfu\ble_dfu_send_hex. "
But there is no ble_dfu_send_hex in that directory on SDK12. Where can I find that?

Where do I find the correct code and libraries to add in my ble_app_uart to perform the jump to the bootloader?

Can I use the old SDK11 bootloader and jump into it from SDK12?
I tried setting gpregret to 0xB1 but I could not get it to enter in the old bootloader.

Edit:
I found this tutorial which answers most of the questions:
https://devzone.nordicsemi.com/b/blog/posts/getting-started-with-nordics-secure-dfu-bootloader

The only thing I did not find is how to jump from the application to the buttonless dfu bootloader.

Parents
  • Hi,

    There was a change in the bootloader for SDK 12, and buttonless DFU went experimental. If you want to use buttonless DFU from SDK 12 you should use SDK 12.3, as the versions before that does not work properly.

    I wouldn't recommend using the old bootloader for new projects, as performing an update from the old to the new bootloader is a bit cumbersome. Rather I would recommend to use the newest available SDK, which currently means v12.3 for nRF51 series and v15.2 for nRF52 series.

    If you choose to use the old bootloader anyway, you can have a look at Adding DFU Service support to an application and the Example application.

    For a stable DFU triggered from the application you should have a look at the Buttonless DFU Template Application from SDK 14.0 or newer. (That is when buttonless DFU went out of experimental.)

    Regards,
    Terje

  • Hi,

    I am actually stuck with an existing project on SDK11, but now I'm working on getting everything working on SDK12.3, I had issues with trying to port it to any newer SDK other than 12.3 so I'm stuck with that for now. I looked into the example from SDK14 and I found this bit of code:

    case BLE_DFU_EVT_BOOTLOADER_ENTER:
                // YOUR_JOB: Write app-specific unwritten data to FLASH, control finalization of this
                //           by delaying reset by reporting false in app_shutdown_handler
                NRF_LOG_INFO("Device will enter bootloader mode.");
                break;


    But I don't see how in this example it performs the jump to the bootloader.
    I read it needs to modify something in the bootloader settings, and I found some code:
    s_dfu_settings.enter_buttonless_dfu = true;
    (void) nrf_dfu_settings_write(flash_callback);


    But then what is the s_dfu_settings variable?
    Also that seems to require ble_dfu.h, which then requires a whole set of other includes including crc32,
    so I'm not sure that is the way to go.


    Edit:
    I'm a bit confused with which example does what exactly.
    The bootloader_secure_ble is the bootloader but it requires a button to be held for it to enter.
    I don't have any buttons on my device, so I need the bootloader to run only when started from the main application.

    The buttonles dfu example is an application example to jump to the bootloader?
    But if that is the case what is with the button needed to be held down?
    Do I need to modify the bootloader_secure_ble code and have it always boot?
    How does all of this work?
    And even in the buttonles dfu source I don't see where is the actual code that performs the jump.

  • Hi,

    I will try to explain how these things fit together. (They are not straight-forward, by any means.)

    The boot process, when you have a bootloader installed, starts with the Master Boot Record (MBR) which is part of the SoftDevice hex file (it is the first flash page.) For now, all we need to know about the MBR is that it sends execution to the Bootloader.

    The bootloader then makes a choice: Either start the application or enter DFU mode. There are multiple ways to enter DFU mode, and one of them alone is sufficient to trigger the DFU mode:

    - holding in a given button when board resets.
    - it has been requested by previous write to a certain flash memory location
    - no valid application is found, so cannot jump to any application

    The "write to flash memory location" option is the way to enter DFU from an application in SDK 12.3. (There are some other ways in some other SDK versions.)

    You will see that both the "hold button" and the "having written to flash" options are checked in nrf_dfu_enter_check() in nrf_dfu.c. (The bootloader project is found in <sdk folder>\examples\dfu\bootloader_secure\pca10040\, or correspondingly for other boards.)

    The s_dfu_settings structure is located in flash, in what is called the "DFU settings page". In order for the application to trigger DFU mode, it must also have knowledge about this flash page and the structure, so that it can write to it.

    The "buttonless DFU example" is an application that triggers DFU mode from the application. It contains a BLE service for letting you do so, but you may choose to take that functionality out of the BLE service and rather trigger a reset into DFU mode another way.

    As you have already mentioned yourself, the buttonless DFU application writes to s_dfu_settings.enter_buttonless_dfu, and the structure is written to flash. The rest of the path towards actually resetting the device goes through callback functions. First to flash_callback() in ble_dfu.c, when flash has been written. It then triggers a disconnect. Then when the BLE_GAP_EVT_DISCONNECTED event is handled by the ble_dfu_on_ble_evt() event handler (also in ble_dfu.c) it calls on_disconnect (also in ble_dfu.c) which actually does the reset (provided that we are waiting for disconnection in order to trigger DFU mode.)

    I am sorry if I have missed anything. Let me know and I will elaborate.

    Regards,
    Terje

  • Thanks for the explanation.

    But there still one thing I'm not getting.
    In the buttonless example, where is the actual code that writes to the bootloader settings and triggers the restart?
    All I see is this bit of code:

    static void ble_dfu_evt_handler(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt)
    {
        switch (p_evt->type)
        {
            case BLE_DFU_EVT_INDICATION_DISABLED:
                NRF_LOG_INFO("Indication for BLE_DFU is disabled\r\n");
                break;
    
            case BLE_DFU_EVT_INDICATION_ENABLED:
                NRF_LOG_INFO("Indication for BLE_DFU is enabled\r\n");
                break;
    
            case BLE_DFU_EVT_ENTERING_BOOTLOADER:
                NRF_LOG_INFO("Device is entering bootloader mode!\r\n");
                break;
            default:
                NRF_LOG_INFO("Unknown event from ble_dfu\r\n");
                break;
        }
    }


    But all this code does it it logs text.
    So where is the implementation of the settings updating and rebooting?

    I went down the rabbit hole of function calls and I found where it ends up:
    In static void ble_evt_dispatch(ble_evt_t * p_ble_evt) there is a function call to:
    ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); that eventually ends up at:
    on_rw_authorize_req(p_dfu, p_ble_evt);  that eventually ends up at:
    on_ctrlpt_write(p_dfu, &p_auth_req->request.write);  that eventually ends up at:
    enter_bootloader(p_dfu); where s_dfu_settings shows up as a global variable.

    But here is what I don't get:
    Why do I need any arguments in the enter bootloader segment of code?
    Looking at the enter bootloader code, should it not be treated as a void function call with no input arguments?
    As far I see I need to call:
    static void on_ctrlpt_write(ble_dfu_t * p_dfu, ble_gatts_evt_write_t const * p_evt_write)
    But in my case, in my ble_app_uart what do I do with the p_dfu and p_evt_write arguments?
    Why does the code need them at all to perform a flash write and restart?

    What I have in my code is a case switch like this:
    case 'O'://OTA
    	interrupts_disable();
    	//enter_bootloader();
    		
    	return;


    So what exactly from that example I must put instead of //enter_bootloader(); ?


    Can I just bypass all this and use the SDK11 methd of using if(NRF_POWER->GPREGRET == 0xB1)return true; in the secure_bootloader and then setting GPREGRET to 0xB1 in my application?


    Edit:
    I was trying to load the bootloader and my app as is, I modified the secure_bootloader to turn on a led so I can see it running.
    When I program the softdevice, and then the bootloader, the bootloader starts and keeps rebooting because there is no application yet. When I program the application (ble_app_uart) and restart the mcu it does not go through the bootloader code anymore. How do I make it to visit the bootloader code?
    Edit2:
    Do I need to perform this step every time I modify the application:
    infocenter.nordicsemi.com/index.jsp

Reply
  • Thanks for the explanation.

    But there still one thing I'm not getting.
    In the buttonless example, where is the actual code that writes to the bootloader settings and triggers the restart?
    All I see is this bit of code:

    static void ble_dfu_evt_handler(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt)
    {
        switch (p_evt->type)
        {
            case BLE_DFU_EVT_INDICATION_DISABLED:
                NRF_LOG_INFO("Indication for BLE_DFU is disabled\r\n");
                break;
    
            case BLE_DFU_EVT_INDICATION_ENABLED:
                NRF_LOG_INFO("Indication for BLE_DFU is enabled\r\n");
                break;
    
            case BLE_DFU_EVT_ENTERING_BOOTLOADER:
                NRF_LOG_INFO("Device is entering bootloader mode!\r\n");
                break;
            default:
                NRF_LOG_INFO("Unknown event from ble_dfu\r\n");
                break;
        }
    }


    But all this code does it it logs text.
    So where is the implementation of the settings updating and rebooting?

    I went down the rabbit hole of function calls and I found where it ends up:
    In static void ble_evt_dispatch(ble_evt_t * p_ble_evt) there is a function call to:
    ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); that eventually ends up at:
    on_rw_authorize_req(p_dfu, p_ble_evt);  that eventually ends up at:
    on_ctrlpt_write(p_dfu, &p_auth_req->request.write);  that eventually ends up at:
    enter_bootloader(p_dfu); where s_dfu_settings shows up as a global variable.

    But here is what I don't get:
    Why do I need any arguments in the enter bootloader segment of code?
    Looking at the enter bootloader code, should it not be treated as a void function call with no input arguments?
    As far I see I need to call:
    static void on_ctrlpt_write(ble_dfu_t * p_dfu, ble_gatts_evt_write_t const * p_evt_write)
    But in my case, in my ble_app_uart what do I do with the p_dfu and p_evt_write arguments?
    Why does the code need them at all to perform a flash write and restart?

    What I have in my code is a case switch like this:
    case 'O'://OTA
    	interrupts_disable();
    	//enter_bootloader();
    		
    	return;


    So what exactly from that example I must put instead of //enter_bootloader(); ?


    Can I just bypass all this and use the SDK11 methd of using if(NRF_POWER->GPREGRET == 0xB1)return true; in the secure_bootloader and then setting GPREGRET to 0xB1 in my application?


    Edit:
    I was trying to load the bootloader and my app as is, I modified the secure_bootloader to turn on a led so I can see it running.
    When I program the softdevice, and then the bootloader, the bootloader starts and keeps rebooting because there is no application yet. When I program the application (ble_app_uart) and restart the mcu it does not go through the bootloader code anymore. How do I make it to visit the bootloader code?
    Edit2:
    Do I need to perform this step every time I modify the application:
    infocenter.nordicsemi.com/index.jsp

Children
  • I figured it out.

    Here is the explanation:
    "Can I just bypass all this and use the SDK11 methd of using if(NRF_POWER->GPREGRET == 0xB1)return true; in the secure_bootloader and then setting GPREGRET to 0xB1 in my application?"
    Yes, and that is the easiest method to do it, and it works just fine.

    "When I program the softdevice, and then the bootloader, the bootloader starts and keeps rebooting because there is no application yet. When I program the application (ble_app_uart) and restart the mcu it does not go through the bootloader code anymore. How do I make it to visit the bootloader code?"
    The configuration settings are not set correctly.

    "Do I need to perform this step every time I modify the application:
    infocenter.nordicsemi.com/index.jsp"
    Yes, I made a batch file to redo it every time I make a new build.

    I had issues that the dfu would disconnect me when uploading a new image, but that was due to bad version settings, especially the --sd-req value.

  • Hi,

    Thanks for the update, and sorry for the delays. I am happy to hear that you found a solution!

    Regards,
    Terje

Related