Xiao BLE nRF52840 flaky scanning

We’re considering switching our project over to using the Xiao BLE nRF52840 instead of the Arduino Nano IoT, but when porting over the BLE functionality (using ArduinoBLE) the Seeed module fails to scan reliably after 30 minutes or so. The scan is using localName matching

BLE.scanForName(PERIPHERAL_NAME, true);
unsigned long startTime = millis();
while(millis() < startTime + 5000) {
  BLEDevice scannedDevice = BLE.available();
  bool isPeripheral = scannedDevice.localName() == PERIPHERAL_NAME;
  if (!isPeripheral) continue;
  Serial.print("Found possible sensor");
  delay(5);
}
BLE.stopScan();
delay(5000);

The scan goes for 5 seconds, then there’s a 5 second break. When I initially start running this code the device is scanned at least once every scan cycle, but after 30 - 60 minutes it barely can pick up the peripheral once every 5 scan cycles. Using the nRF Connect app shows the peripheral advertising when it should be with 100ms advertising interval.

Reliable BLE scans are the most important part of our project so is there some way to fix this? There are no issues here currently with the Nano 33 IoT

The entire module also freezes up after several hours, the serial communication crashes and the GPIO pins continue outputting whatever values they had before the freeze occurred

same here, the Xiao BLE nRF52840 Receiver Central freezes after 15-30min !!!

I am using ArduinoBLE library, normal as usual.

The sender is off most of the time and will only connect and send sensor data about once or twice a day. I try to have the receiver active 24h. But it freezes after 15-30min.

Here is my receiver Central code:

#include <ArduinoBLE.h>

char* PERIPHERAL_LOCAL_NAME = "SOME PERIPHERAL NAME";
#define BLE_UUID_SENSOR_DATA_SERVICE    "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#define BLE_UUID_MULTI_SENSOR_DATA      "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"

#define BAUDRATE                        115200
#define UPDATE_INTERVALL                10
#define BLE_POLL_INTERVALL              5

uint64_t previousMillis = 0;

void setup() {
    
    Serial.begin(BAUDRATE);
    delay(2000);
    
    previousMillis = millis();

    BLE.begin();

    BLE.scanForUuid(BLE_UUID_SENSOR_DATA_SERVICE);
}

void loop() {
        
    unsigned long currentMillis = millis();
    
    if (currentMillis - previousMillis > UPDATE_INTERVALL) {
        
        previousMillis = currentMillis;

        BLE.scanForUuid(BLE_UUID_SENSOR_DATA_SERVICE);
        
        BLEDevice peripheral = BLE.available();

        if (peripheral) {
            
            if (peripheral.localName() != PERIPHERAL_LOCAL_NAME) {
                return;
            }

            BLE.stopScan();

            explorePeripheral(peripheral);
        }
    }
}

bool explorePeripheral(BLEDevice peripheral) {
    
    if (!peripheral.connect()) {
        return false;
    }
  
    if (!peripheral.discoverAttributes()) {
        peripheral.disconnect();
        return false;
    }
  
    BLECharacteristic multiSensorDataCharacteristic = peripheral.characteristic(BLE_UUID_MULTI_SENSOR_DATA);
    if (!multiSensorDataCharacteristic) {
        peripheral.disconnect();
        return false;
    }
    
    if (!multiSensorDataCharacteristic.canSubscribe()) {
        peripheral.disconnect();
        return false;
    }
    
    if (!multiSensorDataCharacteristic.subscribe()) {
        peripheral.disconnect();
        return false;
    }
  
    while (peripheral.connected()) { 

        unsigned long currentMillis = millis();
        
        if (currentMillis - previousMillis > BLE_POLL_INTERVALL) {

            previousMillis = currentMillis;
        
            BLE.poll();

            if (multiSensorDataCharacteristic.valueUpdated()) {

                multiSensorDataCharacteristic.readValue(...);
            }

            // if (peripheral.rssi() != 0) {
            //     digitalWrite(RSSI_OK_LED_PIN, HIGH);
            // } else {
            //     digitalWrite(RSSI_OK_LED_PIN, LOW); // red alarm LED ON
            // }
        }
    }

    peripheral.disconnect();
    
    return true;
}

I tried to add a periodic BLE.end(); followed by a BLE.begin(); every 10 seconds because I believed this might help. But no change.

It seems that only a module RESET will solve the problem.

However, in my application I would like to have the receiver listen during 24 hours (and never power it off or reset).

What could be the issue of the freeze ?

The sender is off most of the time and will only connect and send sensor data about once or twice a day.

Does the transmitter side turn off the power?
Does the transmitter just disconnect the BLE?
Or does the transmitter just not transmit data?

@msfujino: The transmitter is off Power completely! (due to saving Battery). It only turns on twice a day for about 2min or so. The rest it is off power.

The receiver is ON 24h. And should recognize when the sender powers-on.

