Bluetooth Low Energy (a.k.a Bluetooth LE, BTLE, Bluetooth Smart)

Gatt Client support

05 Jun 2015

Hello Community,

We now have experimental support for GattClient. I'd like your feedback, and this will most likely bake for a while before we push it to mbed.org. But you can begin using it.

Here's an initial demo: https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LEDBlinker/

It won't build on mbed.org. But you can combine it with the following to brew something useful: BLE_API https://github.com/mbedmicro/BLE_API/tree/gattClient nRF51822 https://github.com/mbedmicro/nRF51822/tree/gattClient

I'm sorry for making you use offline builds to combine these.

The demo drives an LED service exported by a BLE_LED peripheral. It demonstrates scanning, connections, service-discovery, and reads/writes.

Looking forward to your input.

05 Jun 2015

For those who are brave enough to get this working, please pay attention to the following which needs to be altered based on your setup:

if (params->peerAddr[0] != 0x29) { /* !ALERT! Alter this filter to suit your device. */
06 Jun 2015

Awesome, Rohit - thank you! Will probably play with this over the next week or two, and will give feedback as I can. Much appreciated!

14 Jun 2015

I manage to compile this without issues and the advertisementCallback is executed when my device is advertising. Also, the ble.connect() returns BLE_ERROR_NONE and my device stops advertising. Then nothing happens, the led keeps blinking.

I am thinking I might have some issues on the service side. Can you please point me to a mbed example of the LED service I can use?

Also, if I would like to adjust this example for some other service, can you please point me in the right direction.

Thanks, /Lars

14 Jun 2015

Hi Lars,

Thanks so much for trying this example. You can use https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LED/ for the peripheral.

If you are targeting a peripheral other than the standard BLE_LED, you might want to launch a complete service discovery at the start to discover all services:

void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
    if (params->role == Gap::CENTRAL) {
        ble.onServiceDiscoveryTermination(discoveryTerminationCallback);
        ble.launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback);
    }
}

The above uses wildcards for serviceUUID and charUUID in the call to launchServiceDiscovery(). You could also discover the services of the device using a phone app like Nordic Master Control Panel (Android) or Light Blue (iOS).

Once you know the service/characteristic(s) you're targetting, you can refine your service discovery parameters like I did in the following:

void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
    if (params->role == Gap::CENTRAL) {
        ble.onServiceDiscoveryTermination(discoveryTerminationCallback);
        ble.launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, 0xa000, 0xa001);
    }
}

You've got both the central and the peripheral side to play with; i'm assuming and hoping that you've got two mbed devices. Add printfs and explore how service-discovery proceeds. :)

I'm glad you're trying the example. Thanks.

15 Jun 2015

Hi Rohit,

Thank you for pointing me to the BLE_LED example. With this example on one mbed peripheral device and the BLE_LEDBlinker on the central it worked directly, no issues at all.

I have a BLE peripheral device using a CC2540 chip that I would like to communicate with. I tried the Light Blue and it works great. I manage to read and write to the peripheral without problems, both read and write.

However, when I try to connect to this device using the BLE_LEDBlinker example I get to the point where the device is discovered but then it stops. The 'connectionCallback()' is never executed even though the ble.connect(params->peerAddr) returns BLE_ERROR_NONE. I can't figure out why, or where it gets stuck.

From your example above I understand it as after the connection is complete the connectionCallback should be executed, to launch the service discovery. In my case I don't get that far. The following is what I get from the BLE_Observer example you posted in May (with DUMP_ADV_DATA enabled).

BLE_Observer:

Adv peerAddr: [88 33 14 56 55 bf] rssi -69, ScanResp: 0, AdvType: 0
02 01 06 03 02 f0 ff 11 06 f2 c3 f0 ae a9 fa 15 8c 9d 49 ae 73 71 0a 81 e7

Thanks, /Lars

16 Jun 2015

Hi Lars,

Could it be that the device (using the CC2540 chip) is already connected with some other central at the time you're trying to connect to it from the modified BLE_LEDBlinker? Are you sure you're handling addresses correctly in ble.connect(); BLE addresses are in LSB format?

16 Jun 2015

Hi Rohit,

As I understand it my device advertise itself every time It powers up. Ones it is connected the advertisement stop and it holds the connection until the power is lost. I have no problem connecting it to two different iphones using the Light Blue application by performing a power-cycle on the device.

