Xiao nrf52840 not reconnecting to andriod

I’ve got an Seeed XIAO nRF52840 and would like to turn on an LED automatically when paired or connected via Bluetooth to an Android phone with no manual input or app interaction. It’s meant to check if I’ve forgotten my phone in the car. So the use case is on startup the Seeed light is on, when I pair with the Seeed the light goes off, when the Seeed is out of range of the phone the light is on again and when the phone is back in range the light goes out

The code I have works except when the Seeed is out of range it never reconnects… What am I missing ?

Here’s the code :

#include <bluefruit.h>

#define LED_PIN D2  // pick the pin where your LED is connected

void setup() {
  Serial.begin(115200);
  while (!Serial);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  Bluefruit.begin();
  Bluefruit.setName("XIAO_nRF52840");
  Bluefruit.setTxPower(-20);

  // Set callback functions
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // Start advertising
  startAdv();
  digitalWrite(LED_PIN, HIGH);    // turn LED on when disconnected
  Serial.println("BLE Peripheral started. Ready for connection... \n");
}

void loop() {
  // Nothing needed here, all handled in callbacks
}

void connect_callback(uint16_t conn_handle) {
  digitalWrite(LED_PIN, LOW);   // turn LED off when connected
  Serial.print("Connect callback \n");
}

void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
  digitalWrite(LED_PIN, HIGH);    // turn LED on when disconnected
  Serial.print("Disconnect callback \n");
}

void startAdv(void) {
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.ScanResponse.addName();

  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in units of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // fast mode for 30 sec
  Bluefruit.Advertising.start(0);                // start advertising forever
}

Thanks for all the help !!

Hi there,

And Welcome Here…

So what you have won’t reliably “auto-reconnect” on Android without an app. Two key points why, then a drop-in fix:

Why it never reconnects

  • Android does not auto-reconnect to arbitrary BLE peripherals just because they’re “paired.” It’ll reconnect on its own only for certain system-managed profiles (e.g., HID keyboards/mice), or when an app on the phone actively reconnects in the background. Pairing alone doesn’t create an OS watchdog.
  • Your sketch relies on restartOnDisconnect(true), which should restart advertising—but some library/version combos are picky. A bullet-proof pattern is to explicitly restart advertising in disconnect_callback() and never block boot on USB serial.

Minimal code fixes (keeps your logic)

  • Don’t stall on while (!Serial)—that can delay init and advertising.
  • Explicitly call startAdv() in disconnect_callback() (don’t depend solely on restartOnDisconnect).
  • Keep fast advertising running long enough to be seen again (or just keep it “fast” indefinitely if you want aggressive reconnect).

The “While Serial” will block EVERYTHING if No Usb is plugged in , IE battery only. Won’t run unless You comment that out.:+1:

#include <bluefruit.h>

#define LED_PIN D2

void startAdv(void);

void setup() {
  Serial.begin(115200);
  // Do NOT block waiting for USB; we want immediate advertising
  // while (!Serial) {/* no wait */}

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);   // light ON when not connected

  Bluefruit.begin();
  Bluefruit.setName("XIAO_nRF52840");
  Bluefruit.setTxPower(0);       // -20 was very low; 0 dBm is a sane default

  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // Conservative: we’ll explicitly restart in the disconnect callback
  Bluefruit.Advertising.restartOnDisconnect(false);

  startAdv();

  Serial.println("BLE Peripheral started. Ready for connection…");
}

void loop() {
  // nothing
}

void connect_callback(uint16_t conn_handle) {
  digitalWrite(LED_PIN, LOW);    // OFF when connected
  Serial.println("Connected");
}

void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
  digitalWrite(LED_PIN, HIGH);   // ON when disconnected/out of range
  Serial.print("Disconnected, reason=0x"); Serial.println(reason, HEX);

  // Be explicit: immediately resume advertising so the phone can find us again
  startAdv();
}

void startAdv(void) {
  Bluefruit.Advertising.clearData();
  Bluefruit.ScanResponse.clearData();

  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.ScanResponse.addName();

  // Keep advertising easy to catch — either keep "fast" or make slow modest
  Bluefruit.Advertising.setInterval(32 /*20ms*/, 80 /*50ms*/);
  Bluefruit.Advertising.setFastTimeout(0);     // never drop to slow (optional)
  // If you prefer slow after a while, use:
  // Bluefruit.Advertising.setFastTimeout(60);
  // Bluefruit.Advertising.setInterval(160 /*100ms*/, 244 /*152.5ms*/);

  Bluefruit.Advertising.start(0);              // advertise forever
}

Well, now If you truly want AUTO-Reconnect with ZERO app.
Two robust options:

  1. Run as a BLE HID device (keyboard/mouse). Android’s system manages those and will reconnect in the background. Adafruit’s Bluefruit nRF52 library has HID examples—light goes off when HID connected, back on when not.
  2. Write a tiny Android background service (Tasker/MacroDroid or a simple app) that reconnects to the GATT peripheral. That’s how most BLE gadgets do it.

HTH
GL :slight_smile: PJ

The MIT AI2 app inventor for App I use on Android 15 (moto RaZr +) BLE to Nrf52840 Sense. It works this way, My app scans BLE I can connect to my_chioce device. then Disconnect , the PAIR for it. Keep in mind the firmware doesn’t have any bonding or pairing code in it(nrf52840) side. The phone Remembers the connection because I did the pairing, it doesn’t auto connect BUT when I scan in my app its first Always in the list and connects fast when I pick it. once connected I get 3 notify’s (motion,FALL,Battery) Can Lock and Unlock the device.
:+1:

PJ_Glasso, wow thanks so much !!

That is a great explanation and it never occurred to me to write it as a hid device, I’ll give that a shot

Thanks again for all the help !!

1 Like

Hi there,

Sure it’s pretty non obvious though… I was like wait a minute…
Try scanning for BLE peripherals with around 16-20K other knuckle heads and their phones / BLE earbuds & hand-warmers :grin: … Jezz the amount of devices is UN-managable (went to NAB show in NYC)
Had to have cat like reflexes to catch it go by in the scan phase.
one word “FILTER” LOL .
good luck you’ll get going with that forsure, HID and midi stuff also i have seen auto connecting BLE .

GL :slight_smile: PJ :v:

Yea, I just recoded and it woks just like I wanted

BTW the TxPower of -20 is for a very short range, works great too.

Thanks again !!

1 Like

Hi there,

Yea, I thought so,(-20) you tried the shortest range first , that’s Great!
Do us a Solid and Mark my post as the solution so others can find it Fast! and thank you for the contribution. :+1:

GL :slight_smile: PJ :v:

also you could edit your first post and use the code tags above “</>” and paste it in there makes it look and read way better. :v:

Just to be clear, the solution (that worked for me) was to rewrite as an HID device, using the blehid_keyboard example