Advanced BLE with Table of Contents: Introduction Apps The purpose of this workshop is to help you create Custom GAP Advertising Packet custom services on Bluetooth Low Energy (BLE) by Review leveraging ARM® mbed™ tools. mbed BLE API Check In this workshop, we will cover how to send custom GAP Evothings advertisements and how to create custom GATT Custom GATT Service and services and characteristics. Characteristic Review The prerequisites for this class are having a mbed BLE API BLEenabled smart device (tablet or phone) and Check mbedenabled BLE board. Evothings API 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: Introduction http://goo.gl/H3Iip6 Apps You will need the Evothings Workbench on your laptop and the following apps on your BLEenabled 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 customformatted data. We’re going to do this by using the advertising data’s manufacturerspecific 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: 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: BLEDeviceble; Provide the name of the device: const static char DEVICE_NAME [] = "ChangeMe!!" ; // change this We now have up to 26bytes 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 26bytes 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" }; Try both to see the difference! // example of character data 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: intmain ( 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. Check Load the program onto the board and bring up the Android or the nRF Master Control Panel app on 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. 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! 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 onetomany, GATT uses a onetoone 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. 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: #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. BLEDeviceble; DigitalOutled ( LED1 ); uint16_tcustomServiceUUID = 0xA000 ; //⇐ service UUID uint16_treachCharUUID = 0xA001 ; //⇐ read characteristic UUID uint16_twriteCharUUID = 0xA002 ; //⇐ write Characteristic UUID const static char DEVICE_NAME [] = "ChangeMe!!" ; // change this static const uint16_tuuid16_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_treadValue [ 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_treadValue [ 10 ] = { 0 }; ReadOnlyArrayGattCharacteristic < uint8_t , sizeof ( readValue )>readChar ( readCharUUID ,readValue ); static uint8_twriteValue [ 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: 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 connectionbased, 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: voiddisconnectionCallback ( Gap :: Handle_thandle , Gap :: DisconnectionReason_treason) { 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: voidwriteCharCallback ( 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 ( intx = 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 ); } } Start the main loop and initialize the BLE baselayer: intmain ( 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 } } // 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. 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 custombuilt app, designed to work on the Evothings client that was installed earlier. The Evothings app code can be found here: https://github.com/BlackstoneEngineering/evothingsexamples/tree/development/experiments/ mbedEvothingsCustomGATT 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. vareasyble =evothings . easyble; // 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 ){ varview = new Uint8Array ( win) varled = 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 ); } ); } 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.
© Copyright 2024