Click on images to enlarge them

Wednesday, January 27, 2016

Easy DFU Operation: Activating the nRF Toolbox DFU applet from your Android app.

I have been working on a way to quickly support nRF51 DFU (Device Firmware Update) for development teams. Below is a snippet of java code which shows what I have worked out so far. 

The general outline is that I write a test app which is used by me (nRF51 firmware developer) to exercise the firmware.  This test app is also used by other (mobile-side) developers to update the firmware on their devices.

So the general interaction is to start the Test app (seen below) and after connected, hit the "DFU" button. This will send the DFU-activation sequence to the selected device over BLE.  Upon completion of this sequence, the device should be in DFU mode (e.g. executing in the bootloader) and advertising itself as "DfuTarg".

The final Test app step is to invoke the standard Nordic "nRF Toolbox" app, which will immediately display the nRF Toolbox's "DFU" applet.  This is accomplished by the Test app creating an "intent" which targets the nRF Toolbox app: it starts the nRF Toolbox app's DFU applet and immediately displays the DFU screen.  From that point the user can select the update file (*.zip) and the target device, per normal for this DFU applet.

NOTE:  This code snippet is rather crude and could be improved greatly, but the general flow outline should give you a general operational idea.  This was tested on Android Lollipop with a Nexus 7 tablet.  YMMV on other versions or hardware.




    private static final String DFU_PACKAGE = "no.nordicsemi.android.nrftoolbox/no.nordicsemi.android.nrftoolbox.dfu.DfuActivity";


    private void triggerDFU() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    Log.d(TAG, String.format("enable Indicates for ServiceChanged"));
                    mBleWrapper.setIndicationForCharacteristic(mServiceChangedCharacteristic, true);
                    sleep(1000);
                    Log.d(TAG, String.format("enable Notifys for DfuControlPoint"));
                    mBleWrapper.setNotificationForCharacteristic(mDfuControlPointCharacteristic, true);
                    sleep(1000);
                    Log.d(TAG, String.format("write DFU-Start to DfuControlPoint"));
                    byte[] dfuStart_bytes = new byte[12];
                    dfuStart_bytes[0] = 0x01;  // Start DFU
                    dfuStart_bytes[1] = 0x04;  // Application

                    mBleWrapper.writeDataToCharacteristic(mDfuControlPointCharacteristic, dfuStart_bytes);
                    dfuDelayHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Intent intent = new Intent(Intent.ACTION_MAIN);
                                intent.setComponent(ComponentName.unflattenFromString(DFU_PACKAGE));
                                intent.addCategory(Intent.CATEGORY_LAUNCHER);
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                PeripheralActivity.this.startActivity(intent);
                            } catch (Exception e) 
                                Toast.makeText(PeripheralActivity.this,
                                        "The \"nRF Toolbox\" app was not found. Please install it.",
                                        Toast.LENGTH_LONG).show();
                            }
                            finish();
                        }
                    }, DFU_DELAY);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
    }