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

DFU control point write, app not restarting into bootloader, gcc, SDK 8.0.0

I've followed the instructions for adding the DFU service to my app to the letter and can see the DFU service from the central and can enable notifications on the control point and write a 01 04 to it to kick off DFU OTA for the application. The application does NOT restart into the bootloader, however. I have some debug LEDs on my board that tell me so.

I'm using SDK 8.0.0 on the whole but for GCC, the bootloader_util_gcc.c StartApplication() assembler appears to broken in 8.0.0, from what I read here, so I've pulled that source into my project and used the implementation from SDK 8.1.1.

On a normal reset I can see my bootloader running properly and starting the application. I'm writing to the control point characteristic both from an Android app and from the MCP on Windows. Neither works. I can't connect to the device using the MCP on Android (no CONNECT button on the UI next to the device).

Here's the code for the Android app (central), anyway. There is no error on writing the characteristic value.

    // nordicDfuControlPoint is the characteristic, from discovery.
    final BluetoothGattDescriptor descriptor =
            nordicDfuControlPoint.getDescriptor(BleCharacteristic.CCCD);

    if (descriptor == null) {
        Log.e(TAG, "Can't write descriptor for Nordic DFU control point." +
                " BluetoothGattCharacteristic.getDescriptor() returned null.");
        return;
    }

    Log.d(TAG, "Enabling notifications on characteristic for Nordic DFU control point.");

    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); // {0x01, 0x00}

    if (!gatt.writeDescriptor(descriptor)) {
        Log.e(TAG, "Can't write descriptor for Nordic DFU control point for enable."
                + " BluetoothGatt.writeDescriptor() returned false.");

        // See BluetoothGatt.java L1020. If either of these are null, we get false back.
        Log.e(TAG, "descriptor.getCharacteristic(): " + descriptor.getCharacteristic());
        Log.e(TAG, "characteristic.getService(): "
                + descriptor.getCharacteristic().getService());
    }

    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
            nordicDfuControlPoint.setValue(NORDIC_DFU_UPDATE_APPLICATION, // Integer, 0x0104.
                    BluetoothGattCharacteristic.FORMAT_UINT32, 0);

            if (!gatt.writeCharacteristic(nordicDfuControlPoint)) {
                Log.e(TAG, "Can't write value for Nordic DFU control point for update application."
                        + " BluetoothGatt.writeDescriptor() returned false.");

                // See BluetoothGatt.java L1020. If either of these are null, we get false back.
                Log.e(TAG, "descriptor.getCharacteristic(): " + descriptor.getCharacteristic());
                Log.e(TAG, "characteristic.getService(): "
                        + descriptor.getCharacteristic().getService());
            }
        }
    }, 2000); // 2s

My bootloader code has been modified from the example to remove the button stuff and turn some of my LEDs on, not much else.

How do I debug this?

[EDIT]

StartApplication() implementation: bootloader_util_gcc_bike.c

[EDIT]

Before dfu_app_handler.c calls dfu_ble_svc_set_peer_data(), the key_set passed in looks like this to gdb:

		{
		  keys_periph = {enc_key = {p_enc_key = 0x20003d88}, p_id_key = 0x20002a80 <m_local_id_info>, p_sign_key = 0x0},
		  keys_central = {enc_key = {p_enc_key = 0x0}, p_id_key = 0x200029f0 <m_peer_table>, p_sign_key = 0x0}
		}

I can't see any more detail than that in gdb.

  • @Hung That example project isn't much use, sorry. It's for a 51422, the Makefile is Windows-specific (assumes nrfproj.exe is present) and the linker script has no NOINIT RAM section.

  • @Eliot: The nRF51422 and nRF51822 are the same when you use with S110 (only difference is nRF51422 can run S210, S310). You don't need NO-INIT on the application. Storing bonding information to NO-INIT area (for bootloader) is done by the SVC call.

  • @Hung, OK, well, regardless, I can't get the example app to flash to a custom board on a Mac. I'm looking for differences between the example app and mine and found that your docs make no mention of the fact that I need to add an app_context_load() function and call it from the device manager event handler. But even with that in place, the call to dfu_ble_svc_set_peer_data() still fails. I'll edit the question again and show as much as gdb can see before that call for the peer data.

  • If the SVC_Handler is implemented incorrectly it could make the bootlaoder fail to enter DFU mode. My suggestion would be to use the official GCC bootloader from SDK 8.1.0 if possible. Alternatively import the df_ble_svc.c (adds support for GCC) file from this release to see if it solves the problem.

    You were right that the SDK doc was lacking information regarding the service changed characteristic. In addition to implementing the load app context function as you mentioned, you also need to make sure to allocate space for the app context in device_manager_cnfg.h. I will report this internally, sorry for the inconvenience.

    Attached bug fixed linker script as the one provided in the SDK was missing the NOLOAD directive for the .noinit and .bootsettings sections: dfu_gcc_nrf51.ld This issue caused the linker to ("try") place init data in the bootsettings page and .noinit section in RAM.

  • @Eliot: Could you explain the issue you have with flashing the example ? I don't really get it if you can flash your own hex but not the hex from our example.

    Could you let me know which bootloader you are using (SDK version) ? Have you tried the SDK v8.1 bootloader ? As suggested by Vidar, there are some modification added to dfu_ble_svc.c file to support gcc.

    You were correct on the app_context_load(), we forgot to mention it in the documentation, we will try to add that. The main purpose is to tell the application to send the service changed notification after it finishes with DFU. But you still should be able to jump to DFU bootloader, with or without that call.

    I attached in the answer the 2 hexes, SDK v8.1 bootloader and hrs_dfu application I compiled using gcc (v4.9 2015q1) on Window. I tested and it worked fine.

    nrf51422_xxac_s110.hex

    bootloader.hex

Related