Bluetooth Usage (Seeed nRF52 Boards Library)¶
Seeed Studio XIAO nRF52840 and Seeed Studio XIAO nRF52840 Sense both support Bluetooth connectivity. This wiki will introduce the basics Bluetooth function used by "Seeed nRF52 Boards Library".
Getting Started¶
Hardware Required¶
- 1 x Seeed Studio XIAO nRF52840 or Seeed Studio XIAO nRF52840 Sense
- 1 x Smartphone with bluetooth connectivity
- 1 x USB Type-C cable
Software Required¶
Installation¶
Since the function we apply is packing in the "Seeed nRF52 Boards Library", we don't have to install another third-party library. You can skip the step.
Application examples¶
We here will introduce you Bluetooth applications.
Demo 1 Wireless Connect a PC Keyboard to the Mobile Phone¶
Step 1. Launch the Arduino application.

Step 2. Select your development board model and add it to the Arduino IDE. Here we are using "Seeed nRF52 Boards Library".
For the board libraries installation, please refer to this tutorial to finish installation.

Step 3. Nevigate to "File -> Examples -> Adafruit Bluefruit nRF52 Libraries -> Peripheral -> blehid_keyboard" and open the "blehid_keyboard" exapmle file.

Step 4. Click "Upload" and then open the "monitor" on the upper right corner of the Arduino IDE. The monitor will be shown as:

Step 5. Open the "nRF Connect for Mobile" App or "LightBlue" App on your mobile phone, meanwhile make sure your phone is connecting with Bluetooth. After a while, you will find a device named "XIAO nRF52840" listed.
- For nRF Connect for Mobile APP it should be like:

- For LightBlue APP it should be like:

Step 6. Simply click the device and the connection will be completed automatically. After that we can type characters into the monitor with PC keyboard, and then see what happens on your mobile phone.