I will look into the address handling. Although, why would the MSB/LSB format differ between my device and the mbed device running BLE_LED?

Is the connect() function part of your new contributions, or does that exist with the existing API? I am thinking that the service I have might not be supported by the mbed BLE implementation. However I can see in Light Blue that one of the services is the Device Information Service, that I assume is supported, right?

My conclusion so far is that something happens after or inside ble.connect(params->peerAddr). Since my device stops advertising there must be some information exchange. However, the onConnection call back is never executed in my cental mbed device.

Can possibly some information from LightBlue help in figuring out the problem?

16 Jun 2015

Lars,

According to BLE4.0, a peripheral device stops advertising once it is connected to; this restriction has been removed in the later versions of the spec. Upon disconnection, the peripheral application needs to re-start advertisements before further connections can be made; perhaps the application on your particular device doesn't do that.

Address handling behaviour won't be different between the BLE_LED peripheral and any other peripheral device.

connect() is part of the experimental Gatt Client support; and will soon be put into the mainline BLE_API. Can you try to connect to your peripheral from the modified BLE_LEDBlinker soon after it (your peripheral) restarts? Don't involve lightBlue. I suspect your device doesn't restart advertisements upon a disconnection.

17 Jun 2015

When looking more into this I don't think my problem is related to mbed or your example, but rather on the stack implementation on of the two chips. I'll go back to the basics with the nordic SDK and make it work there, before I come back to mbed again.

Anyhow, I think you have a great axample and it works perfect for me when using two nrf51822 chips.

Thanks for the support! /Lars

17 Jun 2015

Lars, and community,

Can you think of ways to improve the API around GATT Client? The current example looks dense with code even when it does a conceptually simple thing. Can anything be done to simplify the user's exposure to GATT client? Should there be an abstraction to manage connections? How much of the internals and complexity of BLE is necessary to create a useful demo where two devices interact?

Your input would help us design better APIs.

Thanks.

18 Jun 2015

Hi, Rohit - I have not had time to play a lot with the GATT Client code, but connection management, IMHO, is very important.

One of the key differences between GATT client and server is the ability to manage the connection. I know the Nordic SD has an API to initiate disconnect from the server side... but being able to see a "mesh" of peripheral servers and connect and poll them would be very handy.

For example, think of a mobile roving "sensor net". The client would greatly benefit by being able to manage its connections, esp since the s130 can only handle 3 concurrent connection (the s120 can do 8, I believe, while the s110 can only handle one, AFAIK).

Cheers, -Andrew.

19 Jun 2015

Thanks Andrew. Your feedback is valuable. I'll start thinking of APIs for a connection manager abstration to be built on top of GATT Client APIs. I'll add abstract APIs to DiscoveredCharacteristic and DiscoveredService to serialize them to allow storage and recovery across bonded connections. This could be fun.

22 Jun 2015

Hi Rohit, I got some good news.

It appears that the default address type is set to be Gap::ADDR_TYPE_RANDOM_STATIC in BLEDevice::connect() and that does not work with my device. My knowledge of this is limited and therefore I can't have an opinion about what the default behavior should be. Also, I don't know why this does not work with my device while it works flawless with the BLE_LED example.

When I use the following arguments when setting up the connection it works as expected. ble.connect(params->peerAddr, Gap::ADDR_TYPE_PUBLIC)

Thanks, /Lars

22 Jun 2015

This is very useful to know, Lars. Thanks for sharing this, and for the analysis. I'll be away for a few days, but I'll ask my contacts at Nordic about this when I get a chance. I'm glad you're able to make progress.

04 Jul 2015

Hi ,

Guys could you please let me know how to receive notifications with this code?

Thanks and Regards, Neil

04 Jul 2015

You'll need to write to the target characteristic's CCCD attribute directly for now. This part hasn't been implemented using a clean API. Perhaps you can provide a useful implementation and submit a pull request.

04 Jul 2015

Hi Rohit,

Thanks for your comment :)

I am not familiar with how to write to the target characteristic's CCCD attribute. Could you please provide me any reference mbed/other code or sources to help me construct the code ?

Thanks and Regards, Neil Carvalho

05 Jul 2015

Neil,

Apologies for my previous handwaving; and thanks for driving me to a response.

I'll give you a quick answer as a start. This might help you in case you're hoping to get something working in short order.

