Bluetooth Developer Studio Plugin Development

My mission was to write a code plugin for Bluetooth ® Developer Studio (BDS). My new plugin would generate client code for a UWP (Universal Windows Platform) application. At Matchbox Mobile we’ve already produced a selection of plugins, and have deep insights into how BDS works (plus, we built BDS ourselves!). This task, however, was aimed at finding out what the experience would be for a developer who had recently discovered BDS and had to create a new plugin. So, because I was neither involved in the creation of BDS nor any of the plugins my team created, I was the ideal candidate for the job. While I had experience with Bluetooth Smart on other platforms like Android and iOS, UWP was completely new to me in this area (though I did have experience with general Windows 10 UWP application development).

The Task

The task required some initial research, after which I had to run multiple experiments and tests of the target code that I would like to get from the plugin. There were a few things I knew I had to learn:

  1. How Bluetooth Developer Studio works, in the general sense (how to use it, how to define a profile/service/characteristic etc.)
  2. What the plugin architecture is and how to implement my own.
  3. How Bluetooth Smart is implemented on the Windows 10 UWP platform, what APIs I should use, and what I can get from it.

As an example, I wanted to target Heart Rate monitor belts, but from the start I had to keep in mind that the plugin and generated code should work with any kind of Bluetooth Smart device. Heart Rate was just a use case for me to run a demo of the final plugin that I should create. This is a fundamental point about a BDS plug-in. Its output is not tied to a single use-case.

Design

I decided that the output of my plugin wouldn’t be a fully working application, or even a template for it. I just wanted to create a set of files which could be added to the project, and used by developers to add Bluetooth Smart capabilities to their projects. In addition to this, they wouldn’t need to think about which files are crucial for BLE communication, and which are just placeholders or elements of the application’s template.

I also wanted to keep my plugin, as well as any generated code, in a “nice” and easy to read format. I wanted to include notes in each of the files that the plugin generated. I thought that some basic information in those files (like the version of Bluetooth Developer Studio and the plugin used to generate them) could be handy in certain cases.

The Good things

I started by learning how to use Bluetooth Developer Studio. I came to learn that it is a very handy and simple-to-use application, with quite impressive functionality – it is still under heavy development, so we can expect further improvements, and even more functionality. When Bluetooth SIG claims that BDS gives you:

“Up to 70% less Bluetooth development time”

… I can believe it. Either you are working on the device itself, or on the client utilizing that device, and you can gain a lot from the tool. There are many videos, tutorials, and examples provided by Bluetooth. Moreover, you are getting all adopted profiles, services, and characteristics bundled into the Developer Studio out of the box; unless you are working on a completely custom device, your work in the Developer Studio could be as easy as dragging blocks into your designer and then generating the code.

Next thing on the list was to learn how the plugins are created.

They are written in JavaScript and there are some handy example plugins created by others. As they are created in JavaScript, you can freely read and learn from them. Their quality varies, but all of them are a good source of knowledge. I found the Nordic nRF5 plugin the most useful as a reference.

Another vital source of knowledge was the Bluetooth Developer Studio Help. Here you can find a vast amount of information about creating a plugin, as well as a whole Class Diagram for the Model you will get when your plugin starts the work. Understanding that model is probably the most important thing you need in order to write the plugin. Due to the fact that the code is in JavaScript, developers have quite a lot of flexibility for code organization – this could speed up the process of writing the plugin, but also introduce spaghetti code. Proper organization is very important while writing the plugin to keep the code maintainable, easy to read, and able to extend later on.

Generally, a plugin’s logic is very simple:

  • Plugin just needs to have implemented two methods: one for getting some metadata about the plugin itself (GetInfo(): authors, name etc.) and a second one (RunPlugin(profileData)) to generate the code
  • RunPlugin method is called when you start generating the code, and you receive data about the profile (defined in Developer Studio) for your use inside the code.
  • You use the FileManager utility to read files from the plugin’s folder, and write to the output directory.
  • You get a templating engine, so you can define every file template and fill it with proper data inside your JavaScript code.
  • You get the console.log() utility, to print some messages to the user via the Bluetooth Developer Studio’s Generate Code dialog.

So far this is really easy to understand and remember. Everything else is just pure JavaScript programming. The difficulty of the task depends only on your current knowledge of JavaScript and the details of the platform for which you are generating code.

Another thing I needed to do was to learn how Windows 10 UWP should communicate with Bluetooth Smart devices. Microsoft has documentation pages about this; here are the two you really need:

You can also check out this example. Windows 10 API for Bluetooth Smart is consistent and concise. You shouldn’t have any problems finding out what is needed and what for, although I felt that more examples and instructions could be helpful for a smoother introduction for new developers in BLE development on Windows 10.

With knowledge from the above lessons, I was able to write an example application by hand. After testing and a little refactoring, (once I was happy with the result), I was able to freeze the code and use it as a template for the output which should be generated by the plugin. This was an easy task, for the most part…

