DFU over BLE fails with GATT ERROR on nRF52840

I am using a custom board with an nRF52840.
For OTA updates, I’m using SDK 17.1.0 based on the sample codes:

  • Bootloader: secure_bootloader

  • Application: A custom application based on ble_app_blinky.

After flashing my custom application, I try to perform a Firmware Update using the Android version of nRF Connect. However, the update process gets stuck right after "DFU Initialization", and eventually results in a GATT ERROR.

Below is the log from J-Link RTT Viewer:

Device is preparing to enter bootloader mode
Device will enter bootloader mode

The relevant source code is shown below:

static void ble_dfu_buttonless_evt_handler(ble_dfu_buttonless_evt_type_t event)
{
    switch (event)
    {
        case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
            NRF_LOG_INFO("Device is preparing to enter bootloader mode");
            break;
        case BLE_DFU_EVT_BOOTLOADER_ENTER:
            NRF_LOG_INFO("Device will enter bootloader mode");
            break;
        case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
            NRF_LOG_INFO("Device failed to enter bootloader mode");
            break;
        default:
            NRF_LOG_INFO("Unknown event from ble_dfu.");
            break;
    }
}

The nRF Connect (Android) screen during the update is shown below:

Any help or suggestions would be greatly appreciated.
Thank you in advance!

  • Hi, 

    Have you bonded your device with the phone ? 
    Please make sure you have service changed characteristic in the custom application ? 
    Please try to test with the default ble_app_buttonless_dfu example. 
    Do you see the device switch to bootloader mode and advertise in bootloader more (DFU_Targ) ? 
    Please try to test using nRF Connect mobile app as well. 

  • Thank you for your reply.

    The phone is not paired with the device.
    With the combination of secure_bootloader and ble_app_blinky, I was able to perform DFU without bonding.

    >Do you see the device switch to bootloader mode and advertise in bootloader mode (DFU_Targ)?
    No, it does not switch to bootloader mode (it does not advertise as DfuTarg).

    Do I need to implement anything additional in ble_dfu_buttonless_evt_handler in order to enter bootloader mode?
    It seems like the execution does not reach buttonless_dfu_sdh_state_observer at runtime. Could this be the issue?

    Below is a snippet of the DFU-related code implemented in my custom application.
    If you notice anything missing or incorrect, please let me know.

    NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);
    static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
    {
        switch (event)
        {
            case NRF_PWR_MGMT_EVT_PREPARE_DFU:
                NRF_LOG_INFO("Power management wants to reset to DFU mode.");
                break;
            default:
                return true;
        }
    
        NRF_LOG_INFO("Power management allowed to reset to DFU mode.");
        return true;
    }
    
    static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context)
    {
        if (state == NRF_SDH_EVT_STATE_DISABLED)
        {
            // Softdevice was disabled before going into reset. Inform bootloader to skip CRC on next boot.
            nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC);
    
            //Go to system off.
            nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
        }
    }
    
    NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0) =
    {
        .handler = buttonless_dfu_sdh_state_observer,
    };
    
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.    
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
        
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, on_ble_evt, NULL);
    }
    
    static void services_init(void)
    {
        // Initialize the DFU service
        ble_dfu_buttonless_init_t dfus_init =
        {
            .evt_handler = ble_dfu_buttonless_evt_handler
        };
        err_code = ble_dfu_buttonless_init(&dfus_init);
        APP_ERROR_CHECK(err_code);
    }
    
    int main(void)
    {
      ret_code_t err_code;
      // Initialize the async SVCI interface to bootloader before any interrupts are enabled.
        err_code = ble_dfu_buttonless_async_svci_init();
        APP_ERROR_CHECK(err_code);
      ...
    }
    

    Thanks in advance for your help!

  • Hi, 
    I did a quick look but don't see anything obviously missing in your code. 
    You may want to run in debug mode and break after BLE_DFU_EVT_BOOTLOADER_ENTER event to see where the program counter is. 
    Please check if it's still in the application or it's jumped to the bootloader but bootloader crash or something.  
    You can try to test by forcing the device to enter bootloader mode (hold "enter bootloader mode" button when booting, or not flashing the application)

Related