How to use BLE with: Seeed XIAO nRF52840 Sense

I have soon tryed everything to make my Seeed XIAO nRF52840 Sense connect to my computer with BLE. I program the Seeed XIAO nRF52840 Sense with Arduino IDE… but no success. My computer can see the module, but not connect to it.
The script shall connect the Module to a computer via Bluetooth and when it ”hear” a loud sound it shall send a ”A” to the computer.
Here is one of all my script I have tested:
The comments are in Swedish. Sorry.


#include <bluefruit.h>
#include <Adafruit_TinyUSB.h>

// Variabler för ljuddetektion
const int micPin = A0;        // Mikrofon ansluten till analog pin A0
const int threshold = 500;    // Tröskelvärde för ljudnivå

// Skapa BLE HID-tangentbord
BLEDis bledis;                // Enhetens namn- och informationstjänst
BLEHidAdafruit blehid;        // BLE HID-tjänst

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

  // Initiera BLE
  Bluefruit.begin();
  Bluefruit.setName("XIAO BLE Keyboard");

  // Initiera enhetstjänster
  bledis.setManufacturer("Seeed Studio");
  bledis.setModel("XIAO nRF52840");
  bledis.begin();

  // Initiera HID-tangentbord
  blehid.begin();

  // Starta annonsering (gör enheten synlig för Bluetooth-parning)
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
  Bluefruit.Advertising.addService(blehid);
  Bluefruit.Advertising.start();

  // Konfigurera mikrofonens ingång
  pinMode(micPin, INPUT);

  Serial.println("BLE HID-tangentbord redo.");
}

void loop() {
  // Läs av mikrofonens ljudnivå
  int soundLevel = analogRead(micPin);

  if (soundLevel > threshold) {
    Serial.println("Högt ljud upptäckt!");

    // Kontrollera om tangentbordet är anslutet via BLE
    if (blehid.isConnected()) {
      // Skicka bokstaven "A"
      blehid.keyboardPress(KEY_A);
      delay(100);               // Vänta för att simulera nedtryckning
      blehid.keyboardRelease(); // Släpp tangenten
    } else {
      Serial.println("Tangentbord ej anslutet via BLE.");
    }

    // Liten paus för att undvika flera avtryck
    delay(500);
  }

  delay(10); // Stabilitetsfördröjning
}

Regards

Hi Simoffen,
Your question is too vague to answer. Could you ask a more specific question?

Tx for your reply. I have updated my question. Cheers

The submitted sketch cannot be compiled as is. Please re-submit it using the **** symbol for code submission.

It looks like a sketch to send “A” from XIAO via BLE.
Is it another XIAO that receives the “A”? Or is there some other device that receives the “A”?

Are you using “Seeed nRF52 Boards 1.1.8 (so called non-mbed)” for your board support package?

Hi!
Tx for your time. So happy about your engagement.
The ”A” shall be sent from the Seeed. . . to the computer its connected to via BLE.

I really dont know if I’m using: “Seeed nRF52 Boards 1.1.8 (so called non-mbed)” for your board support package?
Have to check later. Where can I see that in Arduino IDE?
Cheers

You mean that it is received by a PC with built-in BLE functionality? If so, what is the application you use to receive on your PC?

To use <bluefruit.h>, the BSP must be non-mbed. If you can compile it, then non mbed is being used. You can also check by selecting “Seeed XIAO nRF52840 Sense” as the board selection.

I tried to compile your sketch, but I get some compile errors. If your sketches are written with reference to some example, please give us that example.

Why not try the following example first?
“File → Examples → Adafruit Bluefruit nRF52 Libraries → Peripheral → blehid_keyboard”

Is your problem that you get compilation errors? Or is there another problem?

Hi!
The software in my SEEED shall be connected via BLE to a computer and when the microphone in the SEEED hears a loud sound, it shall send the letter “A” to the computer its connected to via BLE.
I have succeded to connect now, tx to you :slight_smile: But now I have other problem… never ending story…

The code below can connect to a computer and also to my Iphone via BLE, but no “A” is sent. The Arduino IDE:s Serial monitor say its sent, but no A is written to the Iphone or the computer. So something is strange with that part of the code below:

#include <bluefruit.h>
#include <Adafruit_TinyUSB.h>

// Variabler för ljuddetektion
const int micPin = A0;       // Mikrofon ansluten till analog pin A0
const int threshold = 50;     // Sänkt tröskelvärde för högre känslighet

// Variabel för glidande medelvärde
int averageLevel = 0;

// Skapa BLE HID-tangentbord
BLEDis bledis;                // Enhetens namn- och informationstjänst
BLEHidAdafruit blehid;        // BLE HID-tjänst

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

  // Initiera BLE
  Bluefruit.begin();
  Bluefruit.setName("XIAO BLE Keyboard");

  // Initiera enhetstjänster
  bledis.setManufacturer("Seeed Studio");
  bledis.setModel("XIAO nRF52840");
  bledis.begin();

  // Initiera HID-tangentbord
  blehid.begin();

  // Starta annonsering (gör enheten synlig för Bluetooth-parning)
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
  Bluefruit.Advertising.addService(blehid);
  Bluefruit.Advertising.start();

  // Konfigurera mikrofonens ingång
  pinMode(micPin, INPUT);

  Serial.println("BLE HID-tangentbord redo.");
}

void loop() {
  // Läs av mikrofonens ljudnivå
  int soundLevel = analogRead(micPin);

  // Beräkna glidande medelvärde för att hantera bakgrundsljud
  averageLevel = (averageLevel * 9 + soundLevel) / 10;

  // Visa aktuellt ljudnivåvärde och medelvärde i seriell monitor
  Serial.print("Ljudnivå: ");
  Serial.print(soundLevel);
  Serial.print("  Medelvärde: ");
  Serial.println(averageLevel);

  // Kontrollera om ljudnivån överstiger tröskeln
  if (averageLevel > threshold) {
    Serial.println("Högt ljud upptäckt!");

    // Kontrollera om tangentbordet är anslutet via BLE
    if (Bluefruit.connected()) {
      Serial.println("Enheten är ansluten som tangentbord.");

      // Skicka bokstaven "A" via keyPress och keyRelease
      blehid.keyPress(0x04);      // KEY_A har värdet 0x04 enligt HID-specifikationen
      Serial.println("Skickar bokstaven A...");
      delay(500);                 // Vänta längre för att se om det hjälper
      blehid.keyRelease();        // Släpp tangenten
      Serial.println("Tangenten släppt.");
    } else {
      Serial.println("Tangentbord ej anslutet via BLE.");
    }

    // Liten paus för att undvika flera avtryck
    delay(500);
  }

  delay(10); // Stabilitetsfördröjning
}


I have tryed: “keyPress(KEY_A)” Does not work. I have tryed: “blehid.keyPress(0x28)” does not work.
I have tryed: " blehid.keyboardWrite(‘A’);" does not work and as you se abowe: “blehid.keyPress(0x04);” does not work.
Any ideas?
Cheers Fredrik

Hi! An update, with the code below everything works… But now I found out that I dont use the Built in Microphone… it senses when i come close to some of the pin.
I dont use Mbed and I have not found a way of making the SEEEDs mic work without that…Any ideas?
(And when i try to use Mbed, I got tons of errors when i compile…)

#include <bluefruit.h>
#include <Adafruit_TinyUSB.h>

// BLE HID service
BLEHidAdafruit blehid;  
BLEDis bledis;          

const int micPin = A0;        // Microphone pin
const int threshold = 100;     // Sound detection threshold

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

    // Initialize BLE
    Bluefruit.begin();
    Bluefruit.setName("XIAO BLE Keyboard");

    // BLE device information setup
    bledis.setManufacturer("Seeed Studio");
    bledis.setModel("XIAO nRF52840");
    bledis.begin();

    // Start HID service
    blehid.begin();
    Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
    Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
    Bluefruit.Advertising.addService(blehid);
    Bluefruit.Advertising.start();

    pinMode(micPin, INPUT);

    Serial.println("BLE HID Keyboard Ready.");
}

