edit

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

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.