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

Thank you so much for this Colas71. I was working towards this on my own, but finding the mbed documentation and examples severely lacking. You have probably saved me a weeks work, although I do still need to understand it all.

Has anyone investigated the range using code based on this example, with a couple of BLE 33s?

pailhoux:
I found my solution here :

BLE_GATT_Example - This is a demonstration of how to create a GATT s… | Mbed

In addition there is a video. :wink:

The link here was created in 2015 and was for an earlier version of MBED. So I believe quite a bit has changed since.

I believe Arduino is currently using a hybrid MBED based primarily on version 5.14.

https://os.mbed.com/docs/mbed-os/v5.14/apis/bluetooth.html

In my opinion, the best MBED BLE implementation of a custom peripheral service can be found here (GattServer) as this uses 128-bit UUID's.

https://os.mbed.com/docs/mbed-os/v5.14/apis/gattserver.html

The major downside here is that this example appears to over complicate matters as I can find no real benefit from using #include "platform/Callback.h" for example (and this caused an error when trying to compile). Similarly not sure why #include "platform/NonCopyable.h" is also used. Beside that it demonstrates the use of creating and attaching your READ, WRITE, WRITEWITHOUTREPONSE, INDICATE and NOTIFY attributes for your characteristics etc.

This is very good news, thank you Colas71.

I am trying to get the code compiling but get this error:
#error "BLE requires at least one role 'BROADCASTER' or 'OBSERVER' to be enabled"

I will get this error only by include ble/BLE.h.

Any idea why I get this error and how do I resolve it?

nachoherrera, what do you mean with latest core library, I think I am using the latest one but can't find any library of the installed one with the revision number you are mentioned.

Sorry for this beginning questions, I have just started to bring up the coding again after a few years off.

@JohanL - I was able to get Colas71's example code to work by using the following setup:

  • Arduino IDE v 1.8.13
  • The Arduino genuine board package called "Arduino nRF528x Boards (Mbed OS) version 1.1.4 (older version)
  • Arduino Nano 33 BLE

@jonmoyes: This is amazing - I was searching for such a long time now...

Could you provide your sketch so I could test that for me too?
I do have same setup.

This would be great!

YES!!
Now I got it working with the correct Mbed OS :slight_smile:

Thank you jonmoyes

Gernot1972, just start by creating a new sketch and copy the code from Colas71. Do not forget to use correct mbedos.

Now I just going to make a code to receive the long range data.
Do anyone already have an example?

Hello everyone,

Someone has been able to reach 200m with the right code to use long range that says Colas71? I have achieved some improvement but not so much, with ble legacy around 20 or 22m as someone said in the post and with LE_Coded between 55 or 60m, better but still far from the 200m. By the way I think that it is not commented in the post but I suppose that apart from using a smartphone that supports Ble 5 long range you are using the app "nRF connect" to establish the connection, in my case if in connect I set LE Coded in "Set initial preferred PHY" it works but only occasionally, the thing is that I don't care about the connection because what I need is to generate beacons where the information is coded in the uuid so I adapted the code of Colas71 to generate an ibeacon.

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

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

/*------------GLOBAL VARIABLES-------------*/
const static char DEVICE_NAME[] = "LR Beacon";                    // Device name when detected on Bluetooth
static events::EventQueue event_queue(/* event count */ 16 * EVENTS_EVENT_SIZE);
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 BeaconDemo : ble::Gap::EventHandler {
public:
    BeaconDemo(BLE &ble, events::EventQueue &event_queue) :
        _ble(ble),
        _event_queue(event_queue),
        _adv_data_builder(_adv_buffer) { }

    void start() {
        _ble.gap().setEventHandler(this);

        _ble.init(this, &BeaconDemo::on_init_complete);

        _event_queue.dispatch_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.");

        start_advertising();
    }

    void start_advertising() {
        /* Create advertising parameters and payload */

        ble::AdvertisingParameters adv_parameters(
            //ble::advertising_type_t::CONNECTABLE_UNDIRECTED,
            ble::advertising_type_t::CONNECTABLE_NON_SCANNABLE_UNDIRECTED,
            ble::adv_interval_t(ble::millisecond_t(500)),  
            ble::adv_interval_t(ble::millisecond_t(1000)),
            false
        );

        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)

        _adv_data_builder.setFlags();

        static const uint8_t ibeacon_raw[] = {0x00, 0x4C, 0x02, 0x15, 0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, 0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61, 0x11, 0x22, 0x33, 0x44, 0xC8};
        
        _adv_data_builder.setManufacturerSpecificData(ibeacon_raw);


        /* Setup advertising */

        ble_error_t error = _ble.gap().setAdvertisingParameters(
            ble::LEGACY_ADVERTISING_HANDLE,
            adv_parameters
        );

        if (error) {
            Serial.println("_ble.gap().setAdvertisingParameters() failed");
            return;
        }

        error = _ble.gap().setAdvertisingPayload(
            ble::LEGACY_ADVERTISING_HANDLE,
            _adv_data_builder.getAdvertisingData()
        );

        if (error) {
            Serial.println("_ble.gap().setAdvertisingPayload() failed");
            return;
        }

        /* Start advertising */

        error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);

        if (error) {
            Serial.println("_ble.gap().startAdvertising() failed");
            return;
        }
    }

