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

API extensions to support scanning

27 Apr 2015

Hi,

We're on our way to extending the BLE_API to allow the programming of devices to take on the role of a BLE Central. As a step in this direction, we'd like to propose APIs to support scanning:

extensions to top-level APIs to support scanning

    /**
     * Setup parameters for GAP scanning--i.e. observer mode.
     * @param  interval Scan interval (in milliseconds) [valid values lie between 2.5ms and 10.24s].
     * @param  window   Scan Window (in milliseconds) [valid values lie between 2.5ms and 10.24s].
     * @param  timeout  Scan timeout (in seconds) between 0x0001 and 0xFFFF, 0x0000 disables timeout.
     */
    ble_error_t setScanningParams(uint16_t interval = GapScanningParams::SCAN_INTERVAL_MAX,
                                  uint16_t window   = GapScanningParams::SCAN_WINDOW_MAX,
                                  uint16_t timeout  = 0);
    ble_error_t setScanningInterval(uint16_t interval);
    ble_error_t setScanningWindow(uint16_t window);
    ble_error_t setScanningTimeout(uint16_t timeout);

    /**
     * Start scanning (Observer Procedure) based on the scan-params currently
     * in effect.
     *
     * @param  callback The application callback to be invoked upon receiving
     *     every advertisement report. Can be passed in as NULL, in which case
     *     scanning may not be enabled at all.
     */
    ble_error_t startScanning(Gap::AdvertisementReportCallback_t callback);

    /**
     * Stop scanning. The current scanning parameters remain in effect.
     *
     * @retval BLE_ERROR_NONE if successfully stopped scanning procedure.
     */
    ble_error_t stopScanning(void);

extensions to Gap.h

    enum AdvertisementType_t {
        ADV_IND           = 0x00,   /**< Connectable undirected. */
        ADV_DIRECT_IND    = 0x01,   /**< Connectable directed. */
        ADV_SCAN_IND      = 0x02,   /**< Scannable undirected. */
        ADV_NONCONN_IND   = 0x03,   /**< Non connectable undirected. */
    };

...

    typedef void (*AdvertisementReportCallback_t)(const address_t      peerAddr,
                                                  int8_t               rssi,
                                                  bool                 isScanResponse,
                                                  AdvertisementType_t  type,
                                                  uint8_t              advertisingDataLen,
                                                  const uint8_t       *advertisingData);

Please comment. Thanks.

27 Apr 2015

Here's some test code to drive these new APIs:

void advertisementCallback(const Gap::address_t      peerAddr,
                           int8_t                    rssi,
                           bool                      isScanResponse,
                           Gap::AdvertisementType_t  type,
                           uint8_t                   advertisingDataLen,
                           const uint8_t            *advertisingData) {
    printf("adv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, isScanResponse %u, AdvertisementType %u\r\n",
           peerAddr[0], peerAddr[1], peerAddr[2], peerAddr[3], peerAddr[4], peerAddr[5], rssi, isScanResponse, type);
    printf("len = %u\r\n", advertisingDataLen);
    unsigned index = 0;
    for (; index < advertisingDataLen; index++) {
        printf("%02x ", advertisingData[index]);
    }
    printf("\r\n");
}

...

    ble.startScanning(advertisementCallback);
13 May 2015

Well done!

I just tested the scanning API using https://github.com/xiongyihui/mbed_ble, it works.

13 May 2015

scanning has now been released https://github.com/mbedmicro/BLE_API/commit/dcc18e3d18ee16f11c4ab7bb3582a45492ef5590 Thanks for testing it.

09 Jul 2015

Rohit - this is great, we still need more :)

are you planning on writing some examples that allow discovery, connection and reading/writing of characteristics from mbed? scanning is great; but it would be great to actually connect to a device and interact with them.. i've played around with redbear labs BLE mini with a UART interface to the TI 2540 and implement scanning, connecting, read/write and notifications.. would be great to have it part of the mbed API so we can do it directly on boards like nRF51-dk

09 Jul 2015
09 Jul 2015

Rohit

i'm looking at having the nRF51-DK act as the server.. not the client, as i have written in a blog post earlier.

