IntroductionI was given a Bluegiga DKBLE development board by Bluegiga and have recently been using it in support of some work I'm doing. I hadn't used a Bluegiga board before and so had to learn how to use it and I thought I could share my experience and the things I learned here.
The kit consists of a main DKBLE board and several "carrier boards" which can be plugged into the main board. These are small boards containing a particular Bluegiga module. There are 4 in total; 1 x BLE112-A, 1 x BLE113-A, 1 x BLE113A-M256K and 1 x BLE121LR-A-M256. The latter is Bluegiga's long range module which their web site says has a range of up to 450 metres. Pretty impressive.
The main board has pins at one edge which allow one carrier board to be plugged into it. It also includes an LCD display, a temperature sensor, an accelerometer, an altimeter, a potentiometer and a USB to UART converter.
A Bluegiga project consists of a number of different files, one of which is the project configuration file. It has an extension of ".bgproj" and defines the target hardware and the names of other key project files, such as the name of the XML file which defines the GATT services, the name of the hardware definition file, the name of the primary bgscript file and the name of the output image file to be created by the compiler.
<?xml version="1.0" encoding="UTF-8" ?> <project> <gatt in="gatt.xml" /> <hardware in="hardware.xml" /> <script in="course_project.bgs" /> <image out="out-ble113.hex" /> <device type="ble113" /> <boot fw="bootuart" /> </project>
Defining GATT Services
This is a straightforward task and similar to other boards I've worked with from Nordic Semiconductor and CSR for example, in that it involves using XML to describe services, characteristics and descriptors in a logical, intuitive way. There's no GUI to help with this so your tool is your favourite text editor. There are lots of examples that come with the SDK so it's easy to get started.
I had a custom profile I wanted to implement which included 4 services. The first was an adopted service called the Battery Service. The other three were all custom services; the counter service and random number service are those I used for my Texas Instruments SensorTag hacking and the Personal Naming service was a new one I came up with which allows you to give a custom name to your device. Yes, a "pet name" is what I had in mind :-)
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <service uuid="1800"> <description>Generic Access Profile</description> <characteristic uuid="2a00"> <properties read="true" const="true" /> <value>Innovation Series Device</value> </characteristic> <!-- APPEARANCE = unknown --> <characteristic uuid="2a01"> <properties read="true" const="true" /> <value type="hex">0000</value> </characteristic> </service> <!-- Battery Service --> <service uuid="180f"> <description>Battery</description> <characteristic uuid="2a19" id="xgatt_battery"> <properties read="true" /> <value length="1" type="user" /> </characteristic> </service> <!-- Counter Service --> <service uuid="3E099914-293F-11E4-93BD-AFD0FE6D1DFD" advertise="true"> <description>Counter</description> <!-- Counter --> <characteristic uuid="3E099915-293F-11E4-93BD-AFD0FE6D1DFD" id="xgatt_counter"> <properties read="true" write="true"/> <value length="1" type="user" /> </characteristic> </service> <!-- Random Number Service --> <service uuid="3E099916-293F-11E4-93BD-AFD0FE6D1DFD" advertise="true"> <description>Counter</description> <!-- Random Number --> <characteristic uuid="3E099917-293F-11E4-93BD-AFD0FE6D1DFD" id="xgatt_random"> <properties read="true" notify="true"/> <value length="2" type="user" /> </characteristic> </service> <!-- Personal Naming Service --> <service uuid="3E099918-293F-11E4-93BD-AFD0FE6D1DFD" advertise="true"> <description>Counter</description> <!-- Personal Name --> <characteristic uuid="3E099919-293F-11E4-93BD-AFD0FE6D1DFD" id="xgatt_personal_name"> <properties read="true" write="true"/> <value variable_length="true" length="20"/> </characteristic> </service> </configuration>
The id attribute defines a name for the GATT attribute which can be used to reference it from code. The file attributes.txt maps these ID values to GATT handle values.
xgatt_battery 8 xgatt_counter 11 xgatt_random 14 xgatt_personal_name 18
Developing for the Bluegiga DKBLE
The first thing that strikes you as different when it comes to developing for this board is the programming language. Their API does allow C to be used but in addition, Bluegiga have their own scripting language called bgscript. I liked working with bgscript. It's reminiscent of the BASIC programming language, the first language I ever learned, over 30 years ago! It's pretty quick to develop with as well.
Coding for Bluegiga involves implementing event handlers which receive call backs from the system, generating events by making API calls and writing and calling your own custom procedures, which are like functions which cannot return a result (i.e. they're similar to functions which have a return type of void). When you call a system API, you generally (perhaps always) receive a call back once your event has been handled, so there's a symmetrical request/response pattern working with the event system.
If all you want to do is expose simple characteristics whose values can be read or written to, you don't need to do much coding. The characteristic values lives in the device's attribute table and the framework gives access to it without you having to write any bgscript. If reading or writing to a characteristic requires more processing than this though, then you must implement code inside the appropriate event handler. This was the case for my counter service. Reading from the counter value characteristic required the value to be incremented before returning it to the GATT client. As such, I had to mark the characteristic as having a type of "user" in the gatt.xml file.
<!-- Counter Service --> <service uuid="3E099914-293F-11E4-93BD-AFD0FE6D1DFD" advertise="true"> <description>Counter</description> <!-- Counter --> <characteristic uuid="3E099915-293F-11E4-93BD-AFD0FE6D1DFD" id="xgatt_counter"> <properties read="true" write="true"/> <value length="1" type="user" /> </characteristic> </service>
Requests to read characteristics of type user result in system calls to the event handler called attributes_user_read_request. A simplified version of my implementation appears next:
# Listen for GATT read events event attributes_user_read_request(connection, handle, offset, maxsize) if handle = xgatt_counter then call attributes_user_read_response(connection, 0, 1, counter) counter = counter + 1 if counter > 255 counter = 0 end if return end if if handle = xgatt_random then # swap from little endian (Bluegiga internal format) to big endian for GATT random_be(0:1) = random(1:1) random_be(1:1) = random(0:1) call attributes_user_read_response(connection, 0, 2, random_be(0:2)) return end if if handle = xgatt_battery then batconn_handle=connection #start measurement, read VDD/3, 9 effective bits call hardware_adc_read(15,3,0) return end if end
Note that bgscript supports simple variables and types of variable known as buffers. These are essentially byte arrays and they're addressed using name(start:length) notation.
After dealing with a simple read request, you call attributes_user_read_response which causes the attribute protocol response to be returned to the client.
In the case of the Battery Service, this requires interaction with the hardware APIs and in this case a call to hardware_adc_read. This in turn, causes a callback to an event handler called hardware_adc_result once the operation has completed. My full source code appears below.
The Random Number service generates a 16 bit random number when its characteristic is read and generates and returns a random number once every second as a GATT notification whenever notifications are enabled. The bgscript has no random number function so I coded my own implementation of a simple Linear Congruential Generator algorithm, which is the basis of the random number function in a lot of programming languages. See http://en.wikipedia.org/wiki/Linear_congruential_generator .
# Random number generation export dim rseed(4) export dim m export dim a export dim c dim x dim r dim mod_result procedure mod(number, divisor) x = number / divisor mod_result = (number - (x * divisor)) end procedure genRandom() # Xn+1 = (aXn + c) mod m r = (a * rseed(0:4) + c) call mod(r,m) rseed(0:4) = mod_result end
To seed the random number sequence I used a temperature reading from the board's temperature sensor.
I used a timer to generate a fresh random number each second and write it to the GATT attribute table. If the client had enabled notifications then the framework automatically generated a notification message with the new value in it. I didn't have to explicitly code this which I rather liked.
event hardware_soft_timer(handle) call genRandom() random(0:2) = rseed(0:2) # swap from little endian (Bluegiga internal format) to big endian for GATT random_be(0:1) = random(1:1) random_be(1:1) = random(0:1) # update characteristic value (will push data if client has subscribed to notifications/indications) call attributes_write(xgatt_random, 0, 2, random_be(0:2)) end
The only thing I didn't like about the Bluegiga SDK was how difficult it seemed to be to use the LCD display. The LCD is really useful and I had it displaying the random numbers my timer was generating once per second and displaying a visual acknowledgement of GATT operations such as read requests. This was very useful and reassuring to see during development. But the API is really low level and I can't help but wish a set of higher level wrapper functions had been made available to bgscript to make this easier. A minor point given how great the kit is in every other way.
I have a simple Android client application which I used with the board. It shows the random number service working very nicely!
My full project can be downloaded from here, possible/probably bugs and all. This is yours for educational purposes. No responsibility is accepted for anything you find to be wrong :-)