private:
    /* Event handler */

    void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&) {
        _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
    }

private:
    BLE &_ble;
    events::EventQueue &_event_queue;

    uint8_t _adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
    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

    BeaconDemo demo(ble, event_queue);
    demo.start();                               // Start Bluetooth Long Range
}

void loop(){}

Basically is to add the CONNECTABLE_NON_SCANNABLE_UNDIRECTED option to the advertisement and set phy to LE_Coded and tx power to 8 dBi, then generate the ibeacon

I am using a One Plus 6T that as it shows the capture has all the options for Ble 5, in addition I attach the captures of the beacons one with legacy beacon to around 22m and the other with long range beacon to around 60m, both with a rssi near the limit of range.

Hi Colas,
would it be possible to include the:
adv_parameters.setTxPower(8);

into a exsisitng scetch, and hence this way set the power level?
Klaus

Hello

I am trying to decrease the datarate to increase the range but apparently it is not implemented, seeing the code of ArduinoCore-nRF528x-mbedos this is what it seems to support, which is for nrf51.

/* Bits 1..0 : Radio data rate and modulation setting. Decision point: TXEN or RXEN task. */
#define RADIO_MODE_MODE_Pos (0UL) /*!< Position of MODE field. */
#define RADIO_MODE_MODE_Msk (0x3UL << RADIO_MODE_MODE_Pos) /*!< Bit mask of MODE field. */
#define RADIO_MODE_MODE_Nrf_1Mbit (0x00UL) /*!< 1Mbit/s Nordic propietary radio mode. */
#define RADIO_MODE_MODE_Nrf_2Mbit (0x01UL) /*!< 2Mbit/s Nordic propietary radio mode. */
#define RADIO_MODE_MODE_Nrf_250Kbit (0x02UL) /*!< 250kbit/s Nordic propietary radio mode. */
#define RADIO_MODE_MODE_Ble_1Mbit (0x03UL) /*!< 1Mbit/s Bluetooth Low Energy */

For nrf52840 it even supports datarate 125Kbits, there is even this variable HCI_PHY_OPTIONS_S8_PREFERRED which would be the same but it also doesn't seem to be available for nrf52840. Did someone managed to change the daterate on the arduino nano 33?

Another doubt that I have is about the antenna of the module, I know that it is a pcb antenna but I don't have very clear of what type, initially I thought that it would be omni so I oriented it perpendicular to the ground, then I read that it was quite directional and I thought that this way maybe it radiated to the sky so I put it parallel to the ground but I hardly appreciate the difference.

I have reached 100m with an rssi below -100dbm, apparently the rssi drops quite quickly (-70/-80dbm within a few metres) but remains more constant as you get further away

ifrz:
Another doubt that I have is about the antenna of the module, I know that it is a pcb antenna but I don't have very clear of what type, initially I thought that it would be omni so I oriented it perpendicular to the ground, then I read that it was quite directional and I thought that this way maybe it radiated to the sky so I put it parallel to the ground but I hardly appreciate the difference.

You can find the antenna characteristics in the NINA-B3 series datasheet.

NINA-B3 DataSheet

Hi,
Does anybody know if the coded PHY in the mbed BLE is the longer range (500kbs) or the longest range (125kbs)? How can we choose between the two?
Thanks in advance.

Hi everyone, I'm trying to run the example posted previously:

