Here is the Demo of 2 simultaneous BLE connections to ONE Xiao Nrf52840
its a Adafruity based example. Can be adapted to output the IMU data and Battery info if need be. Good basic starting point for any BLE curious out there with the idea of more than one connection controlling the asset (led).
HTH
GL PJ
/*********************************************************************
This is an example from adafruit
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
This sketch implement part of Nordic custom LED Button Service (LBS).
Install "nRF Blinky" app on your Android/iOS to control the on-board LED
- https://apps.apple.com/us/app/nrf-blinky/id1325014347
- https://play.google.com/store/apps/details?id=no.nordicsemi.android.nrfblinky
Works with Xiao Nrf52840 sense MCU As-is. BSP 1.1.1 allows 2 simultanious connections
I used Nrf_desktop with Nordic $9 dongle and , Nrf_connect for android app. at the same time
each able to control the LED on the Xiao and get notify of change.
*********************************************************************/
#include <bluefruit.h>
#include <Adafruit_NeoPixel.h>
// max concurrent connections supported by this example
#define MAX_PRPH_CONNECTION 2
uint8_t connection_count = 0;
/* Nordic Led-Button Service (LBS)
* LBS Service: 00001523-1212-EFDE-1523-785FEABCD123
* LBS Button : 00001524-1212-EFDE-1523-785FEABCD123
* LBS LED : 00001525-1212-EFDE-1523-785FEABCD123
*/
const uint8_t LBS_UUID_SERVICE[] =
{
0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
0xDE, 0xEF, 0x12, 0x12, 0x23, 0x15, 0x00, 0x00
};
const uint8_t LBS_UUID_CHR_BUTTON[] =
{
0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
0xDE, 0xEF, 0x12, 0x12, 0x24, 0x15, 0x00, 0x00
};
const uint8_t LBS_UUID_CHR_LED[] =
{
0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15,
0xDE, 0xEF, 0x12, 0x12, 0x25, 0x15, 0x00, 0x00
};
BLEService lbs(LBS_UUID_SERVICE);
BLECharacteristic lsbButton(LBS_UUID_CHR_BUTTON);
BLECharacteristic lsbLED(LBS_UUID_CHR_LED);
// Use on-board button if available, else use A0 pin
#ifdef PIN_BUTTON1
uint8_t button = PIN_BUTTON1;
#else
uint8_t button = A0;
#endif
// CPB button active state is HIGH, all other is low
#ifdef ARDUINO_NRF52840_CIRCUITPLAY
#define BUTTON_ACTIVE HIGH
#else
#define BUTTON_ACTIVE LOW
#endif
uint8_t buttonState;
#ifdef PIN_NEOPIXEL
#ifndef NEOPIXEL_NUM
#define NEOPIXEL_NUM 1
#endif
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(NEOPIXEL_NUM, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
#endif
void setup()
{
pinMode(button, BUTTON_ACTIVE ? INPUT_PULLDOWN : INPUT_PULLUP);
buttonState = (BUTTON_ACTIVE == digitalRead(button));
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, 1-LED_STATE_ON); // led off
#ifdef PIN_NEOPIXEL
neopixel.begin();
#endif
Serial.begin(115200);
//while ( !Serial ) delay(10); // for nrf52840 with native usb
delay (3000);
Serial.println("Bluefruit52 nRF Blinky Example");
Serial.println("------------------------------\n");
// Initialize Bluefruit with max concurrent connections as Peripheral = MAX_PRPH_CONNECTION, Central = 0
Serial.println("Initialise the Bluefruit nRF52 module");
Bluefruit.begin(MAX_PRPH_CONNECTION, 0);
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// Setup the LED-Button service using
Serial.println("Configuring the LED-Button Service");
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.. Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was 'begin()'ed!
lbs.begin();
// Configure Button characteristic
// Properties = Read + Notify
// Permission = Open to read, cannot write
// Fixed Len = 1 (button state)
lsbButton.setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
lsbButton.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
lsbButton.setFixedLen(1);
lsbButton.begin();
lsbButton.write8(buttonState);
// Configure the LED characteristic
// Properties = Read + Write
// Permission = Open to read, Open to write
// Fixed Len = 1 (LED state)
lsbLED.setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE);
lsbLED.setPermission(SECMODE_OPEN, SECMODE_OPEN);
lsbLED.setFixedLen(1);
lsbLED.begin();
lsbLED.write8(0x01); // led = on when connected
lsbLED.setWriteCallback(led_write_callback);
// Setup the advertising packet(s)
Serial.println("Setting up the advertising");
startAdv();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include HRM Service UUID
Bluefruit.Advertising.addService(lbs);
// Secondary Scan Response packet (optional)
// Since there is no room for 'Name' in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
}
void setLED(bool on)
{
// data = 1 -> LED = On
// data = 0 -> LED = Off
digitalWrite(LED_BUILTIN, on ? LED_STATE_ON : (1-LED_STATE_ON));
#ifdef PIN_NEOPIXEL
uint32_t c = neopixel.Color(0x00, 0x00, on ? 0x20 : 0x00);
neopixel.fill(c, 0, NEOPIXEL_NUM);
neopixel.show();
#endif
}
void led_write_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len)
{
(void) conn_hdl;
(void) chr;
(void) len; // len should be 1
// data = 1 -> LED = On
// data = 0 -> LED = Off
setLED(data[0]);
}
void loop()
{
delay(10); // poll button every 10 ms
uint8_t newState = (BUTTON_ACTIVE == digitalRead(button));
// only notify if button state changes
if ( newState != buttonState)
{
buttonState = newState;
lsbButton.write8(buttonState);
// notify all connected clients
for (uint16_t conn_hdl=0; conn_hdl < MAX_PRPH_CONNECTION; conn_hdl++)
{
if ( Bluefruit.connected(conn_hdl) && lsbButton.notifyEnabled(conn_hdl) )
{
lsbButton.notify8(conn_hdl, buttonState);
}
}
}
}
// callback invoked when central connects
void connect_callback(uint16_t conn_handle)
{
(void) conn_handle;
setLED(true);
lsbLED.write8(0x01);
connection_count++;
Serial.print("Connection count: ");
Serial.println(connection_count);
// Keep advertising if not reaching max
if (connection_count < MAX_PRPH_CONNECTION)
{
Serial.println("Keep advertising");
Bluefruit.Advertising.start(0);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
setLED(false);
lsbLED.write8(0x00);
Serial.println();
Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
connection_count--;
}
Serial output…
Bluefruit52 nRF Blinky Example
------------------------------
Initialise the Bluefruit nRF52 module
Configuring the LED-Button Service
Setting up the advertising
Connection count: 1
Keep advertising
Connection count: 2
Disconnected, reason = 0x13
Disconnected, reason = 0x13
I like Nrf_connect for Android but the Blinky app is usable too!
Qr for Blinky.