Nano BLE & Nicla Sense: how to enable long range ble using mbed os, cordio stack

Hello all, this is my first post here and it is also my first project. We want to use a nicla sense me and an arduino nano ble where the nicla sends its sensor readings via ble to the nano ble. However, we get a maximum range of about 10-15 meters with line-of-sight, less if blocked. We need a range of at least 100m. According to the specs the of the ANNA B112 bluetooth module 160m range should be possible and longer ranges have been reported . I read a few posts here, and as i understood the ArduinoBLE library standard does only support bluetooth 4.2, not 5.0 and long range is thus not enabled out-of-the-box (see for example this topic and this one). These posts suggest using the BLE implementation of mbed os (cordio stack) to enable long range and up the power.

Questions:

  1. Since these posts are more than a year old: does anyone know if the ArduinoBLE library now does support long range BLE? if so: does anyone have an example of how to enable it and set the power usage.
  2. If not, does anyone have a more recent example of using the mbed cordio stack in the Arduino IDE (i think it's the BLE library) to up the range and power?
  3. I also read a steady power supply is important: we were thinking about a lipo 250 mhz 3.7v battery to power the Nicla, any thoughts about that?

Any tips& trics to up the range are welcome: 10-15 meters is just not enough.

I got the following code to compile and uploaded on a nicla sense me:

#include <mbed.h>
#include <BLE.h>

using namespace mbed;

DigitalOut led(LED1);

/*------------GLOBAL VARIABLES-------------*/
const static char DEVICE_NAME[] = "NiclaLongRange";             // 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
//const ble::phy_set_t OneMPHY(ble::phy_t::LE_1M);    
/*-----------------------------------------*/

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 extended advertising
            //ble::advertising_type_t::CONNECTABLE_DIRECTED_LOW_DUTY,    // Advertising Type here : connectable undirected = connectable with extended advertising with legacy PDUs
            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));
}

void setup(){
    /* Setup Debugging */
    Serial.begin(115200);
    while(!Serial);
    Serial.println("Start Nicla1 BLE long range");
    /* 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() {
  Serial.println ("And advertising ...");
  delay(2000);
}

Following is printed in the serial monitor:

Start Nicla1 BLE long range
Ble initialized.
Start advertising...

Using LightBlue on an iphone 13 i do not see the device, although ble seems to be successfully initialized and adverstising has started. Also tried scanning from an android samsung tablet using nRF Connect app from Nordic: nicla not detected. Any ideas on why this code is not working? Using ArduinoBLE i can easily detect and connect to the nicla from the nano ble and read its sensor values, but the range is very limited.

Arduino IDE version 2.0.3
Arduino mbed os nicla boards 3.5.5

Update: i also tried scanning for the nicla from a nano ble board using the following code:


// include libraries
#include <ArduinoBLE.h>
#include <SPI.h>
#include <avr/dtostrf.h>

void setup() {
  // start serial monitor  
  Serial.begin(115200);
  while (! Serial); 
  Serial.println("NANO BLE - START SETUP");
   
  // start ble 
  if (!BLE.begin()) {
    Serial.println("* Starting Bluetooth® Low Energy module failed!");
    while (1);
  }
 BLE.setLocalName("Nano 33 BLE (Central)");
 BLE.advertise();  
  scanForNiclaLongRange();
}

void loop() { }

void scanForNiclaLongRange(){
   BLEDevice niclaLongRange;  
   Serial.println("- Scanning for Nicla Long Range...");
   do
   {
     BLE.scanForName("NiclaLongRange");
     niclaLongRange = BLE.available();
   } while (!niclaLongRange);
  
  if (niclaLongRange) {
     Serial.println("- Nicla Long Range found");
     Serial.print("* MAC address: ");
     Serial.println(niclaLongRange.address());
     Serial.print("* Local name: ");
     Serial.println(niclaLongRange.localName());
     Serial.print("* Advertised service UUID: ");
     Serial.println(niclaLongRange.advertisedServiceUuid());
     //nicla2.advertised.readings
     Serial.println();
     BLE.stopScan();
     //connectToPeripheral(nicla2);
   }
}

But this also failed to detect the nicla

I am very interested in the answer as well.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.