Colas71:
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](https://github.com/ARMmbed/mbed-os-example-ble)). I advise you to look at these examples and at the documentation ([here](https://os.mbed.com/docs/mbed-os/v5.15/apis/bluetooth.html)) 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.

However, when I do so, the "on_init_complete" callback is never executed (more specifically, the Serial lines are never printed). I have successfully used the ArduinoBLE Library with my board, so I don't think I have some strange hardware problem. Does anyone have any advice on how to start debugging this problem?

Hello

I finally found how to set up 125 Kbit/s setting the coding rate scheme to S8, it's all in the mbed libraries, on the library BleTypes.h are defined the coding schemes and in Gap.h the method to set this values, finally on the code there will be something like these.

ble.gap().setPhy(_connection_handle, &CodedPHY, &CodedPHY, ble::coded_symbol_per_bit_t::S8);  // S8 = 125 kbits/s

I don't known exactly why is necessary establish a connection, I believe that the PHY layer is decoupled from the upper layers, isnt it? so why set the coding rate on a connection and not on the advertisement like the other parameters (probably better ask on mbed forums).

BleTypes.h

/**
 * Type describing the number of symbols per bit in le coded PHY.
 */
struct coded_symbol_per_bit_t :SafeEnum<coded_symbol_per_bit_t, uint8_t> {
    /** struct scoped enum wrapped by the class */
    enum type {
        /**
         * The Number of symbol used to code a bit is undefined.
         */
        UNDEFINED,

        /**
         * Two symbols to code a bit.
         */
        S2,

        /**
         * Eight symbols to code a bit.
         */
        S8
    };

    /**
     * Construct a new instance of coded_symbol_per_bit_t.
     */
    coded_symbol_per_bit_t(type value) :
        SafeEnum<coded_symbol_per_bit_t, uint8_t>(value) { }
};

Gap.h

    ble_error_t setPhy(
        connection_handle_t connection,
        const phy_set_t *txPhys,
        const phy_set_t *rxPhys,
        coded_symbol_per_bit_t codedSymbol
    );

jonmoyes:
@JohanL - I was able to get Colas71's example code to work by using the following setup:

  • Arduino IDE v 1.8.13
  • The Arduino genuine board package called "Arduino nRF528x Boards (Mbed OS) version 1.1.4 (older version)
  • Arduino Nano 33 BLE

I tried this setup and no luck. It compiles, and when I open the serial I get these two notifications:

"Ble initialized." "Start advertising..." but the BLE name "LR Demo" never shows up, nor did I see any "unnamed" BLE devices that may fit the bill.

The same BLE 33 module works with this code in Post #11: BLE very weak signal - Nano 33 BLE - Arduino Forum

What I tried:

tomsz:
I tried this setup and no luck. It compiles, and when I open the serial I get these two notifications:

"Ble initialized." "Start advertising..." but the BLE name "LR Demo" never shows up, nor did I see any "unnamed" BLE devices that may fit the bill.

The same BLE 33 module works with this code in Post #11: BLE very weak signal - Nano 33 BLE - Arduino Forum

Are you using another Nano33 to scan? Or a mobile device? If mobile, which phone/tablet are you using to scan? Keep in mind that most phones don't support Coded PHY. I would suggest you check your device's compatibility. I've had success with the OnePlus 7T. None of the iPhones to date support Long Range/Coded PHY.

jonmoyes:
Are you using another Nano33 to scan? Or a mobile device? If mobile, which phone/tablet are you using to scan? Keep in mind that most phones don't support Coded PHY. I would suggest you check your device's compatibility. I've had success with the OnePlus 7T. None of the iPhones to date support Long Range/Coded PHY.

Thank you. That must be the problem! I have an iPhone SE 2020 and a Motorola Moto G2. Is there any way I can get more than 10 feet distance out of it? I would be happy with about 60 feet.

tomsz:
Is there any way I can get more than 10 feet distance out of it? I would be happy with about 60 feet.

First check that you have a good power supply - noisy power will cause poor range. Also make sure there is no metal above below or to the sides of the chip antenna. The antenna should ideally be pointed up it you have it mounted in an enclosure.
In software, you should be able to set the Tx Power to 8dB, and then keep using the 1M PHY (Bluetooth 4), rather than Coded. I haven't tested this, but at first glance I would recommend trying the following adjustments:

Find:

const ble::phy_set_t CodedPHY(ble::phy_t::LE_CODED);            // Creating a Coded Phy set[color=#222222][/color]

Replace with:

const ble::phy_set_t OneMPHY(ble::phy_t::LE_1M);            // Creating a 1M set[color=#222222][/color]

and
Find:

[color=#222222]adv_parameters.setPhy(ble::phy_t::LE_CODED, ble::phy_t::LE_CODED);  // Set Advertising radio modulation to LE_CODED Phy (=Long Range)[/color][color=#222222][/color]
[color=#222222]adv_parameters.setTxPower(8);                                       // Set radio output power to 8dbm (max)[/color][color=#222222][/color]
[color=#222222]_ble.gap().setPreferredPhys(&CodedPHY, &CodedPHY);                  // Set preferred connection phy to LE_CODED (=long range)[/color]

Replace with:

adv_parameters.setPhy(ble::phy_t:: LE_1M, ble::phy_t::LE_1M);  // Set Advertising radio modulation to 1M Phy (Bluetooth 4)[color=#222222][/color]
adv_parameters.setTxPower(8);                                       // Set radio output power to 8dbm (max)[color=#222222][/color]
_ble.gap().setPreferredPhys(&OneMPHY, &OneMPHY);                  // Set preferred connection phy to LE_CODED (=long range)[color=#222222][/color]

jonmoyes Got it. Will try. Thanks

Unfortunately, did not work for me. Managed to compile and upload, but did not show up on my iPhone with the new parameters. But I now have problems uploading other BLE 33 programs too.

I will try another laptop. The one I use is slow and seems to be a hit and miss to compile and upload.

I'm trying to use NANO33 as a scanner to catch advertisement messages from coded PHY / Long Range beacons. However I have problems in the setup using Colas71's example code. Has anyone succeeded and can share code?