15
Advanced BLE with Introduction The purpose of this workshop is to help you create custom services on Bluetooth Low Energy (BLE) by leveraging ARM® mbed™ tools. In this workshop, we will cover how to send custom GAP advertisements and how to create custom GATT services and characteristics. The prerequisites for this class are having a BLEenabled smart device (tablet or phone) and mbedenabled BLE board. We also assume that you have have a basic understanding of how BLE works. If you are not already familiar with these concepts or the mbed tools please use our previously posted docs: Intro to BLE with ARM mbed and Getting Started with mbed . The slides that accompany this document can be found here . Here is a permalink to the document in case you are viewing it offline: http://goo.gl/H3Iip6 Table of Contents: Introduction Apps Custom GAP Advertising Packet Review mbed BLE API Check Evothings Custom GATT Service and Characteristic Review mbed BLE API Check Evothings API

Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Embed Size (px)

Citation preview

Page 1: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Advanced BLE with

Introduction The purpose of this workshop is to help you create custom services on Bluetooth Low Energy (BLE) by leveraging ARM® mbed™ tools. In this workshop, we will cover how to send custom GAP advertisements and how to create custom GATT services and characteristics. The prerequisites for this class are having a BLE­enabled smart device (tablet or phone) and mbed­enabled BLE board. We also assume that you have have a basic understanding of how BLE works. If you are not already familiar with these concepts or the mbed tools please use our previously posted docs: Intro to BLE with ARM mbed and Getting Started with mbed. The slides that accompany this document can be found here. Here is a permalink to the document in case you are viewing it offline:

http://goo.gl/H3Iip6

Table of Contents: Introduction Apps Custom GAP Advertising Packet

Review mbed BLE API Check Evothings

Custom GATT Service and Characteristic

Review mbed BLE API Check Evothings API

Page 2: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Apps You will need the Evothings Workbench on your laptop and the following apps on your BLE­enabled smart device:

LightBlue

Evothings Client

nRF Master Control panel

Evothings Client

Custom GAP Advertising Packet Let’s start with something simple: we are going to modify the advertising data packet to broadcast some custom­formatted data. We’re going to do this by using the advertising data’s manufacturer­specific data field and encode our information into it. This type of transmission is useful because advertising does not require a connection to take place. Instead, a scanner would be able to read the information and interpret it appropriately on its side.

Review In a GAP instance the Advertising Data is broken down into the following blocks:

Page 3: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

We start with 31 bytes, then: 1. We need to add flags to tell the device about the type of advertisement we are sending

and those will take up three bytes total (one for length, one for type, one for data). 2. After that two more bytes are used for the header of the AD struct that contains the

data So our usable space goes from 31 to 28 to 26 bytes. This means we have 26B to use for the data we want to send over GAP.

mbed BLE API Let’s break down the program piece by piece. To import the project into the mbed compiler, follow the link here: http://developer.mbed.org/users/mbedAustin/code/BLE_EvothingsExample_GAP/ To start off, we will need the mbed header file and the header for the BLE devices:

#include "mbed.h"

#include "BLEDevice.h"

Declare the BLE object:

BLEDevice ble;

Provide the name of the device: const static char DEVICE_NAME[] = "ChangeMe!!"; // change this

We now have up to 26­bytes of data to customize: const static uint8_t AdvData[] = 0x01,0x02,0x03,0x04,0x05; // example of hex data

The important part is to not exceed the 26­bytes in the advertising header. We can put less but not more. We can also use character data instead of hex: const static uint8_t AdvData[] = "ChangeThisData"; // example of character data

Try both to see the difference!

Page 4: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Note: most BLE scanner programs will only display the hex representation of data, the characters may be displayed as the numbers that represent those letters. That concludes the setup. We now have to bring it all together in the main program. Start off by calling the initializer for the BLE baselayer: int main(void)

ble.init();

Note: The ble.init() should always be performed before doing any other BLE setup. Next we set up the advertising flags: ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED |

GapAdvertisingData::LE_GENERAL_DISCOVERABLE ); ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);

The second half sets the flag to put the advertisement in the general discoverable mode and the last flag sets the type of advertisement to be a connectable undirected advertisement. These are the flags that will cost us a total of three out of the 31 bytes. It is worth noting that the ADV_CONNECTABLE_UNDIRECTED flag could just as easily be ADV_NON_CONNECTABLE_UNDIRECTED if no connection is needed. We have chosen to use the connectable flag as some BLE apps will not display advertising data until a connection is established. We can then set up the payload. The header MANUFACTURER_SPECIFIC_DATA is the point where we lose another two bytes of data. Once the header has announced the data we plug in the array we created earlier: ble.accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData));

Notice the AdvData variable is added to the ble device at this point. Set advertising interval and start advertising: ble.setAdvertisingInterval(160); // 100ms; in multiples of 0.625ms. ble.startAdvertising();

