NRF52840 seemingly not disconnecting from nRF Connect App

Hello again.

I have a simple program on an NRF52840. The program transmits sensor data to the nRF Connect App. It all seems to work, however, I’ve noticed that when I press “DISCONNECT” on the nRF Connect App, the NRF52840 seemingly stays connected. For this simple program, it’s not a big deal, however, I imagine this would start to cause issues when connecting to a PC or Chrome browser Application.

Here’s a simplified version of my code. All this is is the MAX30102 example code, transmitted via BLE.

// This script connects a Seeed Xiao NRF52840 to the

// NRF Connect App and displays Average Heart Rate

// from the MAX30102 Sensor.

// Data is sent as a String.

#include <ArduinoBLE.h>

#include <Wire.h>

#include “MAX30105.h”

#include “heartRate.h”

MAX30105 particleSensor;

const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good.

byte rates[RATE_SIZE]; //Array of heart rates

byte rateSpot = 0;

long lastBeat = 0; //Time at which the last beat occurred

float beatsPerMinute;

int beatAvg;

// Bluetooth® Low Energy HR Service

BLEService HRService(“19B10000-E8F2-537E-4F6C-D104768A1214”);

// Bluetooth® Low Energy Switch Characteristic - custom 128-bit UUID, read and writable by central

BLEStringCharacteristic switchCharacteristic(“19B10001-E8F2-537E-4F6C-D104768A1214”, BLERead | BLENotify, 20);

void setup() {

Serial.begin(9600);

Serial.println(“Initializing…”);

// Initialize sensor

if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed

{

Serial.println("MAX30105 was not found. Please check wiring/power. ");

while (1);

}

Serial.println(“Place your index finger on the sensor with steady pressure.”);

particleSensor.setup(); //Configure sensor with default settings

particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running

particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED

// begin initialization

if (!BLE.begin()) {

Serial.println("starting the HR Demo has failed!");

while (1);

}

// set advertised local name and service UUID:

BLE.setLocalName(“Nelevate HR 1”);

BLE.setAdvertisedService(HRService);

// add the characteristic to the service

HRService.addCharacteristic(switchCharacteristic);

// add service

BLE.addService(HRService);

// start advertising

BLE.advertise();

Serial.println(“Seeed Studio XIAO nRF52840 active, waiting for connections…”);

}

void loop() {

// listen for Bluetooth® Low Energy peripherals to connect:

BLEDevice central = BLE.central();

// if a central is connected to peripheral:

if (central) {

Serial.print("Connected to central: ");

// print the central's MAC address:

Serial.println(central.address());

// while the central is still connected to peripheral:

while (central.connected()){

  long irValue = particleSensor.getIR();

  if (checkForBeat(irValue) == true)

  {

    //We sensed a beat!

    long delta = millis() - lastBeat;

    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20)

    {

      rates\[rateSpot++\] = (byte)beatsPerMinute; //Store this reading in the array

      rateSpot %= RATE_SIZE; //Wrap variable

      //Take average of readings

      beatAvg = 0;

      for (byte x = 0 ; x < RATE_SIZE ; x++)

        beatAvg += rates\[x\];

      beatAvg /= RATE_SIZE;

    }

  }

  if (irValue < 50000){

    Serial.println("No Finger Detected");

    String myString = "No Finger";

    switchCharacteristic.setValue(myString);

  }

  else{

    Serial.print("Avg BPM: ");

    Serial.println(beatAvg);

    String myString = "Avg BPM: ";

    myString += String(beatAvg);

    switchCharacteristic.setValue(myString); 

  }

}

// when the central disconnects, print it out:

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

Serial.println(central.address());

}

}

NOTE: When I hit “DISCONNECT” on the nRF Connect App, the last print message does not appear in the Serial Console.

When my friend tested this code by connecting directly to a simple Chrome Application that he wrote, it will connect and work fine; but as soon as he tries to connect from a different computer, the NRF52840 refuses to connect. All signs point to the NRF52840 not disconnecting from the initial Chrome Application.

I tried to add the following code after the Disconnect Serial Print at the bottom of the program, but it did not resolve anything:

BLE.disconnect();

BLE.advertise();

Any ideas??

Best,
Joe

Hi there,

SO , Your peripheral is probably not “ignoring” disconnect. The bigger problem is that your sketch is using a blocking while (central.connected()) loop and not servicing BLE events the way ArduinoBLE expects.

while (central.connected()) {
  ...
  switchCharacteristic.setValue(myString);
}

Arduino’s own peripheral callback example uses BLE.setEventHandler(...) and calls BLE.poll() continuously in loop(). ArduinoBLE’s event handling is polling-based, so if your loop spends too long doing sensor work and string updates inside while (central.connected()), the disconnect event can be delayed or missed in practice. (GitHub) :backhand_index_pointing_left: :wink: This Git.

I would restructure it like this:

  • remove the blocking while (central.connected())
  • use BLEConnected / BLEDisconnected callbacks
  • call BLE.poll() every loop
  • only send data when a bleConnected flag is true
  • restart advertising in the disconnect callback

Also, there is a known ArduinoBLE pattern where central.connected() can stay true after the central disappears, including on XIAO nRF52840 setups, so you’re not imagining this.

[2]: `BLE.central().connected()` is `true` when a disconnect has happened · Issue #334 · arduino-libraries/ArduinoBLE · GitHubBLE.central().connected() is true when a disconnect has happened · Issue #334 · arduino-libraries/ArduinoBLE · GitHub” :backhand_index_pointing_left: This one is Rare :grin:

I would try something around this technique :crossed_fingers:

#include <ArduinoBLE.h>
#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"

MAX30105 particleSensor;

BLEService HRService("19B10000-E8F2-537E-4F6C-D104768A1214");
BLEStringCharacteristic hrCharacteristic(
  "19B10001-E8F2-537E-4F6C-D104768A1214",
  BLERead | BLENotify,
  20
);

const byte RATE_SIZE = 4;
byte rates[RATE_SIZE];
byte rateSpot = 0;
long lastBeat = 0;
float beatsPerMinute = 0;
int beatAvg = 0;

volatile bool bleConnected = false;

void onConnect(BLEDevice central) {
  bleConnected = true;
  Serial.print("Connected to central: ");
  Serial.println(central.address());
}

void onDisconnect(BLEDevice central) {
  bleConnected = false;
  Serial.print("Disconnected from central: ");
  Serial.println(central.address());

  // Start advertising again after disconnect
  BLE.advertise();
}

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

  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
    Serial.println("MAX30105 was not found. Check wiring/power.");
    while (1);
  }

  particleSensor.setup();
  particleSensor.setPulseAmplitudeRed(0x0A);
  particleSensor.setPulseAmplitudeGreen(0);

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

  BLE.setLocalName("Nelevate HR 1");
  BLE.setAdvertisedService(HRService);

  HRService.addCharacteristic(hrCharacteristic);
  BLE.addService(HRService);

  BLE.setEventHandler(BLEConnected, onConnect);
  BLE.setEventHandler(BLEDisconnected, onDisconnect);

  hrCharacteristic.setValue("Starting...");
  BLE.advertise();

  Serial.println("Waiting for connections...");
}

void loop() {
  // Very important with ArduinoBLE
  BLE.poll();

  if (!bleConnected) {
    delay(10);
    return;
  }

  long irValue = particleSensor.getIR();

  if (checkForBeat(irValue)) {
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20) {
      rates[rateSpot++] = (byte)beatsPerMinute;
      rateSpot %= RATE_SIZE;

      beatAvg = 0;
      for (byte x = 0; x < RATE_SIZE; x++) {
        beatAvg += rates[x];
      }
      beatAvg /= RATE_SIZE;
    }
  }

  String msg;
  if (irValue < 50000) {
    msg = "No Finger";
  } else {
    msg = "Avg BPM: " + String(beatAvg);
  }

  hrCharacteristic.setValue(msg);

  // Keep loop responsive
  delay(20);
}

HTH
GL :slight_smile: PJ :v:

1 Like

Thank you so much, this worked like a charm!

1 Like