Porting Nordic BLE DFU FOTA to Xamarin

Hi,

I'm searching for someone who is interested in helping me port the Nordic DFU FOTA library into Xamarin.
DFU stands for Device Firmware Upgrade, and FOTA means Firmware Over The Air.
Basically, it is a fantastic little library that allows you to upload new code to your BLE devices. Very useful if you are deploying IOT devices and want to update them later without plugging them in.
The library has already been implemented for Android (Java) and iOS (Swift): https://github.com/NordicSemiconductor/IOS-DFU-Library/tree/master/DFULibrary, https://github.com/NordicSemiconductor/Android-DFU-Library/tree/release/dfu/src/main/java/no/nordicsemi/android/dfu

If someone is interested getting into IOT, please contact me.

Thank you,

Shay

«1

Posts

  • OddbjornBakkeOddbjornBakke NOMember ✭✭

    Nice, will order the nRF52 Development Kit (DK) and try it out. :smile:

  • ShayOhayonShayOhayon USMember ✭✭✭

    Hi OddbjornBakke,

    Cool. I also have the nRF52 DK.

    I already started to implement an OS agnostic DFU class.
    Basically, the class will have a callback function that will be called upon value update and a delegate function for writing a characteristic to the peripheral.
    Then, in a given OS, one can implement the delegate function.

    I will place the initial code in this github:
    https://github.com/shayo/XamarinDFU

    Let me know when you get the kit. It would be great if you could contribute to this project.

  • PerHelgePerHelge NOMember

    Hi ShayOhayon,
    Great initiative. I'm also interested in this. I assume that the stuff that is in https://github.com/shayo/XamarinDFU is just the initial? Or have you gotten anywhere with it?

  • ShayOhayonShayOhayon USMember ✭✭✭

    Hi PerHelge

    I haven't gotten very far (got bogged down with other stuff).
    I need to look carefully again at the way FOTA is currently implemented in java before continuing the C# coding.

  • OddbjornBakkeOddbjornBakke NOMember ✭✭

    I have gotten the dev kit, and starting to testing it out.

    So this project should not be just a library-binding, but a re-implementation?

  • ShayOhayonShayOhayon USMember ✭✭✭

    Actually, I haven't thought about library binding. Maybe this will be easier.
    It seems like the majority of code is implemented in DfuServiceInitiator.java and DfuBaseService.java
    The entry point to the whole process is DfuServiceInitiator, which needs the device address for instantiation, and then additional parameters that are passed to the init function.
    The DfuBaseService takes care of actual gatt communication.

  • OddbjornBakkeOddbjornBakke NOMember ✭✭

    Library binding might be easy...

    Have done it a couple of times, and Android is sometimes done in just a couple of minutes (but might also be a hell).
    iOS is however worse, have not gotten objective-sharpie to generate the right bindings. Did fix it with manual bindings, but this takes time.

  • ShayOhayonShayOhayon USMember ✭✭✭

    So, compiling the Android DFU into AAR was quite simple. How do you add this to a Xamarin project and instantiate classes?

  • OddbjornBakkeOddbjornBakke NOMember ✭✭

    Just create a new binding project for android, and look at:
    https://github.com/chrisriesgo/Android-AltBeacon-Library/tree/master/AndroidAltBeaconLibrary
    https://github.com/mxdumas/xamarin-firebase-binding

    I often use the java decompiler, and browse around (for namespaces etc.).
    Starting with just "<remove-node", in the metadata.xml until it compiles, and then work my way from there.

  • DanielVartdalDanielVartdal USMember ✭✭
    edited April 2016

    Hey guys @OddbjornBakke @ShayOhayon . Great initiative.

    I just implemented the firmware upgrade using the DFU library on a java application. It worked perfectly.
    I have colleagues who have done a custom implementation on the firmware upgrade, which have used weeks to get it to work. But when this library got available, it was done in a couple of days.

    We also have some applications written in Xamarin which we do also want to benefit from the DFU library.

    Yesterday we got to bind the DFU library to the Xamarin project, and we could reach the DfuInitiator and Dfu Service.

    We have seen that the DfuInitiator gave an exception when passing in an empty .zip path. So communication with the library is ok.

    However, when the initiator/or we start the service StartService(intent) we do not recieve any feedback on the broadcastreciever. This might also be an error from our side on the implementation.

    Example from working Android code:

    We use two classes (borrowed from the DFU ToolBox source, which uses the DFU library):

    DfuService.class that extends from the DfuBaseService:

    import no.nordicsemi.android.dfu.DfuBaseService;
        public class DfuService extends DfuBaseService {
    
        @Override
        protected Class<? extends Activity> getNotificationTarget() {
        /*
         * As a target activity the NotificationActivity is returned, not the MainActivity. This is because
         * the notification must create a new task:
         *
         * intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         *
         * when you press it. You can use NotificationActivity to check whether the new activity
         * is a root activity (that means no other activity was open earlier) or that some
         * other activity is already open. In the latter case the NotificationActivity will just be
         * closed. The system will restore the previous activity. However, if the application has been
         * closed during upload and you click the notification, a NotificationActivity will
         * be launched as a root activity. It will create and start the main activity and
         * terminate itself.
         *
         * This method may be used to restore the target activity in case the application
         * was closed or is open. It may also be used to recreate an activity history using
         * startActivities(...).
         */
            return NotificationActivity.class;
        }
    }
    

    NotificationActivity class:

    public class NotificationActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // If this activity is the root activity of the task, the app is not running
            if (isTaskRoot()) {
                // Start the app before finishing
                final Intent intent = new Intent(this, ScanScreen.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtras(getIntent().getExtras()); // copy all extras
                startActivity(intent);
            }
    
            // Now finish, which will drop you to the activity at which you were at the top of the task stack
            finish();
        }
    }
    

    Add reference in AndroidManifest:

        <service
            android:name="classNamespace.DfuService"
            android:exported="true"
            android:label="@string/dfu_service_title" >
            <intent-filter>
                <action android:name="no.nordicsemi.android.action.DFU_UPLOAD" />
    
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
    

    MainActivity

    Register a local broadcastreciever:**

        public class MainActivity extends Activity
        {
    
        protected override void onResume() 
        {
            base.onResume();
        final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
        broadcastManager.registerReceiver(mDfuUpdateReceiver, makeDfuUpdateIntentFilter());
        }
    
        private static IntentFilter makeDfuUpdateIntentFilter() {
            if(intentFilter != null) return intentFilter;
    
            intentFilter = new IntentFilter();
            intentFilter.addAction(DfuService.BROADCAST_PROGRESS);
            intentFilter.addAction(DfuService.BROADCAST_ERROR);
            intentFilter.addAction(DfuService.BROADCAST_LOG);
            return intentFilter;
        }
    
     private void updateProgressBar(final int progress, final int part, final int total, final boolean error) {
    
            TextView alertTitle = (TextView) mConnectingAlert.findViewById(R.id.connectingTitle);
            Button connectingCancelButton = (Button) mConnectingAlert.findViewById(R.id.connectingCancelButton);
            connectingCancelButton.setVisibility(View.INVISIBLE);
    
            switch (progress) {
                case DfuService.PROGRESS_CONNECTING:
                    Log.i(TAG, "** DFU CONNECTING");
    
                    break;
                case DfuService.PROGRESS_STARTING:
                    Log.i(TAG, "** DFU STARTING");
    
                    break;
                case DfuService.PROGRESS_VALIDATING:
                    Log.i(TAG, "** DFU VALIDATING");
    
                    break;
                case DfuService.PROGRESS_DISCONNECTING:
                    Log.i(TAG, "** DFU DISCONNECTING");
    
                    break;
                case DfuService.PROGRESS_COMPLETED:
                    Log.i(TAG, "** DFU COMPLETED");
    
    
                    // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again.
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
    
                            // if this activity is still open and upload process was completed, cancel the notification
                            final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                            manager.cancel(DfuService.NOTIFICATION_ID);
                        }
                    }, 1000);
                    break;
                case DfuService.PROGRESS_ABORTED:
                    Log.i(TAG, "** DFU ABORTED");
    
    
                    // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again.
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            // if this activity is still open and upload process was completed, cancel the notification
                            final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                            manager.cancel(DfuService.NOTIFICATION_ID);
                        }
                    }, 200);
                    break;
                default:
            Log.i(TAG, "** Progress: " + progress + "%");
    
                    break;
            }
        }
    
        private final BroadcastReceiver mDfuUpdateReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(final Context context, final Intent intent) {
                    // DFU is in progress or an error occurred
                    final String action = intent.getAction();
    
                    if (DfuService.BROADCAST_PROGRESS.equals(action)) {
                        final int progress = intent.getIntExtra(DfuService.EXTRA_DATA, 0);
                        final int currentPart = intent.getIntExtra(DfuService.EXTRA_PART_CURRENT, 1);
                        final int totalParts = intent.getIntExtra(DfuService.EXTRA_PARTS_TOTAL, 1);
                        updateProgressBar(progress, currentPart, totalParts, false);
                    } else if (DfuService.BROADCAST_ERROR.equals(action)) {
                        final int error = intent.getIntExtra(DfuService.EXTRA_DATA, 0);
                        updateProgressBar(error, 0, 0, true);
    
    
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                // Clear progressess and cleanup parameters                       
    
                            }
                        });
    
                        // We have to wait a bit before canceling notification. This is called before DfuService creates the last notification.
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                // if this activity is still open and upload process was completed, cancel the notification
                                final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                                manager.cancel(DfuService.NOTIFICATION_ID);
                            }
                        }, 200);
                    }
                }
            };
        }
    

    In short; The only reference to the DFU library (which is an external library in the java project) is the DFUService class I have created, which extends the DfuBaseService.

    We have binded the DFU library thorugh usage of a bindings library (jar file).

    The code above in its simlpicity, shows how little we need to use the DFU library, contrary to porting the whole library to c# code.

    If anyone makes progess, please notify me. And if you do have any questions, please do ask and I will do my best to help you guys out :)

    We also have a test-application of the (in-progress-but-not-quite-working)DFU-library binding usage in Xamarin, which we could share

  • DanielVartdalDanielVartdal USMember ✭✭

    Hey guys (@OddbjornBakke)

    We managed to reference the DFU library to our Xamarin application, so it's possible to do it this way.

    We are now upgrading our device via an Xamarin application via the linked Java Library :)

  • BrightLeeBrightLee KRMember ✭✭✭

    @DanielVartdal
    I'm in.
    Could you share your latest version of it?
    And plus, Aren't you using iPhone too?

  • DanielVartdalDanielVartdal USMember ✭✭
    edited August 2016

    Hi @BBright, sorry for the late response (summer vacation).

    Here is a link to the two projects we've been working on

    https://drive.google.com/folderview?id=0B32Y1itjWYGNQTBITFFxeDBBc2s&usp=sharing#list

    1. DFUBinding.zip contains the Xamarin bindings library which binds the DFU.jar.

    2. Bindingstest references the .dll file from DFUBinding.

    Since I only have the old solution uploaded, you will need to do some adjustments to make it work (I don't have time to look at it now, but will upload the updated libraries if I get hold of it ):

    1. You need to add gson.jar (http://mvnrepository.com/artifact/com.google.code.gson/gson/2.6.2) as an referenced library in your DFUBinding project.
    2. The Bindings library is referencing some appcompat resources that we couldn't quite get a hold of, so there was an error. Our solution was to just comment out the NotificationCompat (in DFUBaseService.java) reference and bindings to make it work.

    The NotificationCompat is used for adding Notification possibilites from the DFUBaseService to a referenced class.(I guess a popup-message or similar with all messages on progress etc).

    But it's fairly simple to build your own notification based on the broadcastmessages you get from the service.

    I haven't started on the iOS yet, but the port-strategy should be the same.

    Though, I've read that porting from iOS libraries to Xamarin is somehow a pain in the ass.. so fingers crossed on that one.

    All help on the iOS will be most appreciated.

    Kind regards,

    Daniel

  • DanielVartdalDanielVartdal USMember ✭✭
    edited August 2016

    @ShayOhayon @BBright: the link is updated with the newest version of our work. (I believe)

  • BrightLeeBrightLee KRMember ✭✭✭

    @DanielVartdal
    Thanks so much for your sharing.
    I tried to build the project but couldn't get resolve yet.
    I'll try.

    1. Should we have github repo?

    2. So basically, you made jar file from native DFU library, and create dfu project for xamarin?
      (Did you make jar file from this ? : https://github.com/NordicSemiconductor/Android-DFU-Library)

    That's how it's done?
    3. What document should I look for those technic? (And what is it called?)
    If you give me some more information it would be really helpful. (document url)

    Thanks.

  • DanielVartdalDanielVartdal USMember ✭✭
    edited August 2016

    That is correct, @BBright, the code that is used is this:

    https://github.com/NordicSemiconductor/Android-DFU-Library

    What we did extra, was to remove the NotificationCompatBuilder

    In the file: https://github.com/NordicSemiconductor/Android-DFU-Library/blob/release/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java

    private void updateProgressNotification(final int progress)

    Starting from line 2771:

    final NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setSmallIcon(android.R.drawable.stat_sys_upload).setOnlyAlertOnce(true);//.setLargeIcon(largeIcon);

    Before building.

    As for the repository, @ShayOhayon has one up and running, and was also the one who started this thread and initiative:

    https://github.com/shayo/XamarinDFU

  • BrightLeeBrightLee KRMember ✭✭✭

    @DanielVartdal
    Thanks so much for your response!

    Can you provide the official doc link url or name of technique that you used to make jar file from native library?

    I looked nordic dev zone and I saw that their engineers even did not start to look Xamarin. It's so sad.

    Thanks.

  • DanielVartdalDanielVartdal USMember ✭✭

    @BBright for binding java libraries, we took the articles from Xamarin:

    https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/

    As for creating a jar/java library, you should seek up the Android documentations.

    I do believe we took first steps from:

    1. Creating Jar: http://stackoverflow.com/questions/21712714/how-to-make-a-jar-out-from-an-android-studio-project
    2. Creating Library (to bind to Xamarin Bindings project): https://developer.android.com/studio/projects/android-library.html

    Not me who did most of the work, but other than this we searched Google and got some hints here and there through StackOverflow as an example.

  • BrightLeeBrightLee KRMember ✭✭✭

    @DanielVartdal
    Thanks so much! It's really helpful.

  • DanielVartdalDanielVartdal USMember ✭✭

    Hey guys @BBright @OddbjornBakke @ShayOhayon

    As it seems that we have got the Android DFU library to co-work with Xamarin, have any of you guys started to look at the iOS implementation?

    We are planning to do the implementation for the DFU library on iOS, but as it seems, we don't start before September.

    Any input on the binding process with iOS libraries to Xamarin would be most appreciated

  • ShayOhayonShayOhayon USMember ✭✭✭

    No progress here. My time schedule shifted and this got pushed back :(

  • BrightLeeBrightLee KRMember ✭✭✭

    @DanielVartdal @ShayOhayon @OddbjornBakke

    Here is what I've done so far for iOS.

    The problem is iOS framwork from Nordic is created by Swift, which is currently not supported to bind Xamarin code.
    I'm looking for some bridge to connect(or convert) Swift framework to objc framework.

    This is what I've done so far. check this thread.
    http://forums.xamarin.com/discussion/comment/215427#Comment_215427

    And github :
    https://github.com/myallb/nRF51_DFULibrary_Xamarin_iOS/blob/master/README.md

    I'm sorry to say this, I'm considering to stop here and do some other stuff first.

  • ShayOhayonShayOhayon USMember ✭✭✭

    Hi @DanielVartdal , @OddbjornBakke, @BBright

    I have some great news.
    Adrian Eggenberger from http://www.arendi.ch/ was able to post their Xamarin implementation for DFU.
    More information here:
    https://devzone.nordicsemi.com/question/89049/dfu-in-ios-xamarin/?answer=91520#post-id-91520

    I haven't tried it yet. It is based on their Andrei Library, which isn't free. However, the DFU implementation is open source.
    I think there are two ways to continue here:
    1. Pay and get the Andrei library
    2. re-implement the basic BLE calls the Andrei DFU project makes to the Andrei library

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon
    Hi,
    Thanks for the news.

    I checked its source code.
    It seems pretty good.

    It's stopped after 2 minutes as they mentioned about license.

    I'm not sure re-implement its DFU project would work. Because it may depend on its library.
    I will check it later.

    Thanks.

  • BrightLeeBrightLee KRMember ✭✭✭

    Oh, He said "This library provides the implementation of the Nordic DFU. It may be used free but no official support is provided."

    Then using only DFU would be free.
    I will check it later.

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon @DanielVartdal

    Hi, all.

    I tested Arendi's DFU library.
    In my test, It turns our we have to buy licence anyway.

            Stream hexFileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("BleUpdate.Allb.hex");
    
            if (hexFileStream != null)
            {
              byte[] data = new byte[hexFileStream.Length];
              hexFileStream.Read(data, 0, data.Length);
              DfuFirmwareImage dfuFirmwareImage = new DfuFirmwareImage(data);
    
              Service.UpdateController.Update(Service.PeripheralManager.Selected.Peripheral,
                                              null,
                                              null,
                                              dfuFirmwareImage.Data,
                                              options);
            }
            else
            {
              Log.Error("Failed to load hex file from ressources");
            } 
    

    This is code for DFU library and it works great in their sample app. (It stops after 2 minutes unless we have licence)
    And I integrated this code into my app.

    But to call "Update" method, first parameter should be IPeripheral type which is in Arendi's BLE library.
    (casting is not working)
    I'm using other BLE connection library. It also has Peripheral class as we expect.
    But I couldn't make it to use with "Update" method.

    Any idea to make it without using Arendi's library?
    I think in Peripheral class of Native is same.

  • ShayOhayonShayOhayon USMember ✭✭✭

    I am still waiting for Adrian to release the code for the DFU update.
    I think once we have that, it will be possible to re-implement it with any BLE library of your choice.

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon
    That's right. I think so too.

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon
    What ble library are you using?
    I'm using xabre-ble-plugin which is developed version of Monkey robotics ble.

  • ShayOhayonShayOhayon USMember ✭✭✭

    I actually wrote my own for iOS. xabre-ble-plugin looks nice. I might switch since it already has Android support.

  • DanielVartdalDanielVartdal USMember ✭✭

    @BBright @ShayOhayon so you guys think they will open source the source code for this library?

  • ShayOhayonShayOhayon USMember ✭✭✭

    They have (at least, the DFU part): https://bitbucket.org/arendiag/arendi_nordicdfu/overview
    I think all we need to do now is to replace the their low level calls to device/service/characteristics/ with an open source cross platform ble library.

  • BrightLeeBrightLee KRMember ✭✭✭

    Fortunately, I just succeed to use it to my own. (for iOS and still working on Android little more)
    I'm making app with using Xamarin.forms PCL and xabre's ble plugin.

    Because the source code Adrian gave us is low level, I can use it beautifully.
    And I don't have to work for each platform. That's really nice. (little problem on Android but will make it)

    I know Daniel is using modified Monkey robotic library.
    What about you Shay?

    @DanielVartdal @ShayOhayon

  • ShayOhayonShayOhayon USMember ✭✭✭

    @BBright - I am using Xamarin forms and PCL as well. I will probably transition to xabre's ble because they have a nice async implementation, so basically, it's going to be the same approach. If you end up modifying the low-level calls to xabre's, please share. It will definitely save me some time.
    I'm glad we finally found a cross platform solution for this. It should have been provided by Nordic upfront.....

  • BrightLeeBrightLee KRMember ✭✭✭
    edited August 2016

    Yes. I'm really glad about it.
    I believe you all understand and same.

    I will share it.
    And yes. xabre's ble plugin is wonderful and still being developed.
    You can use it without MvvMCross, which I don't like.

    Only problem is, sharing on public is understandable to Adrian, engineer from Arendi company?
    How do you think?

    @ShayOhayon

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon @DanielVartdal

    Working fine on Android too.
    How do you guys think about sharing?
    If you have thought about my previous question, share please.

  • ShayOhayonShayOhayon USMember ✭✭✭

    I don't see a problem sharing. Adrian posted the code publicly on the Nordic Developer Zone.

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon @DanielVartdal

    OK. I will make github repo tomorrow.

  • BrightLeeBrightLee KRMember ✭✭✭

    @ShayOhayon @DanielVartdal

    I'm sorry.
    If you give me your email addresses, I will just send it to you guys.

  • BrightLeeBrightLee KRMember ✭✭✭

    Shay, I sent you an email.
    to answer you question, "why not publicity"

    Because I don't know if it might hit Adrian who opened source code.
    If you seems it's ok, please make repo. (If you make it as library, it will be great)
    Arendi company is selling ble library and I think the fact they have a nordic DFU is strong point, because others don't have.

    But for saying about just ble library, xabre's ble library is good enough.
    So, I don't know what Adrian's position in the company is, I don't know it's good choise to make it publicity.

    It's just my thought. Don't have to listen to me :)

    @ShayOhayon @DanielVartdal

«1
Sign In or Register to comment.