Nano 33 ble nRF52840 - BLE long range/pairing ? Mbed BLE API ? Adafruit Lib?

Hello everyone,
thanks for all your answers, I have good news !

After a lot of research trying to understand how the mbed BLE API works, I discovered how to get long range by setting the PHY to CODED and the Radio TX output power to 8dbm. All of this is possible thanks to the mbed BLE library which can look complex at first for C++ beginners like me (it is more complex than ArduinoBLE though) but is actually very powerful and way more complete.

This is the code I made for the long range feature with which I reached a 200 meters range :

/*==============================================
DEMONSTRATION OF THE LONG RANGE FEATURE :

The Bluetooth is managed by a class ("LRDemo"). 
When an objet is created from this class, we
can call the .start() function. It will initialize
Bluetooth and once it's done, it will advertise
using PHY CODED technologie (= Long range) with
the max output power (8dbm). It will be connectable
and keep the long range mode during connection
however it won't have specifics services.

WORKING ON MBED 5 WITH ARDUINO NANO 33 BLE (NRF52840)
Library needed : BLE_API
Average Consumption : 1,20mA
================================================*/

/*--------INCLUDES--------*/
#include "ble/BLE.h"
/*------------------------*/

using namespace mbed;       // Enable us to call mbed functions without "mbed::"

/*------------GLOBAL VARIABLES-------------*/
const static char DEVICE_NAME[] = "LR Demo";                    // Device name when detected on Bluetooth
static events::EventQueue event_queue(10 * EVENTS_EVENT_SIZE);  // Create Event queue
static const uint16_t MAX_ADVERTISING_PAYLOAD_SIZE = 59;        // Advertising payload parameter
const ble::phy_set_t CodedPHY(ble::phy_t::LE_CODED);            // Creating a Coded Phy set
/*-----------------------------------------*/






/*------------------------------Bluetooth Device class------------------------------*/
class LRDemo : public ble::Gap::EventHandler {
public:
    /* Class constructor */
    LRDemo(BLE &ble, events::EventQueue &event_queue) :
        _ble(ble),                                      // BLE API Class
        _event_queue(event_queue),                    
        _adv_handle(ble::INVALID_ADVERTISING_HANDLE),   // Advertising parameter
        _adv_data_builder(_adv_buffer) { }              // Advertising parameter

    /* Class destructor */
    ~LRDemo() {
        if (_ble.hasInitialized()) {
            _ble.shutdown();
        }
    }

    void start() {
        _ble.gap().setEventHandler(this);               // Assign GAP events to this class
        _ble.init(this, &LRDemo::on_init_complete);     // Initialize Bluetooth
        _event_queue.dispatch_forever();                // Wait for event forever
    }

private:
    /** Callback triggered when the ble initialization process has finished */
    void on_init_complete(BLE::InitializationCompleteCallbackContext *params) {
        if (params->error != BLE_ERROR_NONE) {
            Serial.println("Ble initialization failed.");
            return;
        }
        Serial.println("Ble initialized.");

        /* Create advertising parameters and payload */
        ble::AdvertisingParameters adv_parameters(
            ble::advertising_type_t::CONNECTABLE_NON_SCANNABLE_UNDIRECTED,    // Advertising Type here : connectable non scannable undirected = connectable with exetended advertising
            ble::adv_interval_t(ble::millisecond_t(500)),           // Min Advertising time in ms
            ble::adv_interval_t(ble::millisecond_t(500)),           // Max Advertising time in ms
            false                                                   // Legacy PDU : Needed to be OFF in Long Range Mode
        );
        adv_parameters.setPhy(ble::phy_t::LE_CODED, ble::phy_t::LE_CODED);  // Set Advertising radio modulation to LE_CODED Phy (=Long Range)
        adv_parameters.setTxPower(8);                                       // Set radio output power to 8dbm (max) 
        _ble.gap().setPreferredPhys(&CodedPHY, &CodedPHY);                  // Set preferred connection phy to LE_CODED (=long range)

        if (_adv_handle == ble::INVALID_ADVERTISING_HANDLE) {       // Create advertising set with parameters defined before
        _ble.gap().createAdvertisingSet(
                &_adv_handle,
                adv_parameters);
        }
        _adv_data_builder.clear();                                  
        _adv_data_builder.setFlags();                                
        _adv_data_builder.setName(DEVICE_NAME);                     // Set Bluetooth device name

        /* Setup advertising */
        _ble.gap().setAdvertisingParameters(_adv_handle, adv_parameters); 
        _ble.gap().setAdvertisingPayload(_adv_handle, _adv_data_builder.getAdvertisingData());  

        /* Start advertising */
        _ble.gap().startAdvertising(_adv_handle);
        Serial.println("Start advertising...");
    }

    void onConnectionComplete(const ble::ConnectionCompleteEvent &event) {
        Serial.println("Device connected");
    }

    void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&) {
        Serial.println("Device disconnected");
        _ble.gap().startAdvertising(_adv_handle);
        Serial.println("Start advertising...");
    }
    

private:
    /* Class variables declaration*/
    BLE &_ble;
    events::EventQueue &_event_queue;

    uint8_t _adv_buffer[MAX_ADVERTISING_PAYLOAD_SIZE];  // Advertising parameters
    ble::advertising_handle_t _adv_handle;              //
    ble::AdvertisingDataBuilder _adv_data_builder;      //
};
/*----------------------------------------------------------------------------------*/

/** Schedule processing of events from the BLE middleware in the event queue. */
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) {
    event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents));
}

/*====================== MAIN CODE ======================*/

void setup(){
    /* Setup Debugging */
    Serial.begin(9600);
    while(!Serial);

    /* Low Power */
    digitalWrite(LED_PWR, LOW);                 // Turn off power LED
    digitalWrite(PIN_ENABLE_SENSORS_3V3, LOW);  // Turn off sensors
    NRF_POWER->DCDCEN = 0x00000001;             // Enable DCDC
    /*           */

    BLE &ble = BLE::Instance();                 // Create the BLE object in order to use BLE_API function
    ble.onEventsToProcess(schedule_ble_events); // Set event schedule

    LRDemo demo(ble, event_queue);              // Create LRDemo Object
    demo.start();                               // Start Bluetooth Long Range 
}

void loop(){}

/*=======================================================*/

It is based on the examples provided by the library (here). I advise you to look at these examples and at the documentation (here) to understand how the library works.

The Mbed BLE_API is included in the arduino nano 33 ble firmware so there is no need to install anything.
My code is also optimized for low power consumption (1.20mAh when powering from an external source).

Hope it helps ! I am here if there is any questions about the code.

4 Likes