Seeed studio xiao nrf52840 pdm mic over ble

Hello,

I was wondering if someone can point me out to a sample project that makes use of sending the values from the PDM over BLE to an app like NRF connect?

Hi there, and Welcome,
Any of the IUM over BLE demo’s will serv as a good template with Accelerometer and gyro data as the same as data from PDM MIC.–> Exhibition for XIAO Series | Seeed Studio Wiki
It’s pretty basic BLE and IMU connection and data exchange. Search for that above you 'll find what you need AOK.
HTH
GL :slight_smile: PJ :v:

I was able to incorporate both examples from the arduino BLE and the PDM. It compiles and creates a ble service but I am not able to connect to it via nrf connect. I had a feeling that the PDM example is causing timing issues so that my phone wont be able to connect to the device so I was hoping there was someone out there who already had something similar. From what I can guess, it has something to do with the sampling period in the serial plotter for the PDM. I will try to post a code as soon as I get back in the case you or others can help me. Thank you!

Hi there,
Yes , do that. Well that’s the first step is being able to connect.
What Kind of Phone? Early eye phones don’t support BLE 5.0
BTW, If you scan in Nrfconnect do you see the device advertising?
Start there. It’s not rocket science, you’ll see but there is a process. Post the code and we can get you going I’m certain.
HTH
GL :slight_smile: PJ :v:

yes i wached a video on thi reciently… but i dont remember where i saw it

Try this

This brings up the question can XIAO ESP32S3mic be used to send IIS overbluetooth to a blootooth headphone for example… i would really like to try that

Got a bit side tracked with solving the issue. But i had the time today to sit down and try one last thing before giving up and asking for someone to directly help. I changed the sample rate and sample size and it seems to allow BLE connection. For the questions from PJ_Glasso, before i did the changes, I was able to see the device via nrf connect. I am using a Samsung A32 5g.

The code below is what i got to make it work. Its not clean and just a bunch of things copy pasted onto the same file.

#include <ArduinoBLE.h>
#include <ArduinoBLE.h>
#include <SPI.h>
#include <LSM6DS3.h>
#include <Wire.h>

#define Accel_Number 3

union Accel_sensor_data {
  struct __attribute__((packed)) {
    float values[Accel_Number];
  };
  uint8_t bytes[Accel_Number * sizeof(float)];
};

union Accel_sensor_data AccelSensorData;

#define Gyro_Number 3

union Gyro_sensor_data {
  struct __attribute__((packed)) {
    float values[Gyro_Number];
  };
  uint8_t bytes[Gyro_Number * sizeof(float)];
};

union Gyro_sensor_data GyroSensorData;

#define Mic_Number 1

union Mic_sensor_data {
  struct __attribute__((packed)) {
    float values[Mic_Number];
  };
  uint8_t bytes[Mic_Number * sizeof(float)];
};

union Mic_sensor_data MicSensorData;


#define BLE_UUID_SENSOR_DATA_SERVICE "2BEEF31A-B10D-271C-C9EA-35D865C1F48A"
#define BLE_UUID_Accel_SENSOR_DATA "4664E7A1-5A13-BFFF-4636-7D0A4B16496C"
#define BLE_UUID_Gyro_SENSOR_DATA "63bf7ca3-374e-4c9d-8e88-cfb66756d96f"
#define BLE_UUID_Mic_SENSOR_DATA "d257a622-e5ed-4fed-b7a6-53366bfb52f9"

//Service for publish value...
BLEService batteryService("180F");
BLEUnsignedCharCharacteristic batteryLevelChar("2A19", BLERead | BLENotify);

BLEService sensorDataService(BLE_UUID_SENSOR_DATA_SERVICE);
BLECharacteristic AccelDataCharacteristic(BLE_UUID_Accel_SENSOR_DATA, BLERead | BLENotify, sizeof AccelSensorData.bytes);
BLECharacteristic GyroDataCharacteristic(BLE_UUID_Gyro_SENSOR_DATA, BLERead | BLENotify, sizeof GyroSensorData.bytes);
BLECharacteristic MicDataCharacteristic(BLE_UUID_Mic_SENSOR_DATA, BLERead | BLENotify, sizeof MicSensorData.bytes);

//Create a instance of class LSM6DS3
LSM6DS3 myIMU(I2C_MODE, 0x6A);  //I2C device address 0x6A
float aX, aY, aZ, gX, gY, gZ;
const float accelerationThreshold = 2.5;  // threshold of significant in G's
const int numSamples = 119;
int samplesRead = numSamples;
int largestValue = 0;

int oldBatteryLevel = 0;  // last battery level reading from analog input
long previousMillis = 0;  // last time the battery level was checked, in ms

//pdm stuff
#include <mic.h>
#if defined(WIO_TERMINAL)
#include "processing/filters.h"
#endif

// Settings
#if defined(WIO_TERMINAL)
#define DEBUG 1  // Enable pin pulse during ISR
#define SAMPLES 1 * 3
#elif defined(ARDUINO_ARCH_NRF52840)
#define DEBUG 1  // Enable pin pulse during ISR
#define SAMPLES 1
#endif

mic_config_t mic_config {
  .channel_cnt = 1,
  .sampling_rate = 16000,
  .buf_size = 1600,
#if defined(WIO_TERMINAL)
  .debug_pin = 1  // Toggles each DAC ISR (if DEBUG is set to 1)
#elif defined(ARDUINO_ARCH_NRF52840)
  .debug_pin = LED_BUILTIN  // Toggles each DAC ISR (if DEBUG is set to 1)
#endif
};