I rewrote the waiting-loop in the receiver as follows (see below). But no change: after about 15-30min the receiver “freezes”.

In fact, I realized that inside the “do-while”-loop, it can no longer turn on an LED. Which means that the module is completely frozen.

I could introduce a watchdog - but this is not a nice solution I think. I would like to find the root cause of the freeze instead!

void loop() {

    BLEDevice peripheral;       
     
    do {
        BLE.scanForUuid(BLE_UUID_SENSOR_DATA_SERVICE);
        peripheral = BLE.available();
    } while (!peripheral);                

    if (peripheral) {        
        if (peripheral.localName() != PERIPHERAL_LOCAL_NAME) {
            return;
        }
        BLE.stopScan();
        explorePeripheral(peripheral);
    }
}

is this the correct data type for the length of timer needed, just asking?
TY
HTH
GL :slight_smile:

@ PJ_Glasso: yes, I never had a problem with it anyways on any other Arduino boards.

Or do you have a better suggestion ?

Hi there, No not specifically but I use allot of Sense units for advertise and connection, not so much as a Central, mostly peripheral, seems as though it should work as described ?

Scanner on 24x7
Receiver on if Sensor is BLE available and subscribe-able.(data available)
I wonder what happens if you just let the Sensor side drop connection? Receiver keeps listening? then freezes?
any more complete code?
I can try some stuff… :wink: :ok_hand:
HTH
GL :slight_smile:

Do you think I should exchange Peripheral and Central ? Does it matter if one is the sender and one the receiver or vice-versa ?

In my first response above, I pretty much gave 100% of my Central-Receiver Code (except the sensor payload I omitted since not needed).

Here is my complete Peripheral-Sender Code for completeness reasons:

#include <ArduinoBLE.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>

char* PERIPHERAL_LOCAL_NAME = "SOME PERIPHERAL NAME";
#define BLE_UUID_SENSOR_DATA_SERVICE    "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#define BLE_UUID_MULTI_SENSOR_DATA      "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"

#define BAUDRATE                        115200
#define UPDATE_INTERVALL                50

#define BATTERY_CHARGING_TYPE_PIN       P0_13 // internal
#define SEND_DATA_BUTTON_PIN            D10

BLEService sensorDataService(BLE_UUID_SENSOR_DATA_SERVICE);
BLECharacteristic multiSensorDataCharacteristic(BLE_UUID_MULTI_SENSOR_DATA, BLERead | BLENotify, sizeof multiSensorData.bytes);

bool setupBleMode() {
    
    if (!BLE.begin()) {
        return false;
    }

    // set advertised local name and service UUID:
    BLE.setDeviceName(PERIPHERAL_LOCAL_NAME); // make it the same...
    BLE.setLocalName(PERIPHERAL_LOCAL_NAME);
    BLE.setAdvertisedService(sensorDataService);

    // BLE add characteristics
    sensorDataService.addCharacteristic(multiSensorDataCharacteristic);

    // add service
    BLE.addService(sensorDataService);

    // set the initial value for the characeristic:
    multiSensorDataCharacteristic.writeValue(multiSensorData.bytes, sizeof multiSensorData.bytes);

    // start advertising
    BLE.advertise();

    return true;
}

void setup() {
  
    Serial.begin(BAUDRATE);
    delay(2000);
  
    setupBleMode();
}

void loop() {
      
    static long previousMillis = 0;

    // listen for BLE peripherals to connect:
    BLEDevice central = BLE.central();

    if (central) {
        
        Serial.print(F("Connected to central: "));
        Serial.println(central.address()); 

        while (central.connected()) {
            
            unsigned long updateMillis = millis();            
            if (updateMillis - previousMillis > UPDATE_INTERVALL) {                
                previousMillis = updateMillis;          
                
                if (central.rssi() != 0) {
                    multiSensorDataCharacteristic.writeValue(..., sizeof multiSensorData.bytes);                       
                }
            }
        }

        Serial.print(F("Disconnected from central: "));
        Serial.println(central.address());
    }
}

Do you have an example code for 24h receiver ?

And example code for Sender ? (the sender powers-ON, sends some data, and then powers-Off again)… Receiver stays alive 24h…

LOL, I was just interpreting your code flow aloud. seems as if it should work. I’ll try to set up a test here and lyk.
HTH
GL :-p

edit:
Can’t hurt to switch roles on testing, see if the behavior is the same.
I use a Android Phone as the Central BTW :wink:

Thank you for all your inputs so far.

Did you ever try to use two Xiao nRF52840 BLE modules to communicate with each other ?

I think this might be an extra issue here but I am not sure at all what plays a role since the problem is pretty nasty to find.

The fact that my do...while loop keeps running - but the connection no longer works makes me think that the error must be somewhere deep down the ArduinoBLE library in combination with this Xiao nRF52840 BLE module.

The LED keeps flashing all the time - even tough the receiver no longer connects with the sender…