void sendKeyA() {
    uint8_t report[8] = {0};   // HID Report: Modifier, Reserved, Key1, ...
    report[2] = 0x04;          // HID Keycode for 'A'

    // Send key press
    Serial.println("Sending 'A' key press...");
    blehid.inputReport(1, report, sizeof(report));

    delay(10);                // Simulate key press duration

    // Send key release
    Serial.println("Sending 'A' key release...");
    report[2] = 0;             // Release the key
    blehid.inputReport(1, report, sizeof(report));
}

void loop() {
    int soundLevel = analogRead(micPin); // Read sound level
    Serial.print("Sound Level: ");
    Serial.println(soundLevel);

    if (soundLevel > threshold) { // Check if sound exceeds threshold
        if (Bluefruit.connected()) {
            Serial.println("Loud sound detected! Device connected. Sending 'A'...");
            sendKeyA();
        } else {
            Serial.println("Loud sound detected, but device is NOT connected.");
        }

        delay(10); // Prevent multiple detections in quick succession
    }

    delay(10); // Short stability delay
}

Below is an example of the built-in microphone.
File → Examples → nRF52840 PDM Adafruit Fork → PDMSerialPlotter

This getting more and more confusing…
Now I have solved the microphone problem, works ok. But now I can´t connect the BLE. My computer sees the SEEED, but can´t connect…
Can you see whats wrong with this code? I have tryed everything soon, even asked ChatGPT… no solution…

#include <ArduinoBLE.h>
#include <mic.h>

// BLE HID service and characteristic
BLEService hidService("1812"); // HID service UUID
BLECharacteristic inputReportCharacteristic("2a4d", BLERead | BLENotify, 8);

// Microphone settings
#define SAMPLES 800  // Number of audio samples per cycle
mic_config_t mic_config{
  .channel_cnt = 1,
  .sampling_rate = 16000,
  .buf_size = 1600,
#if defined(ARDUINO_ARCH_NRF52840)
  .debug_pin = LED_BUILTIN  // Debug pin (LED_BUILTIN)
#endif
};

NRF52840_ADC_Class Mic(&mic_config);  // Initialize the microphone
int16_t recording_buf[SAMPLES];   // Buffer to store audio data
volatile uint8_t recording = 0;
volatile static bool record_ready = false;
int soundLevel = 0;  // Calculated sound level from the microphone

// Threshold to trigger keypress
const int threshold = 100;

// Function to send 'A' as a keypress
void sendKeyA() {
    uint8_t report[8] = {0};  // HID Report: Modifier, Reserved, Key1, ...
    report[2] = 0x04;         // HID Keycode for 'A'

    // Send key press
    Serial.println("Sending 'A' key press...");
    inputReportCharacteristic.writeValue(report, sizeof(report));

    delay(10);  // Simulate keypress duration

    // Send key release
    report[2] = 0;  // Release the key
    inputReportCharacteristic.writeValue(report, sizeof(report));
}

// Callback function for PDM (Microphone)
static void audio_rec_callback(uint16_t *buf, uint32_t buf_len) {
    static uint32_t idx = 0;

    // Copy audio data from DMA buffer to our buffer
    for (uint32_t i = 0; i < buf_len; i++) {
        recording_buf[idx++] = buf[i];  // Store audio sample in the buffer

        if (idx >= SAMPLES) {
            idx = 0;
            recording = 0;
            record_ready = true;
            break;
        }
    }
}

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

    // Initialize BLE
    if (!BLE.begin()) {
        Serial.println("Starting BLE failed!");
        while (1);
    }
    BLE.setLocalName("XIAO BLE Keyboard");
    BLE.setAdvertisedService(hidService);
    hidService.addCharacteristic(inputReportCharacteristic);
    BLE.addService(hidService);
    BLE.advertise();

    // Initialize the microphone and set callback
    Mic.set_callback(audio_rec_callback);

    if (!Mic.begin()) {
        Serial.println("Mic initialization failed");
        while (1);
    }

    Serial.println("Mic initialization done.");
}

