Can't DFU if app_shutdown_handler() initially returns false

Hi,

  I'm using SDK v17.0.2 on my nRF52840-based board. I have implemented buttonless secure OTA reflash and it generally works.  When I get the NRF_PWR_MGMT_EVT_PREPARE_DFU event in app_shutdown_handler() and then return true, the DFU process works great.  However, I would like to ensure that my device either has enough battery power or is powered by USB before starting a DFU.  When I added code to check for this, I found that I could not continue the DFU process by connecting USB power to my device after initiating the DFU process.  I then simplified the code to add a counter to app_shutdown_handler() to just delay before starting the DFU process rather than checking power constraints.  This also did not work.  It appears the DFU process works fine if I return true the first time through app_shutdown_handler(), but does not work if I return false the first time, and then return true when app_shutdown_handler() executes 1 second later.  Here is the code I am using to test this -

static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
    // uint8_t battery_level = 0;
    // bool usb_connected = false;
    bool ok_to_reflash = false;
    static int count = 0;

    switch (event)
    {
        case NRF_PWR_MGMT_EVT_PREPARE_DFU:
            // Ensure we are connected to USB or have enough
            // battery before starting DFU.

            // // Get USB connection status
            // usb_connected = system_usb_connected();

            // // Get the current battery level
            // battery_level = system_battery_level_get();

            // if ((battery_level > 80) || (usb_connected) || (++count > 10))
            if (++count > 10)
            {
                ok_to_reflash = true;
            }

            // NRF_LOG_INFO("DFU request, battery level: %i%%, USB %sconnected", battery_level, usb_connected ? "" : "not ");
            NRF_LOG_INFO("DFU request, count: %i", count);
            break;

        default:
            return true;
    }

    if (ok_to_reflash)
    {
        NRF_LOG_INFO("Power management allowed to reset to DFU mode.");
        enter_dfu();
    }

    return ok_to_reflash;
}

/**@brief Set register to start DFU on power-up and reset.
 *
 */
static void enter_dfu(void)
{
    uint32_t old;

    (void)sd_power_gpregret_get(0, &old);
    (void)sd_power_gpregret_set(0, old | BOOTLOADER_DFU_START);
    NVIC_SystemReset();
}

The stack trace when the DFU fails is this -

The stack trace when the DFU works (when app_shutdown_handler() returns true the first time through it) is this - 

  Am I missing something on how I've implemented this?

Thanks...

Brian

  • Hi Brian,

    I tested this now and I am not able to reproduce it. I used the buttonless DFU example from SDK 17.0.2 and essentially your changes:

    diff --git a/examples/ble_peripheral/ble_app_buttonless_dfu/main.c b/examples/ble_peripheral/ble_app_buttonless_dfu/main.c
    index 2b99a42..ff2a66b 100644
    --- a/examples/ble_peripheral/ble_app_buttonless_dfu/main.c
    +++ b/examples/ble_peripheral/ble_app_buttonless_dfu/main.c
    @@ -126,6 +126,20 @@ static void advertising_start(bool erase_bonds);
     // YOUR_JOB: Use UUIDs for service(s) used in your application.
     static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}};
     
    +
    +/**@brief Set register to start DFU on power-up and reset.
    + *
    + */
    +static void enter_dfu(void)
    +{
    +    uint32_t old;
    +
    +    (void)sd_power_gpregret_get(0, &old);
    +    (void)sd_power_gpregret_set(0, old | BOOTLOADER_DFU_START);
    +    NVIC_SystemReset();
    +}
    +
    +
     /**@brief Handler for shutdown preparation.
      *
      * @details During shutdown procedures, this function will be called at a 1 second interval
    @@ -138,31 +152,22 @@ static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUI
      */
     static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
     {
    +    static int count = 0;
    +
         switch (event)
         {
             case NRF_PWR_MGMT_EVT_PREPARE_DFU:
                 NRF_LOG_INFO("Power management wants to reset to DFU mode.");
    -            // YOUR_JOB: Get ready to reset into DFU mode
    -            //
    -            // If you aren't finished with any ongoing tasks, return "false" to
    -            // signal to the system that reset is impossible at this stage.
    -            //
    -            // Here is an example using a variable to delay resetting the device.
    -            //
    -            // if (!m_ready_for_reset)
    -            // {
    -            //      return false;
    -            // }
    -            // else
    -            //{
    -            //
    -            //    // Device ready to enter
    -            //    uint32_t err_code;
    -            //    err_code = sd_softdevice_disable();
    -            //    APP_ERROR_CHECK(err_code);
    -            //    err_code = app_timer_stop_all();
    -            //    APP_ERROR_CHECK(err_code);
    -            //}
    +            if (++count > 10)
    +            {
    +                enter_dfu(); // This is not needed!
    +                return true;
    +            }
    +            else
    +            {
    +                NRF_LOG_INFO("Not entering DFU mode yet...");
    +                return false;
    +            }
                 break;
     
             default:
    @@ -833,7 +838,7 @@ static void power_management_init(void)
      */
     static void idle_state_handle(void)
     {
    -    if (NRF_LOG_PROCESS() == false)
    +    //if (NRF_LOG_PROCESS() == false)
         {
             nrf_pwr_mgmt_run();
         }
    

    Note that the enter_dfu() function serves no purpose, as this is done by the buttonless DFU library when you return true. However, this works both with and without the call to enter_dfu().

    Einar

Related