http://evothings.com/how-to-turn-a-nordic-semiconductor-nrf51-dk-into-a-discoverable-beacon-using-mbed/

unless your saying i can use the same API's once i have discovered the device.. i'll take a look but i'm looking at starting the connection from the mbed application; not the standard way we've done things above in the blog post.

10 Jul 2015

Aaron,

Am I correct in understanding that you want your nRF51-DK to act as a GAP peripheral and a GATT client? refer to http://docs.mbed.org/docs/ble-intros/en/latest/InDepth/BLEInDepth/#peripheral-and-central-devices-v-servers-and-clients

Once a connection is established, you should be able to initiate service discovery from the peripheral using the same APIs. This is what ANCS (Apple Notification Centre Service) expects. I haven't tried this with BLE_API, that that's one of the demos which is waiting to be developed.

regards,

10 Jul 2015

Hi ,

Could you please let me know if you'll have examples on multiple peripheral connections using GattClient. I am finding difficulty in understanding how to connect to multiple peripherals at the same time. I know there is a limit of 3 but I need to connect to only 2 devices and receive notifications from both.

Thanks and Regards, Neil

11 Jul 2015

Hi Neil,

It is on our near-term roadmap to produce demos for concurrent connections.

Connection event callbacks provide a role and connection handle. The application can cache the connection handle for further API calls.

I expect we'll have something along those lines before the first week of August. If you purpose a demo, we could review it and have something ready sooner.

11 Jul 2015

Neil, Could you elaborate on the challenges in using the API for concurrent connections?

11 Jul 2015

Hi Rohit,

Thanks for your response.

Well, I am confused on how to go about doing it concurrently.

If you could perhaps give me some guidelines and I could try it out.

Thanks and Regards, Neil

11 Jul 2015

Neil,

I would expect scanning to continue in the background during and after the first connect. If your advertisement callback is extended to respond to multiple addresses, it would be a matter of calling connect more than once and following up with connection-specific service-discovery.

Does that make sense?

26 Jul 2015

Hi,

I am trying to Implement Scan Request and Scan response. I have a device that sends advertisements and also scans for advertisements(using the softdevice s130). I want to send some custom data from one such device to another in scan response after a scan request is received. I see accumulateScanResponse() in Gap.h and that active scanning is false by default. I am struggling to understand how a scan request is sent and received and how a device recognizes that a scan request has been made after which a scan response payload is sent. If someone has any suggestions, I would be really grateful.

Thanks Regards Swati

26 Jul 2015

If you enable active scanning, the observer will send a scan request automatically and immediately upon receiving an advertisement.

27 Jul 2015

Hi Rohit,

Thank you for your reply. But I am still having a few problems. I don't want to send scanRequest right at the beginning but only if a certain value is received from the initial advertisement packet.

Here is the scenario.....

In my code say device A sends a certain value (that increments every second) to device B as part of an Advertising structure with type TRICKLE_TYPE (Here TRICKLE_TYPE is custom type that I have added in the GapAdvertisingData.h ). Device B should then send a scan request to device A only if a certain value is received, after which the device A should send a scan Response. In my code, I have put the following statement so that data in array scanResponse is sent only when the scan request is received:

ble_device.accumulateScanResponse(GapAdvertisingData::TRICKLE_TYPE,(uint8_t *)scanResponse, sizeof(scanResponse));

However, when I check on Master Control Panel, right at the beginning I can see data in array ScanResponse being advertised. Is it wrong to put the accumulateScanResponse() function along with statements calling accumulateAdvertisingPayload() to populate the initial advertisement packet? I don't want the Scan Response advertising structure to appear till the scan request has been received.

Also, in the code in Device B, I am checking for the value received in the advertisement callback and only if it is a required value I use ble.gap().setActiveScanning(true); to send scan request. Is it possible to dynamically change the scan parameters like this or I need to stopScan then setActiveScanning and then startScan again.

Kind Regards Swati Sehgal

27 Jul 2015

Hello Swati,

