Sending IMU data over ArduinoBLE Issue

I am having an issue with sending data via bluetooth to a smartphone from the onboard IMU on the Nano 33 BLE with the ArduinoBLE library.

Ideally, the IMU data is being written in the values for the defined characteristics and then displayed in app like nrf Connect in the same format/value. However, the value displayed in two ble apps (FlutterBlue and nrf Connet) I have tried the value displayed for each IMU characteristic was in a strange format. Images of both apps while connected to the board is attached.

This was initially done with setting the data as double characteristics with BLEDoubleCharacteristic. However, I have tried other methods such as BLEUnsignedIntCharacterisitic and BLEFloatCharacteristics with the corresponding data types and nothing seems to work. Any help would be appreciated.

#include <ArduinoBLE.h>
#include <Arduino_LSM9DS1.h>

float x, y, z;
double accelX=0;
double accelY=1;
double accelZ=0;
double gyroX=0;
double gyroY=0;
double gyroZ=0;

BLEService IMUService("1101");
BLEDoubleCharacteristic AccXChar("2101", BLERead| BLENotify);
BLEDoubleCharacteristic AccYChar("2102",  BLERead | BLENotify);
BLEDoubleCharacteristic AccZChar("2103",  BLERead | BLENotify);
BLEDoubleCharacteristic GyroXChar("2104", BLERead | BLENotify);
BLEDoubleCharacteristic GyroYChar("2105", BLERead | BLENotify);
BLEDoubleCharacteristic GyroZChar("2106", BLERead | BLENotify);

void setup() {
IMU.begin();
Serial.begin(9600); 

pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected

if (!BLE.begin()) {
Serial.println("BLE failed to Initiate");
delay(500);
while (1);
}


  /* Set a local name for the BLE device
     This name will appear in advertising packets
     and can be used by remote devices to identify this BLE device
     The name can be changed but maybe be truncated based on space left in advertisement packet
  */
  
BLE.setLocalName("Arduino IMU");
BLE.setAdvertisedService(IMUService);
IMUService.addCharacteristic(AccXChar);
IMUService.addCharacteristic(AccYChar);
IMUService.addCharacteristic(AccZChar);
IMUService.addCharacteristic(GyroXChar);
IMUService.addCharacteristic(GyroYChar);
IMUService.addCharacteristic(GyroZChar);
BLE.addService(IMUService);



AccXChar.writeValue(accelX);
AccYChar.writeValue(accelY);
AccZChar.writeValue(accelZ);
GyroXChar.writeValue(gyroX);
GyroYChar.writeValue(gyroY);
GyroZChar.writeValue(gyroZ);

  /* Start advertising BLE.  It will start continuously transmitting BLE
     advertising packets and will be visible to remote BLE central devices
     until it receives a new connection */

BLE.advertise();

Serial.println("Bluetooth device is now active, waiting for connections...");
}


void loop() {

BLEDevice central = BLE.central();
if (central) {
Serial.print("Connected to central: ");
Serial.println(central.address());
digitalWrite(LED_BUILTIN, HIGH);
while (central.connected()) {


read_Accel(); //runs the read_Accel function defined below
read_Gyro(); //runs the read_Gyro function defined below

AccXChar.writeValue(accelX);
AccYChar.writeValue(accelY);
AccZChar.writeValue(accelZ);
GyroXChar.writeValue(gyroX);
GyroYChar.writeValue(gyroY);
GyroZChar.writeValue(gyroZ);

Serial.print("At Main Function");
Serial.println("");

Serial.print("Accel:");
Serial.print(accelX);
Serial.print(",");
Serial.print(accelY);
Serial.print(",");
Serial.print(accelZ);
Serial.println("");

Serial.print("Gyro:");
Serial.print(gyroX);
Serial.print(",");
Serial.print(gyroY);
Serial.print(",");
Serial.print(gyroZ);
Serial.println("");
Serial.println("");
}
}
digitalWrite(LED_BUILTIN, LOW);
Serial.print("Disconnected from central: ");
Serial.println(central.address());
}

void read_Accel() {

if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);

accelX = x;
accelY = y;
accelZ = z;
}
}
void read_Gyro() {

if (IMU.accelerationAvailable()) {
IMU.readGyroscope(x, y, z);
gyroX = x;
gyroY = y;
gyroZ = z;
}
}

nrfConnect.PNG

First you should not use short UUIDs for your own services and characteristics. These are reserved by the BLE org. If you create your own characteristics use random 128 bit numbers. Only this will ensure you get a universally unique identifier.

Second most apps just show you raw data from the GATT unless they recognize the UUID. Some apps can decode the data according the BLE specification. But this is only true for some very generic services.

When you use the 16 bit UUID but the data is not in the format specified by BLE org the app cannot show you the right values. For instance the 1101 you used tells the app there is a Serial Port Profile (SPP).

Have a look at the following post. I created an example for an Environmental Sensing Service (ESS) with temperature and humidity, which can be decoded by the ERF Connect app, because I looked up the UUIDs and the data format.

You are right to play with the data types as you need to use a signed data type. Most utility apps like nRF connect tends to treat everything as uint8_t (bytes) unless you tell it what the data type is via the 0x2904 descriptor (Characteristic Presentation Format).

There are ways around this. BLE Health Thermometres, for example, use ISO/IEEE 11073 as a conversion technique. I have an implementation example here (this is based off an Adafruit library):

Anyway, I suspect your problem relates to not setting the data array size correctly.

In the above example I used this command to set the size of my byte array:

htsMeasureCharId = gatt.addCharacteristic(0x2A1C, GATT_CHARS_PROPERTIES_INDICATE, 5, 13, BLE_DATATYPE_BYTEARRAY);

This then allows the byte array to be transmitted across via BLE.

Best thing is check the BLE library library source code for the "addCharacteristic" function as this will tell you how to define your parameters.

This is a much better way of transmitting your data as you're most likely will run into problems, such as running out of memory (as you need to assigned 128-bit UUID's for each), with those characteristics you've assigned for each axis etc.

Hope this helps.