void loop() {
    BLEDevice central = BLE.central();  // Check if a central device is connected

    // If recording is done and audio data is available
    if (record_ready) {
        // Calculate sound level by summing and averaging the absolute sound strength
        soundLevel = 0;
        for (int i = 0; i < SAMPLES; i++) {
            soundLevel += abs(recording_buf[i]);  // Take the absolute value for each sample
        }
        soundLevel /= SAMPLES;  // Average sound strength

        Serial.print("Sound Level: ");
        Serial.println(soundLevel);

        // If sound level exceeds the threshold, trigger a keypress
        if (soundLevel > threshold) {
            if (central) {
                Serial.println("Loud sound detected! Device connected. Sending 'A'...");
                sendKeyA();
            } else {
                Serial.println("Loud sound detected, but device is NOT connected.");
            }
        }

        // Reset the status after processing the sound
        record_ready = false;
    }

    delay(10);  // Short delay for stability
}

I think the shortest way to solve your problem is to first complete a sketch that can be reliably connected, without the microphone.
In your previous post you used bluefruit.h, but in your upcoming post you use ArduinoBLE.h?
There is no consistency and I have no idea what on earth you are trying to do.

I have some questions to understand exactly what your situation is.

  1. Transmiit device:
    You are transmitting from an XIAO connected to PC(A) via USB, right?
  2. Receive device:
    Received by another XIAO connected to PC(B) via USB? If so, show me your sketch.
  3. Are you receiving via another PC(B)'s built-in BLE function? If so, tell me what application you are using.
  4. Are you receiving with an iPhone? If so, tell me what application you are using.
  5. What sketches, if any, did you use as references to create your sketches you submitted?

Hi!
I have XAIO with battery.
It shall connect to a PC (win10) via BLE.
And when the XAIO hears a loud sound it shall send an ”A” to the PC
I hope you understand.

I am not sure of your situation. Please answer my questions 1. through 5.

  1. Transmiit device:
    You are transmitting from an XIAO connected to PC(A) via USB, right? No, connected via Bluetooth to the PC.
  2. Receive device:
    Received by another XIAO connected to PC(B) via USB? If so, show me your sketch.
  3. Are you receiving via another PC(B)'s built-in BLE function? If so, tell me what application you are using.
    No, The PC is the only reciver.
  4. Are you receiving with an iPhone? If so, tell me what application you are using. No, only PC.
  5. What sketches, if any, did you use as references to create your sketches you submitted?
    I found it on the Internet and asked ChatGPT for help.

Are you saying that the sending device is an XIAO not connected to a PC and the receiving device is a PC?
If so, what application are you using to receive on the PC?

@msfujino
I think the shortest way to solve your problem is to first complete a sketch that can be reliably connected, without the microphone.
In your previous post you used bluefruit.h, but in your upcoming post you use ArduinoBLE.h?
There is no consistency and I have no idea what on earth you are trying to do.

What do you think about this?

I will send a ”A” from my wireless XAIO to the PC it is connected to.
The application on the PC is just a texteditor right now.

I used bluefruit.h first with no Mbed. But It could not use the Mic on the XAIO. It only send if I touch one of pin on the XAIO. (First i thought it worked, but soon I realized it didnt use the mic)

Then i read more about Mbed and no Mbed and found out that the Mic only work in Mbed enabled XAIO. But now the bluefruit.h didnt work. So thats why I have to change to ArduinoBLE.h

And with ArduinoBLE.h I cant connect XAIO to my PC via Bluetooth. . . Does not work. I can see the XAIO in the PC when i scan for BLE, but not connect to it.

So there you have all my problem and why I have to change my code all the time.

Now, the reason I have asked this question so many times is that I needed to know exactly what your equipment configuration is in order to run your sketch and reproduce the trouble situation.

@Simoffen
Hi! An update, with the code below everything works… But now I found out that I dont use the Built in Microphone… it senses when i come close to some of the pin.
I dont use Mbed and I have not found a way of making the SEEEDs mic work without that…Any ideas?

Have you tried this example in post #9 of a microphone that works with non-mbed?
If everything was working except the microphone, what would happen if you combined it with the microphone example above?

This sketch was tested using non-mbed BSP. The following two examples were used as reference.

File → Examples → Adafruit Bluefruit nRF52 Libraries → Peripheral → blehid_keyboard
File → Examples → nRF52840 PDM - Adafruit Fork → PDMSerialPlotter