Challenges

During my work I found two interesting challenges:

By default Bluetooth Developer Studio loads only a single file (Plugin.js) from the plugin directory.

I wanted to split my code into separate logic units (files), so it would be easier to maintain. But there is no built-in functionality for loading more JavaScript files while the plugin is working. I worked around this by using FileManager to read the JS code into the string, and then using the “devil” `eval()` function to make the code magically available for later lines of code. We don’t have any security threats here, so `eval()` is very handy and no one should worry about using it. You can also use the `new Function()` method to load the code into the memory. With that method, you can even load 3rd party popular frameworks like `underscore.js` or `jQuery`.

Now I could add as many JavaScript source code files as I wanted. I added a simple utility to define dependencies and load them at plugin initialization time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Dependencies = {
    dependencies: [
      "Enums.js",
      "Utils.js",
      "tmpl.js",
      "PluginProcessor.js"
   ],
    load: function () {
        var global_eval = eval;
        for (i = 0; i < this.dependencies.length; ++i) {
            var script = FileManager.ReadFile(this.dependencies[i]);
            global_eval(script);
        }
    }
};

The first line in the RunPlugin method is now just `Dependencies.load();` which loads all other files, before going into the real work of generating source code. I have added some utility functions, Enums definitions, and real code to generate the output into separate files.

Template engine cannot handle custom model generated inside my JavaScript code or reference templates between each other.

Another limitation I found was the functionality of the built-in template engine. This is based on Liquid, but the real implementation is in the form of a dotliquid library running in a native part of the Bluetooth Developer Studio. I had no influence on how it was configured, and currently it does not support the ability to include one template inside another. Another limitation is that the model passed to the engine needs to meet special requirements (it has to be properly registered inside C# code), which I was not able to fulfil with my JS code. But again, JavaScript flexibility helped here and I introduced my own templating engine, which has all the functionality that I wanted. It was based on the JavaScript-Templates engine, which I modified a little bit to provide missing functionalities (e.g. referencing other templates is not supported in the original). It can work with any model that I create in my own JS code, including other templates, and process them. So I was able to pass the below model to each file’s generation process:

1
2
3
4
var _model = {
    model: data,
    meta: new Metadata()
};

`Metadata` keeps information about the plugin, authors, plugin release date, etc. Every template can reference `copyright` template to get a full license and copyright notes at the top of the file. Those notes were processed and filled with proper data taken from my `Metadata()` object:

1
2
3
4
{% include "copyright" %}
{% include "generation_details" %}
using System;
...

and the copyright.template file looks like this:

1
2
3
4
/******************************************************************************
 The MIT License (MIT)
 Copyright (c) {%# o.meta.getCopyrightYears() %} {%# o.meta.getAuthor() %} <{%# o.meta.getContactEmail() %}>
 ...

This automatically produces output files, with each of them starting with a license/note:

1
2
3
4
/******************************************************************************
 The MIT License (MIT)
 Copyright (c) 2016 Matchbox Mobile Limited <info@matchboxmobile.com>
 ...

Now I can easily update all headers for all the files, and make templates smaller and easier to maintain.

Future development

I managed to write the plugin, which generates a selection of C# source code files. After adding them into the UWP project, I was able to connect to the Heart Rate belt and receive notifications with measurements by only adding around 13 new lines of code – everything else is just standard Windows 10 code to initialize the app, setup the Page, etc. I have lots of ideas for further improvements of the generated code:

  • At this moment only read, write, and notifications are supported for characteristics.
  • I would like to add parsers for all adopted field formats (byte, boolean, string unicode/utf8 etc.)
  • I would like to add more parsers for adopted characteristics (now we have parser only for Heart Rate Measurement characteristic – as an example; developers can write their own parsers and use it with the base code, generated by the plugin).
  • More popular adopted Services could have dedicated, ready to use, subclass of `BleService` (like “Device Information”, “Battery Service” or “TX Power”.
  • I would like to run more tests and create an example application for more complex devices (like TI SensorTag)
  • Plugin’s could utilize more data from the profile (e.g. Summary or Abstract fields in the Bluetooth Developer Studio could be used to add some comments for particular Service/Characteristic inside the generated code).

CONCLUSION

I really enjoyed writing a plugin for Bluetooth Developer Studio. It is still a young product, but is absolutely production-ready. It can significantly improve your team performance and results if you have to work with any Bluetooth Smart device. You can visually design your device, generate the code and start working straight ahead on the device’s domain level and not on Bluetooth LE communication level. Newer versions of BDS will be more powerful and offer more functionality. For sure we will also see more and more plugins dedicated to many different tasks. At this moment you can generate code for Android and iOS apps, and for Windows 10 UWP apps you can now use the plugin I have just created. Enjoy!