This will take care of the GAP advertising on the mbed side.

Page 5: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Check

Load the program onto the board and bring up the nRF Master Control Panel app on

Android or the LightBlue app on iOS. It will automatically start scanning and we should see a screen like this:

The name is coming in as what it was set, the appropriate flags are set and the data we pushed into the manufacturer data is coming through. Similar information can be seen by the LightBlue app.

Page 6: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Evothings Go here to download the custom GAP application for Evothings: http://goo.gl/tJP5EY. Download the files and drag the index.html file into the Evothings Workbench.

Next make sure the Evothings app is running on the smartphone and connected to the Evothings workbench on a computer. Click RUN to run the code on the smartphone and watch for the custom data to be displayed!

Page 7: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

The code for the application is in the app.js file. It is written in javascript and can be modified in real time. Try making a modification to the app.js file, save the changes, and watch them load to the Evothings client on your phone! This demo is very simple but provides a starting point for more advanced programing.

Custom GATT Service and Characteristic Now let’s try a custom GATT service and characteristic to blink the LED on the mbed board. Unlike GAP, which operates on broadcasting one­to­many, GATT uses a one­to­one connection between the board and the phone. GATT does not send all the data at connection; it sends only a description of available services , and then ­ if requested ­ it will provide details about a service, like the characteristics each service has and the values of these characteristics. All information must be explicitly requested from the server by the client.

Page 8: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

To demonstrate this we will create a service with two characteristics and assign custom UUIDs to both the service and the characteristics.

Review A GATT server can have multiple services. Each service contains one or more characteristics, each with its own properties such as whether it can be read by the client or be written to by the client. Each characteristic has a single value of up to 512 bytes and can have zero or more descriptors. This can be seen below:

We are going to create a custom GATT service by providing two characteristics, one for reading and one for writing, detailing their properties accordingly, and then putting both into the one service.

mbed BLE API The mbed program can be found here: http://developer.mbed.org/users/mbedAustin/code/BLE_EvothingsExample_GATT/ To get started with the mbed side there are a couple of headers we will need:

Page 9: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

#include "mbed.h" #include "BLEDevice.h"

We will need a few declarations: a BLE object, the LED we will be turning on and off, the UUID for our custom service, a characteristic UUID for reading and a characteristic UUID for writing. We then provide a distinguishing name for our device that the application will be looking for. Finally, the UUID (unique identifier) needs to be declared. We chose 0xFFFF since it is designated for development instead of a particular service. BLEDevice ble; DigitalOut led(LED1); uint16_t customServiceUUID = 0xA000; //⇐ service UUID uint16_t reachCharUUID = 0xA001; //⇐ read characteristic UUID uint16_t writeCharUUID = 0xA002; //⇐ write Characteristic UUID const static char DEVICE_NAME[] = "ChangeMe!!"; // change this static const uint16_t uuid16_list[] = 0xFFFF; // Custom UUID, FF is reserved for development

Note: if we change the name here we will also need to change it in the subsequent Evothings application app.js (which will be covered later). Now that we have the UUIDs declared, set up the characteristics:

1. Start off by declaring the array variable for the read value uint8_t readValue[10]. 2. Next, declare the read only characteristic (ReadOnlyArrayGattCharacteristic). 3. Provide the initializer describing the type of the array and the number of elements in

the array (<uint8_t, sizeof(readValue)>). 4. The characteristic will be called “readChar” and initialized with the UUID variable for the

read characteristic readCharUUID and the pointer to the array that was just created (readValue) .

5. The same is done for the write characteristic, “writeValue”. This is what it looks like: static uint8_t readValue[10] = 0; ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(readValue)> readChar(readCharUUID, readValue); static uint8_t writeValue[10] = 0; WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(writeValue)> writeChar(writeCharUUID, writeValue);

Now that both of the characteristics have been defined, we can move on to the custom service:

Page 10: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

1. Initialize a GATT service by filling the characteristics array with references to the read and write characteristics.

2. Declare the GATT service 3. The deceleration includes the UUID, the characteristics array, and the number of

