Nano 33 BLE stops responding after a short time

Hi, I'm somewhat new to Arduino and using Bluetooth so here's the problem I've been having: I'm trying to write a program that scans for other devices with a specific UUID when they are in a certain range and logs them. This information can then later be sent to a phone if one connects to the board. This functionality works, but the Arduino keeps freezing randomly, making me have to reset.

The scan function is with duplicates, so the Arduino will start outputting data to the Serial monitor when it finds a device and keeps doing so, but after anywhere from 10 to 30 seconds, it stops outputting to the Serial monitor even though the device is still powered on and in range. After this, I can no longer find the Arduino listed in my phone's list of BLE devices and cannot connect to it despite normally being able to. I can only conclude that something about scanning for devices is freezing the board.

Also, if I try finding a device in an environment with many Bluetooth connections, the device with the correct UUID won't be detected at all, despite this working in an environment with only a few Bluetooth connections. Is this issue because I'm trying to both scan and advertise simultaneously? Is it impossible for a board to successfully be both a central and a peripheral at different times? Any help is appreciated.

// ArduinoBLE - Version: Latest 
#include <ArduinoBLE.h>

BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service

// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
BLEByteCharacteristic syncCharacteristic("19B10002-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite | BLENotify);
BLEByteCharacteristic dataCharacteristic("19B10003-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite | BLENotify);

const int ledPin = 9; // pin to use for the LED
const float measuredPower = -70; //built-in BLE value
const float nConstant = 5; //environmental factor
const int uniqueID = 0;

float rssiValue;
float distance;
float traceDistance = 1.829; //in meters

byte currentMaskID = 0;
byte contactList[20];
int contactListIndex = 0;
unsigned long lastTime;

void setup() {
  Serial.begin(9600);

  // set LED pin to output mode
  pinMode(ledPin, OUTPUT);

  // begin initialization
  if (!BLE.begin()) 
  {
    Serial.println("starting BLE failed!");
    while (1);
  }

  // set advertised local name and service UUID:
  BLE.setLocalName("SmartMask0");
  BLE.setAdvertisedService(ledService);

  // add the characteristic to the service
  ledService.addCharacteristic(switchCharacteristic);
  ledService.addCharacteristic(syncCharacteristic);
  ledService.addCharacteristic(dataCharacteristic);

  // add service
  BLE.addService(ledService);

  // set the initial value for the characeristic:
  switchCharacteristic.writeValue(0);
  syncCharacteristic.writeValue(0);
  dataCharacteristic.writeValue(0);

  // start scanning for peripheral
  BLE.scanForUuid("19B10003-E8F2-537E-4F6C-D104768A1214", true);

  // start advertising
  BLE.advertise();

  Serial.println("BLE enabled");
}

void loop() {
  // listen for BLE peripherals to connect:
  BLEDevice central = BLE.central();
  BLEDevice peripheral = BLE.available();

  // if a central is connected to peripheral:
  if (central) 
  {
    BLE.stopScan();
    Serial.print("Connected to central: ");
    Serial.println(central.address());   // print the central's MAC address:

    // while the central is still connected to peripheral:
    while (central.connected()) 
    {
      // if the remote device wrote to the characteristic,
      // use the value to control the LED:
      if (switchCharacteristic.written())
      {
        if (switchCharacteristic.value())    // any value
        {
          Serial.println("LED on");
          digitalWrite(ledPin, HIGH);     // will turn the LED on
        }
        else
        {                                 // a 0 value
          Serial.println("LED off");
          digitalWrite(ledPin, LOW);     // will turn the LED off
        }
      }
      if (syncCharacteristic.written())
      {
        if (syncCharacteristic.value())    //a value
        {
          for(int i = 0; i < 20; i++)  //will send each ID one by one
          {
            lastTime = millis();
            dataCharacteristic.writeValue(contactList[i]);
            contactList[i] = 0;
            while(millis() - lastTime < 200);
          }
          contactListIndex = 0;
          Serial.println("List data has been sent.");
          syncCharacteristic.writeValue(0);
        }
      }
    }

    // when the central disconnects, print it out:
    Serial.print(F("Disconnected from central: "));
    Serial.println(central.address());
    BLE.scanForUuid("19B10003-E8F2-537E-4F6C-D104768A1214", true);
    BLE.advertise();
  }
  
  if (peripheral)  // find distance of other devices with matching UUID
  {
    rssiValue = peripheral.rssi();
    distance = pow(10, (measuredPower - rssiValue)/(10 * nConstant));
    Serial.print("Mask found. Distance: ");
    Serial.println(distance);
    
    if(distance < traceDistance)  //if distance falls into threshold
    {
      digitalWrite(ledPin, HIGH);  //turn on the LED
      bool isInList = false;
      currentMaskID = peripheral.localName().substring(9).toInt();
      
      for(int i = 0; i < 20; i++)  
      {
        if(contactList[i] == currentMaskID)
        {
          isInList = true;
          Serial.println("Mask already in list.");  //check if this mask is already logged
          break;
        }
      }
      if(!isInList)  //add to list if not
      {
        contactList[contactListIndex] = currentMaskID;
        Serial.print("Mask logged: ");
        Serial.println(peripheral.localName());
        contactListIndex++;
      }
    }
    else
    {
      digitalWrite(ledPin, LOW);   //not in threshold, turn off LED
    }
  }
}

Welcome to the forum.

I had a quick look trough your code and this is the first thing I noted.

if (syncCharacteristic.written())
{
  if (syncCharacteristic.value())    //a value
  {
    for(int i = 0; i < 20; i++)  //will send each ID one by one
    {
      lastTime = millis();
      dataCharacteristic.writeValue(contactList[i]);
      contactList[i] = 0;
      while(millis() - lastTime < 200);
    }
    contactListIndex = 0;
    Serial.println("List data has been sent.");
    syncCharacteristic.writeValue(0);
  }
}

In my opinion this is not a good way to do this.
First you are effectively using a delay() function which is not a good idea and should be avoided.
Second you are trying to serialize data into an interface that is not a pipe. A BLE peripheral is more like a web server with data available for other to read at their will. You can simply create a characteristic which holds multiple values. Use a generic BLECharacteristic e.g.,

BLECharacteristic dataCharacteristic(UUID, BLERead | BLEWrite | BLENotify, 20);

This will also reduce the overhead. All bytes can be transferred in one read from the client. Otherwise, you will need to notify the client 20 times and have the additional bits (~80 bits per packet) for each transfer. This happens in the background, but it will still cost you battery life.

About the following comment in your code.

//will send each ID one by one

This is not true. You are writing the data to the characteristic. The client decides whether it will read the data or not. If the client does not read it, the data is not sent. This might sound like a small thing but may be helpful to understand how BLE works and help you in the future with some other BLE issue.

Alright that makes sense. In this instance, I did make a simple accompanying mobile app for connecting to the Arduino that does read each of the bytes as they come in, but obviously it would be much more efficient to send it all in one go. I'll do that now that I realize that's possible. Of course this code doesn't pertain to the scanning freeze but thanks for the advice.

Are you sure?

Another potential issue is the following:

BLEDevice central = BLE.central();
BLEDevice peripheral = BLE.available();

...
while (central.connected())
{
// this while() will run as long as a central is connected maybe 10 minutes ...
}
...
// When the code arrives here, the peripheral might be long gone 
if (peripheral)
{

}