It seems the peripheral is never available anymore !!!

do {
    peripheral = BLEDevice()

    digitalWrite(LED_R_PIN, HIGH) // turn red LED off
    digitalWrite(LED_G_PIN, HIGH) // turn gren LED off
    digitalWrite(LED_B_PIN, LOW) // turn blue LED on
    delay(10)
    digitalWrite(LED_R_PIN, LOW) // turn red LED on
    digitalWrite(LED_G_PIN, HIGH) // turn gren LED off
    digitalWrite(LED_B_PIN, HIGH) // turn blue LED off
    delay(100)

    BLE.scanForUuid(BLE_UUID_SENSOR_DATA_SERVICE)
    peripheral = BLE.available()
} while (!peripheral);

No , not specifically(BLE sense to BLE sense) but the connection protocol is unforgiving, Timing matters and things like Delays, etc. as well as other BLE devices can have an effect the receiving radios performance IMO. I see nordic folks on there forum using RSSi etc. to qualify there connections.
more testing—
HTH
GL :-p

edit:
I had a troublesome sketch and it worked much better when I got rid of the LED flashing etc. No fluff, or eyecandy. not to mention the power savings on my batteries …lol :wink:

1 Like

I have confirmed that if a turned off peripheral is continuously scanned for more than 2 hours, it cannot be reconnected when turned on again.
I changed the idea and stopped scanning with BLE.end() while the peripheral was OFF, and when the peripheral was turned ON, restarted scanning with BLE.begin() The peripheral were able to reconnect even after 12 hours. However, it is necessary to use a switch or other means to tell Central that the peripheral has been turned on.

My sketches are attached.
freeze_test.zip (2.7 KB)

Nice work,
He would need to have a timer or something , seems wacky that it does that but it may just be in the BLE spec to do it (battery power etc), I don’t think my Android phone will scan for 12 hrs.?
I loaded your code and they both ran without mod, I used the button on the dev board and away it went.




Scanning…Scanned, SWOFF(central) Peripheral Unabaliable and data -RSSI ?
myPeripheral is Running, Got Central (some data)

I thought there was a way to scan, connect, (then bond), then reconnect and just leave central ON always.? (like my BLE speaker and cellphone) once it’s paired it can reconnect anytime?
I might be tired too… LOL
cool stuff…
HTH
GL
:slight_smile:

EDIT:
I wonder if anything like this is available? " Enhanced ShockBurst" :disguised_face:
Nordic proprietary protocols

Low Power Transmitter/Receiver Example
This example shows how to use Enhanced ShockBurst (ESB) in an energy-efficient way. Conserving energy is important, for example, when one device is on a small battery that is intended to last for a long period of time while the other device has access to an abundant power source.
This example consists of two applications: a low power Transmitter and a Receiver. The idea is that the Transmitter conserves as much energy as possible and can therefore run on a small battery, while the Receiver needs more power. The Receiver is always on and listens for packets from the Transmitter whenever the Transmitter is active.

sounds like the OP spec…?

end—EDIT__

Hi msfujino, thanks for your contribution.

However, an extra switch or other user-interaction-measn is NO option to me ! The receiver has to work independently 24h without user-interaction. The application does not allow that.

Therefore your workaround with BLE.end() and user-triggered BLE.begin() is not a valid option.

Hi PJ_Glasso,

thanks for confirming msfujino’s workarround. However as explained, we need to find out why the Seeed Studio Xiao nRF52840 BLE set as Central-Receiver stops connecting to any Peripheral-Sender after a certain time - WITHOUT EXTRA NEED FOR USER INTERACTION. More investigation or ideas are needed.

More findings:

Trying the identical Central-Receiver Code on another board works !!! i.e. the Arduino Nano 33 BLE board does not show the receiver-freeze issue !! (testing hours = 6h).

It seems, the Central-Receiver “freeze” definitively seems to be a HW-related-cause.

The Seeed Studio Xiao nRF52840 BLE board does not work and Central-Receiver freezes happens as we know. (ArduinoBLE libarar v1.3.2 used)

People on the ArduinoBLE forum are investigating - see ArduionoBLE issue#284.

The big question that remains is if the ArduinoBLE library can do anything about it ?

-------- oh no ---------------------------------

Bad news: after 12 hours the Arduino Nano 33 BLE board also stopped working. It froze as well !

I am back to zero.

Moreover, the Seeed Studio Xiao nRF52840 BLE board does not work with the SW-change in HCI.cpp (i.e. commenting out HCI.leSetAdvertiseEnable(0x01); does not help).

The fact that the second board ran longer, makes me think it is a memory problem. What is memory-related in the ArduinoBLE library during Central-Receive while Peripheral-Sender is off ???

I will continue to watch issue #284.
Good luck.

[edit]
I tried “HCI.cpp change on the Seeed Xiao nRF52840” in “issue#284” but it had no effect.