Scan requests are sent out very shortly after the receipt of an advertisement. There isn't enough of an intervening time interval to put application logic to enable active scanning. What can be achieved is for the B-application to enable active scanning *after* receiving a trigger; and for the A-application to call accumulateScanResponse() after sending the trigger. This means that a scan request/response will be exchanged for the following advertisement. Calling accumulateScanResponse() will take effect immediately; which means the scan response will be available for the next advertisement event. A call to setActiveScanning(true) will require a startScan() to take effect.

21 Dec 2015

Hi Rohit, Thanks for the great job :) I just wonder if you can give me some details/help on how to get the complete param array in advertisementCallback? As far i saw, the specs (?) limits advertisingDataLen/Data to 31 bytes. It seems there's a way to get a "bigger" array, using setActiveScanning(true)... Does it talk to you ? Thanks Olivier

21 Dec 2015

Hi Olivier,

In BLE, there is two types of packets that a broadcaster can broadcast, these packets are :

  • Advertisements packets: These packets will be send as long as the device advertise.
  • Scan response: These packets are send if the broadcaster receive a scan request.

Both of these packets are 31 bytes long and have the same layout.

You can enable active scanning by using the function setActiveScanning. In such case, You will receive advertisements packets and scan responses from devices around,

21 Dec 2015

Thanks for the very fast reply !!! Let me clarify my request. I m working with a RedBearLab nRF51822 as a "receiver" (the device is able to look for an HRM sensor and display the bpm. I started with the BLE_Observer code. Everything seems to work (except some disconnections from time to time), but i would like to receive, in the advertisementCallback(), the complete list of parameters (seems to be coherent with your answer) Btw, do you have any snippet to explain how to get the information using "scan response" method ? Thanks Olivier

21 Dec 2015

Hi Olivier,

What do you mean by " the complete list of parameters " ? startScan callback will be called when the device receive an advertisement packet or a scan response. AdvertisementCallbackParams_t structure contain the field isScanResponse to discriminate the two.

21 Dec 2015

Hi Vincent (it seems we have some friends in common :) ... )

Default status of isScanResponse is 0 ... but maybe i have to enable activeScanning in setScanParams()... i ll check that tomorrow. eg. with a ibeacon module, i receive this kind of frame:

Adv peerAddr: [d4:0b:cc:f0:4a c8] rssi -57, ScanResp: 0, AdvType: 0
02 01 05 0e 09 49 43 4f 4f 4b 49 45 5f 41 31 30 36 36 03 19 ff ff

and i expect these additionnal bytes:

... 05 02 02 18 03 18 02 0A 00 05 14 02 18 11 18

edit

  ble.gap().setScanParams(GapScanningParams::SCAN_INTERVAL_MAX, GapScanningParams::SCAN_WINDOW_MAX,
		0 /* timeout */, true /* Active mode */); 

doesn't help :(

22 Dec 2015

Ok...found my issue... ActiveMode is ok, but (correct me if it is not correct) i had to change a little bit my advertcallback to :

void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params)
{
#ifdef DBG_TRACE
    pc.printf("Adv peerAddr: [%02x:%02x:%02x:%02x:%02x %02x] rssi %d, ScanResp: %u, AdvType: %u\r\n",
           params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0],
           params->rssi, params->isScanResponse, params->type);
#if DUMP_ADV_DATA
    for (unsigned index = 0; index < params->advertisingDataLen; index++) {
        pc.printf("%02x ", params->advertisingData[index]);
    }
    pc.printf("\r\n");
#endif /* DUMP_ADV_DATA */
#endif //DBG_TRACE

	/* Is it correct ??? */
	if (params->isScanResponse == 0) 
		return;
	
	pc.printf("Try to connect !!!! \r\n");
	ble.gap().connect(params->peerAddr, Gap::ADDR_TYPE_PUBLIC, NULL, NULL);
}
22 Dec 2015

Hi Olivier,

For sure we have some friends in common :).

If active scanning is enabled, the scanner will send scan requests to other devices around. In the mean time, broadcaster continue to send advertising data. Once an observer receive a scan request, it can reply with a scan response.

That means that the scan callback can be called with the advertising data from your beacon before you received a scan response. The scan callback will be called every time the device will receive a broadcast packet, until the scan process stop.

Regarding your code, you have to know that scan requests are broadcast to all devices around, other devices around can also respond to your scan request.