The CCCD attribute handle is very likely going to be the characteristic's valueHandle + 1. This isn't a guarantee, but would most likely be the case. So you can use GattClient::write(char.getValueHandle() + 1, ...) and get things going for now. Please keep in mind that CCCD is a uint16_t bitmap. This is not the proper or portable solution; for that please read on.

It could be that what's needed here is a simple API like: DiscoveredCharacteristic::enableNotification(), but then that would allow manipulation of only the CCCD. There may be other writable descriptors, which would then require a more generic API.

There's an API meant to discover descriptors for a Characteristic. https://github.com/mbedmicro/BLE_API/blob/master/ble/DiscoveredCharacteristic.h#L117

Unfortunately, I haven't implemented it for the nRF51 yet. The default implementation remains in effect: https://github.com/mbedmicro/BLE_API/blob/master/source/DiscoveredCharacteristic.cpp#L63

My thinking is that launching descriptor discovery should result in callbacks passing the DiscoveredDescriptors (which is a datatype which needs to be added to BLE_API). I'm not sure how best to handle the discovered descriptors following the callback. Perhaps they should be contained within the owning characteristic, or perhaps the user would issue reads/writes directly upon the discovered descriptors.

I'll be tied up with other things for the coming days/weeks. It would be lovely if the community comes together and proposes a solution (or even a pull request).

thanks.

05 Jul 2015

Hi Rohit,

Thanks for your mail.

I have tried the rough method "The CCCD attribute handle is very likely going to be the characteristic's valueHandle + 1. This isn't a guarantee, but would most likely be the case. So you can use GattClient::write(char.getValueHandle() + 1, ...) and get things going for now. Please keep in mind that CCCD is a uint16_t bitmap." But I havent been able to achieve any success.

What I am currently doing is, trying to use the below function:

gattc->write(GattClient::GATT_OP_WRITE_REQ, connHandle, valueHandle, length, value);

to write a value of 0x0100 to enable notifications. I have tried both 0x100(something I used with bluez) and 0x01 (https://www.safaribooksonline.com/library/view/Getting+Started+with+Bluetooth+Low+Energy/9781491900550/ch04.html#gatt_feat_si) but both didnt work.

I am changing the valueHandle in this function to point to +1

I know your very busy but if you have time could you please let me know if I am going wrong somewhere?

Thanks and Regards, Neil

06 Jul 2015

The above discussion started by Neil is now being tracked by https://github.com/mbedmicro/BLE_API/issues/53 and https://github.com/mbedmicro/nRF51822/issues/22.

There is a separate issue to track enabling of notifications: https://github.com/mbedmicro/BLE_API/issues/54

06 Jul 2015

A new demo has been added to demonstrate the enabling of notifications. https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_ButtonSense/

07 Jul 2015

Hi Rohit,

Thanks a lot

07 Jul 2015

You're welcome, Neil. Thanks for test driving the APIs. Feedback from users contributes significantly towards our progress.

07 Jul 2015

The API for enabling notifications is still raw. We'd like input from the community for https://github.com/mbedmicro/BLE_API/issues/54.

07 Jul 2015

Hi Rohit,

I hope the community comes up with something :)

Do you have any idea how many servers this client can connect to using a nrf51 dongle as the client and nrf51dk as a server?

Thanks and Regards, Neil

07 Jul 2015

I believe the Nordic stack limits it to 3 concurrent connections.

12 Jul 2015

I am using a slightly different setup, but essentially I cannot get the device to send multiple blue tooth commands in a cascade fashion... I am trying to use the gattClient.onDataWrite() callback funcationality. I am using a BLE sniffer, it sends the first command, but any advice on getting it to send the 2nd-10th? Eventually once I get my nrf to communicate with my device I will then be using the notifications... Also any advice on setting up a windows compiler for the RedBearLabs nrf51822? I havent been able to successfully compile on anything other than mBed

13 Jul 2015

@JPaul: could you please share a simplified version of the program you're working with? thanks.

17 Jul 2015

@Rohit I imported the LEDBlinky that was released in June. I adapted it starting at "discoveryTerminationCallback" to issue a write command. It is sending that command. At first I stacked them and it would not send them, so I found the DataWriteCallback to create a que, because I know from documentation and from observation that the same list of commands will need to sent for setup and then I will start working on using the information from the handle notifications. I am using a bluetooth sniffer to track what is being sent/received. /media/uploads/Jpawww/main_-1-.cpp Also is there a better way to privately share code with you. /media/uploads/Jpawww/communcation_observation.csv JPaul Carpenter