7 years, 1 month ago.

Asynchronous BLE on the nRF52

Hello mbed,

I was wondering how to implement a 'BLE Sniffer' that does not block the main thread but doesn't miss any BLE packets either.

ARM says in this Introduction (https://developer.mbed.org/teams/Bluetooth-Low-Energy/wiki/Introduction) that they are 'The mbed team will be coming out with asynchronous APIs very shortly' But I couldn't find any updates or further documentation on this.

My code is this:

void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) {

    pc.printf("Adv peerAddr: [%02x %02x %02x %02x %02x %02x] \r\n rssi: %d \r\n ScanResp: %u \r\n AdvType: %u\r\n\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);
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;

    if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }
 
    ble.gap().setScanParams(500 /* scan interval */, 200 /* scan window */);
    ble.gap().startScan(advertisementCallback);
}

int main(){

  BLE &ble = BLE::Instance();
 ble.init(bleInitComplete);

 while (1) {
    pc.printf("Waiting for a BLE advertisement!\r\n");
    wait(2);
  }

}

I am hoping that the BLE callbacks are running in parallel, but I can't get anything to work unless I use "waitForEvent()" in the forever loop :[

Any help is greatly appreciated... thanks!

1 Answer

7 years, 1 month ago.

Hi Matt,

First of all, the implementation is already asynchronous, advertising events received are stacked when they happens and treated when BLE::waitforEvent or BLE::processEvents is called.

The documentation needs to be refreshed a bit, using waitForEvent to wait for BLE events is outdated (even if it still works!) .

Instead, users can register a callback which will be called when the BLE stacks have events to process and a function to process events in the stack. While simple this mechanism allows BLE API to be plug into any kind of event loop out there, even your own.

The most simple remains to use the default event queue bundled in mbed-os:

// inclusion of the event queue 
#include <events/mbed_events.h>


// declaration of an event queue, 
// its size should be adjusted to the needs of the application
static EventQueue eventQueue(
    /* event count */ 10 *  EQUEUE_EVENT_SIZE 
);

// function which schedule processing of events from BLE API to the event queue.
void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    // post a new event which will result in ble.processEvents();
    eventQueue.call(Callback<void()>(context->ble, &BLE::processEvents));
}


int main()
{
    BLE &ble = BLE::Instance();
    // when ble signal an event then call scheduleBleEventsProcessing
    ble.onEventsToProcess(scheduleBleEventsProcessing);

    ble.init(...);

    // other initialization

    // never returns , dispatch events in the event queue indefinitely
    eventQueue.dispatch_forever();

    return 0;
}

Unlike waitForEvent, using an event queue allows the application to post and wakeup the event queue for events non related to BLE. More informations here and in the documentation.