It may be necessary to first unpair the PC and XIAO, and then perform the pairing operation again.

/*********************************************************************
 This is an example for our nRF52 based Bluefruit LE modules

 Pick one up today in the adafruit shop!

 Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!

 MIT license, check LICENSE for more information
 All text above, and the splash screen below must be included in
 any redistribution
*********************************************************************/
#include <bluefruit.h>
#include <PDM.h>

// buffer to read samples into, each sample is 16-bits
short sampleBuffer[256];

// number of samples read
volatile int samplesRead;

BLEDis bledis;
BLEHidAdafruit blehid;

bool hasKeyPressed = false;

void setup() 
{
  Serial.begin(115200);
  while ( !Serial ) delay(10);   // for nrf52840 with native usb

  // configure the data receive callback
  PDM.onReceive(onPDMdata);

  // optionally set the gain, defaults to 20
  // PDM.setGain(30);

  // initialize PDM with:
  // - one channel (mono mode)
  // - a 16 kHz sample rate
  if (!PDM.begin(1, 16000)) {
    Serial.println("Failed to start PDM!");
    while (1) yield();
  }  

  Serial.println("Bluefruit52 HID Keyboard Example");
  Serial.println("--------------------------------\n");

  Serial.println();
  Serial.println("Go to your phone's Bluetooth settings to pair your device");
  Serial.println("then open an application that accepts keyboard input");

  Serial.println();
  Serial.println("Enter the character(s) to send:");
  Serial.println();  

  Bluefruit.begin();
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values

  // Configure and Start Device Information Service
  bledis.setManufacturer("Adafruit Industries");
  bledis.setModel("Bluefruit Feather 52");
  bledis.begin();

  /* Start BLE HID
   * Note: Apple requires BLE device must have min connection interval >= 20m
   * ( The smaller the connection interval the faster we could send data).
   * However for HID and MIDI device, Apple could accept min connection interval 
   * up to 11.25 ms. Therefore BLEHidAdafruit::begin() will try to set the min and max
   * connection interval to 11.25  ms and 15 ms respectively for best performance.
   */
  blehid.begin();

  // Set callback for set LED from central
  blehid.setKeyboardLedCallback(set_keyboard_led);

  /* Set connection interval (min, max) to your perferred value.
   * Note: It is already set by BLEHidAdafruit::begin() to 11.25ms - 15ms
   * min = 9*1.25=11.25 ms, max = 12*1.25= 15 ms 
   */
  /* Bluefruit.Periph.setConnInterval(9, 12); */

  // Set up and start advertising
  startAdv();
}

void startAdv(void)
{  
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
  
  // Include BLE HID service
  Bluefruit.Advertising.addService(blehid);

  // There is enough room for the dev name in the advertising packet
  Bluefruit.Advertising.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() 
{
  // wait for samples to be read
  if (samplesRead) {
    // print samples to the serial monitor or plotter
    for (int i = 0; i < samplesRead; i++) {
      if(abs(sampleBuffer[i]) > 200) {
        Serial.println('A');
        blehid.keyPress('A');
        hasKeyPressed = true;
        delay(5);
        blehid.keyRelease();        
      }
    }
    // clear the read count
    samplesRead = 0;
  }
}


void onPDMdata() {
  // query the number of bytes available
  int bytesAvailable = PDM.available();

  // read into the sample buffer
  PDM.read(sampleBuffer, bytesAvailable);

  // 16-bit, 2 bytes per sample
  samplesRead = bytesAvailable / 2;
}


/**
 * Callback invoked when received Set LED from central.
 * Must be set previously with setKeyboardLedCallback()
 *
 * The LED bit map is as follows: (also defined by KEYBOARD_LED_* )
 *    Kana (4) | Compose (3) | ScrollLock (2) | CapsLock (1) | Numlock (0)
 */
void set_keyboard_led(uint16_t conn_handle, uint8_t led_bitmap)
{
  (void) conn_handle;
  
  // light up Red Led if any bits is set
  if ( led_bitmap )
  {
    ledOn( LED_RED );
  }
  else
  {
    ledOff( LED_RED );
  }
}

Fantastic, it works :slight_smile: Big tx…