Tuesday, December 30, 2014

Integrating an Android smartphone application with the BLED112 module

My conclusion with the RFDuino adventures was that RFDuino is a perfect platform to start familiarizing with the Bluetooth Low Energy (BLE) technology. BLE programming was made so simple with RFDuino that it provides quick success. Simplification comes with limitations, however, and eventually time has come for me to step further toward a more flexible BLE platform. Bluegiga's BLE121LR long range module seems to have outstanding range but first I tried a piece of hardware that is equivalent from the API point of view with the BLE121LR but is easier to start with and that is Bluegiga's BLED112 USB dongle.


The BLED112 implements the same API (called BGAPI, check out Bluetooth Smart Software API reference) that other Bluegiga BLE modules do but there's no need to buy the pricey DKBLE development board or build any hardware. It plugs neatly into the USB port and is functional without any additional piece of hardware. From the serious BLE application development perspective it has drawbacks too. Firstly, its USB interface is drawing a constant 5mA current so this solution is not very much "low energy". Second disadvantage is that its single USB interface is shared between the BGAPI API and the programming interface so installing scripts into the BLED112 is a risky enterprise. If the script running on the BLED112 occupies the USB port, there's no way to update it so the module is essentially bricked. Hence in this exercise we will keep the BLE application logic on the PC hosting the module and talk to the module with BGAPI. This is very similar setup when the application logic is running on a microcontroller or embedded PC.

Click here to download the Android client and the PC server example programs.

In this exercise, we will implement the Current Time Service (CTS) and access this service from an Android application. CTS is a standard Bluetooth service. The PC application will fetch the current time from its clock and will update the characteristic exposed by the BLED112. The Android application will detect the advertised CTS service, connect to it, retrieve the time and display it to the user. The Android application will also subscribe to time changes demonstrating the notification feature of BLE GATT.

Let's start with the PC part. Unpack the cts_example.zip file and inside there are a set of C files belonging to the PC application in the root directory. I developed and tested the application on Ubuntu 14.10 so if you use a similar system, you have good chances that you just type "make" and it will compile. Preparing the BLED112 dongle is more complicated, however and this is the result of the quite cumbersome Bluegiga tool chain. Any change to the GATT services (read this presentation if you don't know what GATT is) requires a firmware update of the BLED112. This sounds scary but it is not too complicated if you have a Windows system. Bluegiga SDK supports only Windows and there is one element of the tool chain, the firmware downloader that does not run on emulated Windows either - you need the real thing. So the steps are the following:

  • Grab a Windows machine, download the Bluegiga SDK and install it.
  • Get the content of the config subdirectory in cts_example.zip and copy somewhere in the Windows directory system. Then generate the new firmware with the <bluegigasdk_install_location>\bin\bgbuild.exe cts_gattBLED112_project.bgproj command. The output will be the cts_BLED112.hex file which is the new firmware. We could have placed application logic into the firmware with a script but as I said, it is a bit risky with the BLED112 so this time the new firmware contains only the GATT database for the CTS service.
  • Launch the BLE GUI application, select the BLED112 port and try to connect by clicking the "Attach" button. If all goes well, you will see green "Connected" message. Then select Commands/DFU menu item, select the HEX file we have just generated, click on the "Boot into DFU mode" button. One pecularity of the BLED112 that in DFU mode it becomes logically another USB device so the main window will display red "Disconnected" message.  Then click "Upload". If the upload counter reaches 100% and you see the "Finished" message, the firmware update is done.
At this point we are finished with Windows and can start the serious business. Plug the dongle into the Ubuntu machine and check out its port.

dmesg | tail
...
usb 3-2: Product: Low Energy Dongle
usb 3-2: Manufacturer: Bluegiga
usb 3-2: SerialNumber: 1
cdc_acm 3-2:1.0: ttyACM0: USB ACM device


So this time the dongle is mapped to /dev/ttyACM0. Launch the BLE server with the following command:


./conn_example /dev/ttyACM0


The server is ready, let's see the Android client. Import the Android project in CTS.zip into Android Studio. Note that I am still baffled by the fact that this shiny new IDE does not have a project export command so I had to zip part of the project's directory tree manually. Once you launch the Android application, you will see a screen like this:




The device named "test" is our device and the CTS service is identified by the UUID of 0x1805. The other entry is just another BLE device that I threw in for demonstration. Click on the device name and you get the time emitted by the BLE dongle:


The current time is also updated every second demonstrating that the client successfully subscribed to the changes.





On the server side, it is important to note that the BGAPI protocol is defined in terms of byte arrays sent and received over the serial port (which is mapped to USB in the case of BLED112). The BGAPI support library coming from Bluegiga that I used in this demo is just a wrapper over this interface so it can be replaced with an optimized implementation if the library is too heavy for the application platform (e.g. for a microcontroller) or is not implemented in the desired language (e.g. in Python). On the Android client side, it is interesting to note how the BLE advertisement parser library I presented in this post is used to figure out, whether the device advertises the CTS service we are interested in.

Friday, December 19, 2014

Parsing BLE advertisement packets

Ever since I created the Gas Sensor demo (post here, video here, presentation here), I had the feeling of an unfinished business. That demo sent the sensor data in BLE advertisement packets so the client never connected to the sensor but received data from the sensor in a broadcast-like fashion. The implementation looked like this:

        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            String deviceName = device.getName();
...
                int addDataOffs = deviceName.length() + 16;
                int siteid = ((int)scanRecord[addDataOffs]) & 0xFF;
                int ad1 = ((int)scanRecord[addDataOffs+1]) & 0xFF;

This was a quick & dirty solution that remained there from my earliest prototypes. It sort of assumes that the structure of the BLE advertisement packet is fixed so the sensor data can always be found at fixed locations of the advertisement packet. This does not have to be the case, Bluetooth 4.0 Core Specification, Part C, Appendix C (or Core Specification Supplement in case of 4.2 version) describes, how the fields of the advertisement packets look like. It just so happens that with the given version of the RFDuino BLE module, the Manufacturer Specific Data field where RFDuino puts the user data for the advertisement packet can always be found at a specific location.

The proper way is of course to parse this data format according to the referred appendix of the specification and in this post I will show you how I implemented it.

Here are the three example programs mentioned in this post: blescan.zip, gassensordemo.zip, gas_adv.ino

Let's see first the BLEScan project.

Update: the project has been updated to support more of the 4.2 elements. It has also been converted into an Android Studio project but the download material contains only the app/src part of the tree.

Update: I was asked by e-mail, how to import the project (in blescan.zip) into Android Studio. Here is a simple process.
  • Create a new project in Android Studio under any name. Make sure that your project supports at least API level 18. Choose the "create no Activity" option.
  • Once your project is created, go and find it on the disk. On my Ubuntu system, the project files go under ~/StudioProjects/<ProjectName> where <ProjectName> is the name you gave to your project. We will call this directory <ProjectDir>.
  • Go into <ProjectDir>/app/src and delete everything there. Copy blescan.zip into <ProjectDir>/app/src and unzip it. It will create a single directory called "main" and the sources below.
  • In Android Studio, do File/Synchronize. After that is completed, you can open your project files, build APK, etc.

The parser code is under the hu.uw.pallergabor.ble.adparser package. Then you just give the scanRecord array to AdParser's parseAdData method like this:

            ArrayList<AdElement> ads = AdParser.parseAdData(scanRecord);

and then you get an array of objects, each describing an element in the scan record. These objects can also produce printable representation like this:



Now let's see the revised GasSensorDemo project, how the gas sensor measurement is properly parsed out of the scan record. First we parse the scan packet fields:

                ArrayList<AdElement> ads = AdParser.parseAdData(scanRecord);

Then we look for a TypeManufacturerData element which corresponds to a Manufacturer Specific Data field in BLE. We make an extra check to make sure that the manufacturer field in the Manufacturer Specific Data is 0x0000 because RFDuino always creates a Manufacturer Specific Data field like that if the application programmer specifies additional advertisement data.

                    AdElement e = ads.get(i);
                    if( e instanceof TypeManufacturerData ) {
                        TypeManufacturerData em = (TypeManufacturerData)e;
                        if( em.getManufacturer() == 0x0000) {


It would be tempting to use a custom manufacturer field or better, a Service Data field. But then we run into another limitation of RFDuino because RFDuino with its default firmware is only able to create advertisement packets like in the previous example. This is not bad because it allows the programmer to achieve quick success but later on, we will need more flexibility and that will need another BLE module.