#if defined(WIO_TERMINAL)
DMA_ADC_Class Mic(&mic_config);
#elif defined(ARDUINO_ARCH_NRF52840)
NRF52840_ADC_Class Mic(&mic_config);
#endif

int16_t recording_buf[SAMPLES];
volatile uint8_t recording = 0;
volatile static bool record_ready = false;

#if defined(WIO_TERMINAL)
FilterBuHp filter;
#endif

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

  pinMode(LED_BUILTIN, OUTPUT);

  if (myIMU.begin() != 0) {
  } else {
  }

  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");

    while (1)
      ;
  }

  BLE.setDeviceName("Sensors");
  BLE.setLocalName("Sensors");

  BLE.setAdvertisedService(batteryService);            // add the service UUID
  batteryService.addCharacteristic(batteryLevelChar);  // add the battery level characteristic
  BLE.addService(batteryService);                      // Add the battery service
  batteryLevelChar.writeValue(oldBatteryLevel);        // set initial value for this characteristic

  BLE.setAdvertisedService(sensorDataService);
  sensorDataService.addCharacteristic(AccelDataCharacteristic);
  sensorDataService.addCharacteristic(GyroDataCharacteristic);
  sensorDataService.addCharacteristic(MicDataCharacteristic);
  BLE.addService(sensorDataService);
  AccelDataCharacteristic.writeValue(AccelSensorData.bytes, sizeof AccelSensorData.bytes);
  GyroDataCharacteristic.writeValue(GyroSensorData.bytes, sizeof GyroSensorData.bytes);
  MicDataCharacteristic.writeValue(MicSensorData.bytes, sizeof MicSensorData.bytes);

  BLE.advertise();

  Serial.println("Bluetooth® device active, waiting for connections...");

  //pdm stuff
#if defined(WIO_TERMINAL)
  pinMode(WIO_KEY_A, INPUT_PULLUP);
#endif

  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();

  if (central) {
    Serial.print("Connected to central: ");
    Serial.println(central.address());
    digitalWrite(LED_BUILTIN, HIGH);

    // check the battery level every 200ms
    while (central.connected()) {
      long currentMillis = millis();
      // if 200ms have passed, check the battery level:
      if (currentMillis - previousMillis >= 200) {
        previousMillis = currentMillis;
        updateBatteryLevel();
      }

      AccelSensorData.values[0] = myIMU.readFloatAccelX();
      AccelSensorData.values[1] = myIMU.readFloatAccelY();
      AccelSensorData.values[2] = myIMU.readFloatAccelZ();
      GyroSensorData.values[0] = myIMU.readFloatGyroX();
      GyroSensorData.values[1] = myIMU.readFloatGyroX();
      GyroSensorData.values[2] = myIMU.readFloatGyroX();
      //IEEE 754 Converter
      AccelDataCharacteristic.writeValue(AccelSensorData.bytes, sizeof AccelSensorData.bytes);
      GyroDataCharacteristic.writeValue(GyroSensorData.bytes, sizeof GyroSensorData.bytes);

//pdm stuff
#if defined(WIO_TERMINAL)
      if (digitalRead(WIO_KEY_A) == LOW && !recording) {

        Serial.println("Starting sampling");
        recording = 1;
        record_ready = false;
      }
#endif

#if defined(WIO_TERMINAL)
      if (!recording && record_ready)
#elif defined(ARDUINO_ARCH_NRF52840)
      if (record_ready)
#endif
      {
        for (int i = 0; i < SAMPLES; i++) {
          //int16_t sample = filter.step(recording_buf[i]);
          int16_t sample = recording_buf[i];
          Serial.println(sample);
          MicSensorData.values[0] = sample;
        }

        record_ready = false;
      }
      MicDataCharacteristic.writeValue(MicSensorData.bytes, sizeof MicSensorData.bytes);
    }
  }

  // when the central disconnects, turn off the LED:
  digitalWrite(LED_BUILTIN, LOW);
  Serial.print("Disconnected from central: ");
  Serial.println(central.address());
}

static void audio_rec_callback(uint16_t *buf, uint32_t buf_len) {

  static uint32_t idx = 0;
  // Copy samples from DMA buffer to inference buffer
#if defined(WIO_TERMINAL)
  if (recording)
#endif
  {
    for (uint32_t i = 0; i < buf_len; i++) {

      // Convert 12-bit unsigned ADC value to 16-bit PCM (signed) audio value
#if defined(WIO_TERMINAL)
      recording_buf[idx++] = filter.step((int16_t)(buf[i] - 1024) * 16);
      //recording_buf[idx++] = (int16_t)(buf[i] - 1024) * 16;
#elif defined(ARDUINO_ARCH_NRF52840)
      recording_buf[idx++] = buf[i];
#endif

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

void updateBatteryLevel() {
  /* Read the current voltage level on the A0 analog input pin.
     This is used here to simulate the charge level of a battery.
  */
  int battery = analogRead(A0);
  int batteryLevel = map(battery, 0, 1023, 0, 100);

  if (batteryLevel != oldBatteryLevel) {        // if the battery level has changed
    batteryLevelChar.writeValue(batteryLevel);  // and update the battery level characteristic
    oldBatteryLevel = batteryLevel;             // save the level for next comparison
  }
}

Hi there,
Well Talk about the “Kitchen Sink” you got it all in there though LOL, Phone is very Good, ok So What BSP are you using.? Mbed or NON?
why 2x #include <ArduinoBLE.h> ? first off.
What errors are you getting , Im sure allot?
Post what you had working and the Serial output.
Have you recorded a sample wav ?
HTH
GL :slight_smile: PJ