characteristics included. GattCharacteristic *characteristics[] = &readChar, &writeChar; GattService customService(customServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

We've established the service; we can now create other functions. First, since GATT is connection­based, we need a disconnection callback function. This functions restarts advertising after a disconnect occurs, so a device can find and reconnect to a lost board. If we don't include this function, we'll have to restart the board to be able to reconnect to it: void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) ble.startAdvertising();

Next we need to create the write callback function so it can be called when the BLE board is written to. This callback makes sure the write operation triggering the callback (params->charHandle) is a write operation to the write characteristic (writeChar.getValueHandle()). This is not necessary when we only have one write characteristic, but is absolutely necessary when we have multiple ones on a device. The remainder of the code will print out the data written to the write characteristic: void writeCharCallback(const GattCharacteristicWriteCBParams *params) // check to see what characteristic was written, by handle if(params->charHandle == writeChar.getValueHandle()) // toggle LED if only 1 byte is written if(params->len == 1) led = params->data[0]; // print led toggle (params->data[0] == 0x00) ? printf("\n\rled on ") : printf("\n\rled off "); // print the data if more than 1 byte is written else printf("\n\r Data received: length = %d, data = 0x",params->len); for(int x=0; x < params->len; x++) printf("%x",params->data[x]); // update the readChar with the value of writeChar ble.updateCharacteristicValue(readChar.getValueHandle(),params->data,params->len);

Page 11: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Start the main loop and initialize the BLE baselayer: int main(void) printf("\n\r********* Starting Main Loop *********\n\r"); ble.init();

Once that is done, set up the disconnection callback so that it is called if there is a disconnection and the write character callback when data is written: ble.onDisconnection(disconnectionCallback); ble.onDataWritten(writeCharCallback);

Now we have to set up the advertising parameters:

1. First we set the flag that this advertising message is BLE only. 2. We then set the advertising type as connectable and undirected. 3. The payload can now be propagated with the chosen device name and the service’s

UUIDs list. 4. Finally the advertising interval, in multiples of .625 ms (the time interval Bluetooth uses

to send single packets), needs to be established. ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); // BLE only, no classic BT ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // advertising type ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t

*)DEVICE_NAME, sizeof(DEVICE_NAME)); // add name ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t

*) uuid16_list, sizeof(uuid16_list)); // UUIDs in advertising packet

ble.setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */

Add in the custom service: ble.addService(customService); // Add our custom service to device

Now that everything is set up we can start advertising the connection: ble.startAdvertising(); // start advertising // infinite loop waiting for BLE interrupt events while (true) ble.waitForEvent(); //Save power

Page 12: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

// end of mainloop

Once we have set up everything as we like it, compile it and program the board.

Check

Use the nRF Master Control Panel app on Android or the LightBlue app on iOS to see if the Service and Characteristic UUIDs are being set as expected:

Once we can see the service and its characteristics try writing a single byte of 0x00 or 0x01 to the write characteristic to see the LED flash on and off. Next try opening up the console connected to the board and see the debug messages print out:

1. When we toggle the LED it should print out the status of the LED.

Page 13: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

2. When we write more than one byte it should print out the hex representation of the data we send.

Keep in mind we can send no more than 10 bytes, unless we increase the size of the characteristic.

Evothings API The service we created and put on our board is interactive: we can read the LED’s status and change it. We do that using a custom­built app, designed to work on the Evothings client that was installed earlier. The Evothings app code can be found here: https://github.com/BlackstoneEngineering/evothings­examples/tree/development/experiments/mbed­Evothings­CustomGATT As before, drag the index.html file into the Evothings workbench:

We can open the code with a variety of applications; in this case Notepad++ was utilized to view and edit.When the code is open in an editor, change the MyDeviceName variable in the app.js file to match the device name given to the mbed board:

// JavaScript code for the mbed ble scan app // Short name for EasyBLE library. var easyble = evothings.easyble;

Page 14: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

// Name of device to connect to var MyDeviceName = "ChangeMe!!"

Make sure to walk through the code to check that everything makes sense. On startup, the app will begin searching for the device with the set name. When it connects the message will change from “connecting” to “connected” and the toggle button will change to green. If we press the button, it will switch to red and LED1 on the board will turn on. The snippet that controls the toggle function can be seen below:

app.toggle = function()

// console.log(GDevice.__services[2].__characteristics[0]['uuid']) GDevice.readCharacteristic(

"0000a001-0000-1000-8000-00805f9b34fb", function(win)

var view = new Uint8Array(win) var led = new Uint8Array(1) if(view[0] == ledON)

$('#toggle').removeClass('green') $('#toggle').addClass('red') led[0] = ledOFF;

else if (view[0] == ledOFF)

$('#toggle').removeClass('red') $('#toggle').addClass('green') led[0] = ledON;

GDevice.writeCharacteristic(

'0000a002-0000-1000-8000-00805f9b34fb', led, function(win)console.log("led toggled successfully!"), function(fail)console.log("led toggle failed: "+fail))

, function(fail)

console.log("read char fail: "+fail);

);

Page 15: Advanced BLE with - Arm Community · PDF fileWe now have up to 26­bytes of data ... Try making a modification to the app.js file, save the changes ... functions restarts advertising

Once we are ready we can run the program on a smartphone.

App is scanning for the device.

App is connected and LED is off.

App is connected and LED is on.

It may take a moment for the app to find the board. Once the connection is confirmed, press the toggle to see if the LED turns on. Below is a animation of what the board should look like when repeatedly pressing the toggle button on the app.