Demo 2 Two XIAO nRF52840 control LED via Bluetooth communication.¶
In this example, we will use two XIAO nRF52840, using their Bluetooth capabilities to communicate. One of the XIAOs acts as a host and is connected to the XIAO expansion board, sending control commands via the expansion board's buttons. The other XIAO acts as a slave.
Before you start, please be prepared to do the following.
![]() |
![]() |
Seeed Studio XIAO Expansion board | 2 x Seeed XIAO BLE nRF52840 Sense |
Please select one of the XIAO nRF52840, which does not require any device to be connected, and upload the program below directly. It will serve as the host.
#include <bluefruit.h>
BLEClientBas clientBas; // battery client
BLEClientDis clientDis; // device information client
BLEClientUart clientUart; // bleuart client
const int ledPin = LED_BUILTIN; // pin to use for the LED
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
// set LED pin to output mode
pinMode(ledPin, OUTPUT);
Serial.println("Bluefruit52 Central BLEUART Example");
Serial.println("-----------------------------------\n");
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName("Bluefruit52 Central");
// Configure Battery client
clientBas.begin();
// Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback);
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don't use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don't stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print("BLE UART service detected. Connecting ... ");
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}else
{
// For Softdevice v6: after received a report, scanner will be paused
// We need to call Scanner resume() to continue scanning
Bluefruit.Scanner.resume();
}
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println("Connected");
Serial.print("Dicovering Device Information ... ");
if ( clientDis.discover(conn_handle) )
{
Serial.println("Found it");
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print("Manufacturer: ");
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print("Model: ");
Serial.println(buffer);
}
Serial.println();
}else
{
Serial.println("Found NONE");
}
Serial.print("Dicovering Battery ... ");
if ( clientBas.discover(conn_handle) )
{
Serial.println("Found it");
Serial.print("Battery level: ");
Serial.print(clientBas.read());
Serial.println("%");
}else
{
Serial.println("Found NONE");
}
Serial.print("Discovering BLE Uart Service ... ");
if ( clientUart.discover(conn_handle) )
{
Serial.println("Found it");
Serial.println("Enable TXD's notify");
clientUart.enableTXD();
Serial.println("Ready to receive from peripheral");
}else
{
Serial.println("Found NONE");
// disconnect since we couldn't find bleuart service
Bluefruit.disconnect(conn_handle);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @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;
Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
}
/**
* Callback invoked when uart received data
* @param uart_svc Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void bleuart_rx_callback(BLEClientUart& uart_svc)
{
Serial.print("[RX]: ");
while ( uart_svc.available() )
{
// Serial.print( (char) uart_svc.read() );
if ((char)uart_svc.read() == '1') { // any value other than 0
Serial.println("LED on");
digitalWrite(ledPin, HIGH); // will turn the LED on
} else { // a 0 value
Serial.println(F("LED off"));
digitalWrite(ledPin, LOW); // will turn the LED off
}
}
Serial.println();
}
void loop()
{
if ( Bluefruit.Central.connected() )
{
// Not discovered yet
if ( clientUart.discovered() )
{
// Discovered means in working state
// Get Serial input and send to Peripheral
if ( Serial.available() )
{
delay(2); // delay a bit for all characters to arrive
char str[20+1] = { 0 };
Serial.readBytes(str, 20);
clientUart.print( str );
}
}
}
}
The main purpose of this program is to make XIAO a Bluetooth device that can be searched for and connected to by other Bluetooth devices. Once connected, you can control the LEDs on the XIAO by sending a 0 or a 1 to turn them off or on respectively.
Put the Seeed Studio XIAO nRF52840 on the expansion board.

And for this purpose the XIAO with the extension board connected uploads the following procedure.
#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.h>
// BLE Service
BLEDfu bledfu; // OTA DFU service
BLEDis bledis; // device information
BLEUart bleuart; // uart over ble
BLEBas blebas; // battery
// variables for button
const int buttonPin = D1;
int oldButtonState = LOW;
void setup()
{
Serial.begin(115200);
// Blocking wait for connection when debug mode is enabled via IDE
while ( !Serial );
// configure the button pin as input
pinMode(buttonPin, INPUT_PULLUP);
Serial.println("Bluefruit52 BLEUART Example");
Serial.println("---------------------------\n");
// Setup the BLE LED to be enabled on CONNECT
// Note: This is actually the default behavior, but provided
// here in case you want to control this LED manually via PIN 19
Bluefruit.autoConnLed(true);
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
//Bluefruit.setName(getMcuUniqueID()); // useful testing with multiple central connections
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Configure and Start Device Information Service
bledis.setManufacturer("Adafruit Industries");
bledis.setModel("Bluefruit Feather52");
bledis.begin();
// Configure and Start BLE Uart Service
bleuart.begin();
// Start BLE Battery Service
blebas.begin();
blebas.write(100);
// Set up and start advertising
startAdv();
Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode");
Serial.println("Once connected, enter character(s) that you wish to send");
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// 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 loop()
{
// Forward data from HW Serial to BLEUART
int buttonState = digitalRead(buttonPin);
if (oldButtonState != buttonState) {
// button changed
oldButtonState = buttonState;
if (buttonState) {
Serial.println("button pressed");
// button is pressed, write 1 to turn the LED on
bleuart.write( '1' );
} else {
Serial.println("button released");
// button is released, write 0 to turn the LED off
bleuart.write( '0' );
}
}
// Delay to wait for enough input, since we have a limited transmission buffer
delay(2);
// uint8_t buf[64];
// int count = Serial.readBytes(buf, sizeof(buf));
// bleuart.write( buf, count );
// // Forward from BLEUART to HW Serial
// while ( bleuart.available() )
// {
// uint8_t ch;
// ch = (uint8_t) bleuart.read();
// Serial.write(ch);
// }
}
// callback invoked when central connects
void connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char central_name[32] = { 0 };
connection->getPeerName(central_name, sizeof(central_name));
Serial.print("Connected to ");
Serial.println(central_name);
}
/**
* 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;
Serial.println();
Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
}
Once you have uploaded the program, turn on the serial monitor and the program will start searching for a nearby Bluetooth device (XIAO) and connect to it.

Once the successful connection message is displayed in the serial monitor, you can control the switching of the other XIAO nRF52840 LED via key D1 of the expansion board.

Of course, if you don't have an extension board, you can also use your own buttons or other devices.
What's more?¶
If you want to try out more examples you can navigate to File > Examples > Examples for Seeed Studio XIAO nRF52840 > Adafruit Bluefruit nRF52 Libraries
and check all the examples under Adafruit Bluefruit nRF52 Libraries.
Tech Support¶
Please do not hesitate to submit the issue into our forum.