Arduino to Raspberry Pi Communication over BLE

I am trying to send a real-time temperature data (float) from Arduino Nano 33 BLE Sense to Raspberry Pi 4B over BLE connection. I already have succeeded to transmit the data with broadcasting. So, this time I want to establish a connection between central and peripheral and send the data. However, there are few blurry things that I could not understand:

1 - The BLECharacteristic() method has float version BLEFloatCharacteristic so I am using that one as given here. However, the writeValue comes with two versions:

bleCharacteristic.writeValue(buffer, length)
bleCharacteristic.writeValue(value)

So does that mean the value can be a float if my characteristic is a float as well or I should send byte array and try the workaround that is mentioned here?

2 - I have also given read | notify permission to read the data continuously as seen below:

BLEFloatCharacteristic tempCharacteristic(TEMP_UUID, BLERead | BLENotify);
BLEFloatCharacteristic humCharacteristic(HUM_UUID, BLERead | BLENotify);

Yes, you can write the float value. The buffer and length variant can be used for custom type characteristics.

However, may I recommend, not using float data types unless necessary. Here are a few points that may help you make your own choice.

  • The BLE interface (service and characteristic) is public, and you should choose it independent of the implementation. This will allow you to change the sensor and the library which may use a different data type and still use the same BLE app because the interface can stay the same.
  • The Bluetooth SIG has specified an Environmental Sensing Service and characteristics using a fixed-point system by specifying an exponent.
  • If you just use them, you can use an app that knows how to decode the data. e.g., EFR Connect on iOS (maybe Android, I did not test)

Here is an example of this service for the Arduino Nano 33 BLE Sense using the onboard sensors. As you can see, I used float to store the values internally and use the BLE specified type for the BLE characteristics.

Arduino Nano 33 BLE ESS Example (click to view)
/*
  This example creates a BLE peripheral with a Environmental Sensing Service (ESS)
  that contains a temperature, humidity and pressure characteristic.
  The yellow LED shows the BLE module is connected to a central.

  The circuit:
  - Arduino Nano 33 BLE Sense

  You can use a generic BLE central app, like BLE Scanner (iOS and Android) or
  nRF Connect, to interact with the services and characteristics
  created in this sketch.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>
#include <Arduino_HTS221.h>
#include <Arduino_LPS22HB.h>


//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

// https://www.bluetooth.com/specifications/assigned-numbers/environmental-sensing-service-characteristics/
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.temperature.xml
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.humidity.xml
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.pressure.xml

#define BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE    "181A"
#define BLE_UUID_TEMPERATURE                      "2A6E"
#define BLE_UUID_HUMIDITY                         "2A6F"
#define BLE_UUID_PRESSURE                         "2A6D"

//----------------------------------------------------------------------------------------------------------------------
// BLE
//----------------------------------------------------------------------------------------------------------------------

#define BLE_DEVICE_NAME                           "Arduino Nano 33 BLE"
#define BLE_LOCAL_NAME                            "Arduino Nano 33 BLE"

BLEService environmentalSensingService( BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE );
BLEShortCharacteristic temperatureCharacteristic( BLE_UUID_TEMPERATURE, BLERead | BLENotify );
BLEUnsignedShortCharacteristic humidityCharacteristic( BLE_UUID_HUMIDITY, BLERead | BLENotify );
BLEUnsignedLongCharacteristic pressureCharacteristic( BLE_UUID_PRESSURE, BLERead | BLENotify );

//----------------------------------------------------------------------------------------------------------------------
// APP & I/O
//----------------------------------------------------------------------------------------------------------------------

#define SENSOR_UPDATE_INTERVAL                    (5000)

typedef struct __attribute__( ( packed ) )
{
  float temperature;
  float humidity;
  float pressure;
  bool updated = false;
} sensor_data_t;

sensor_data_t sensorData;

#define BLE_LED_PIN                               LED_BUILTIN


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

  Serial.println( "BLE Example - Environmental Sensing Service (ESS)" );

  pinMode( BLE_LED_PIN, OUTPUT );
  digitalWrite( BLE_LED_PIN, LOW );

  // Without Serial when using USB power bank HTS sensor seems to needs some time for setup
  delay( 10 );

  if ( !HTS.begin() )
  {
    Serial.println( "Failed to initialize humidity temperature sensor!" );
    while ( 1 );
  }

  if ( !BARO.begin() )
  {
    Serial.println( "Failed to initialize pressure sensor!" );
    while ( 1 );
  }

  if ( !setupBleMode() )
  {
    Serial.println( "Failed to initialize BLE!" );
    while ( 1 );
  }
  else
  {
    Serial.println( "BLE initialized. Waiting for clients to connect." );
  }
}


void loop()
{
  bleTask();
  if ( sensorTask() )
  {
    printTask();
  }
}


bool sensorTask()
{
  static long previousMillis = 0;

  unsigned long currentMillis = millis();
  if ( currentMillis - previousMillis < SENSOR_UPDATE_INTERVAL )
  {
    return false;
  }
  previousMillis = currentMillis;

  sensorData.temperature = HTS.readTemperature();
  sensorData.humidity = HTS.readHumidity();
  sensorData.pressure = BARO.readPressure() * 1000; // kPa -> Pa
  sensorData.updated = true;

  return sensorData.updated;
}


void printTask()
{
  Serial.print( "Temperature = " );
  Serial.print( sensorData.temperature );
  Serial.println( " °C" );

  Serial.print( "Humidity    = " );
  Serial.print( sensorData.humidity );
  Serial.println( " %" );

  Serial.print( "Pressure = " );
  Serial.print( sensorData.pressure );
  Serial.println( " Pa" );
}


bool setupBleMode()
{
  if ( !BLE.begin() )
  {
    return false;
  }

  // set advertised local name and service UUID
  BLE.setDeviceName( BLE_DEVICE_NAME );
  BLE.setLocalName( BLE_LOCAL_NAME );
  BLE.setAdvertisedService( environmentalSensingService );

  // BLE add characteristics
  environmentalSensingService.addCharacteristic( temperatureCharacteristic );
  environmentalSensingService.addCharacteristic( humidityCharacteristic );
  environmentalSensingService.addCharacteristic( pressureCharacteristic );

  // add service
  BLE.addService( environmentalSensingService );

  // set the initial value for the characeristic
  temperatureCharacteristic.writeValue( 0 );
  humidityCharacteristic.writeValue( 0 );
  pressureCharacteristic.writeValue( 0 );

  // set BLE event handlers
  BLE.setEventHandler( BLEConnected, blePeripheralConnectHandler );
  BLE.setEventHandler( BLEDisconnected, blePeripheralDisconnectHandler );

  // start advertising
  BLE.advertise();

  return true;
}


void bleTask()
{
  const uint32_t BLE_UPDATE_INTERVAL = 10;
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis >= BLE_UPDATE_INTERVAL )
  {
    previousMillis = currentMillis;
    BLE.poll();
  }

  if ( sensorData.updated )
  {
    // BLE defines Temperature UUID 2A6E Type sint16 ( see XML links )
    // Unit is in degrees Celsius with a resolution of 0.01 degrees Celsius
    int16_t temperature = round( sensorData.temperature * 100.0 );
    temperatureCharacteristic.writeValue( temperature );

    // BLE defines Humidity UUID 2A6F Type uint16
    // Unit is in percent with a resolution of 0.01 percent
    uint16_t humidity = round( sensorData.humidity * 100.0 );
    humidityCharacteristic.writeValue( humidity );

    // BLE defines Pressure UUID 2A6D Type uint32
    // Unit is in Pascal with a resolution of 0.1 Pa
    uint32_t pressure = round( sensorData.pressure * 10.0 );
    pressureCharacteristic.writeValue( pressure );

    sensorData.updated = false;
  }
}


void blePeripheralConnectHandler( BLEDevice central )
{
  digitalWrite( BLE_LED_PIN, HIGH );
  Serial.print( F ( "Connected to central: " ) );
  Serial.println( central.address() );
}


void blePeripheralDisconnectHandler( BLEDevice central )
{
  digitalWrite( BLE_LED_PIN, LOW );
  Serial.print( F( "Disconnected from central: " ) );
  Serial.println( central.address() );
}

Here are a few more general information and arguments.

  • Float is a data type that has been implemented in multiple formats. e.g., IEEE754 (used by ARM processors) and IEEE 11073 used for medical devices.
  • FP data types are hard to read and to convert if it is not the native type supported by the platform
  • Both data types have been specified for the Characteristics Presentation Format by the Bluetooth SIG. So, if you must, you can use them and even inform the central.

Hi @Klaus_K,

Thank you for these great advices and an example. Currently my Arduino is busy with generating a dataset, as soon as it finishes, I will adjust my code according to your example. I was unaware of Environmental Sensing Service.

I have found the document that defines UUID characteristics. However I could not find the mention of Type. So can you share the XML links that you mention in your code here:

// BLE defines Temperature UUID 2A6E Type sint16 ( see XML links )

In my case, I am not using any specific app. I am reading the data in Node-RED hosted in Raspberry Pi, that allows you to create custom nodes based on JavaScript to develop applications.

I am using the @abandonware/noble as in my case Pi is the central. So data will be read by pure JavaScript script.

So here what you store is float, what you send is either short or long if I understand right. For example the data you store is float (24.541) what you send is short (2454). I can do the same and convert the data on Node-RED. I will update as soon as I can.

I am not able to see any float support mentioned in the noble.js as well.

The links are at the top of the document in the BLE UUIDs section. They contain the UUID and format description.

:+1:

How did you do that? I can use BLE on the Pi with some CMD line tool but the BLE support in Node RED seems to have access right issues or something like that. I have Raspberry Pi 3 and 4 with Raspberry Pi OS.

The links are at the top of the document in the BLE UUIDs section. They contain the UUID and format description.

You meant the code, I have seen now thanks.

How did you do that?

I don't remember having any access issue. You might want to reinstall those.

bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

I would recommend running a basic BLE Scanner first on Pi, to see if you can discover peripherals. Here is the example script I have written:

const noble = require('@abandonware/noble');
const PERIPHERAL = (peripheral) => {
    const ADVERTISEMENT = peripheral.advertisement;
    console.log(ADVERTISEMENT);
};
noble.on('discover', PERIPHERAL);

noble.startScanning([], true);
setTimeout( async () => {
    await noble.stopScanningAsync();
    process.exit(0);
}, 5000);

Just put this in something.js and run with node something.js .

Then in Node-RED, you should get the same result by using function node.

I posted a question to stackoverflow as well. There, you can see I am reading some non-sense in Node-RED due to sending float and not decoding it.

If you still have an error please share the exact error, so I might help further.

Thank you, I will try this. I already found a note on one of the pages that some privileges need to be set for this to work. I will see whether I can follow the instruction successfully.

@Klaus_K why these (temperature, humidity, pressure) characteristics are not subscribable?

bleCharacteristic.canSubscribe() returns 0 for all these 3 characteristics. Hence, I am not able to subscribe to them from a Raspberry Pi via Node.js.

Did you confirm with a generic BLE app on your smartphone? If that works, the issue is on the Raspberry Pi.

Yes. I can subscribe via BLE app but can't via Noble.js. How can I debug this further?

Edit: I checked the output of sudo btmon on Pi. This is what I get Error: Attribute Not Found (0x0a).

It seems like I need to add descriptors. I will update after figuring out how handles and descriptors are handled.

That points toward an issue on the Pi. Your Arduino sketch is working with the app.

How did you come to that conclusion? When you set BLENotify for the characteristic, the descriptors are added by the ArduinoBLE library for you. This is how the BLE app on your phone knows it can describe to the charateristic.

There are functions to add descriptors in the ArduinoBLE library. But that is only for optional descriptors. Unfortunately, they are mostly ignored by BLE apps.

How did you come to that conclusion?

@Klaus_K I have tried to debug further. The sudo btmon log of the Pi as follows:

> HCI Event: LE Meta Event (0x3e) plen 18                                                                                      #101 [hci0] 216.091528
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Scan response - SCAN_RSP (0x04)
        Address type: Public (0x00)
        Address: 09:16:8C:47:5F:71 (OUI 09-16-8C)
        Data length: 6
        Name (complete): Nano
        RSSI: -73 dBm (0xb7)
    < HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2                            #166 [hci0] 20.855945
        Scanning: Disabled (0x00)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                                       #167 [hci0] 20.856820
      LE Set Scan Enable (0x08|0x000c) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Create Connection (0x08|0x000d) plen 25                         #168 [hci0] 20.861549
        Scan interval: 60.000 msec (0x0060)
        Scan window: 30.000 msec (0x0030)
        Filter policy: White list is not used (0x00)
        Peer address type: Public (0x00)
        Peer address: 09:16:8C:47:5F:71 (OUI 09-16-8C)
        Own address type: Public (0x00)
        Min connection interval: 7.50 msec (0x0006)
        Max connection interval: 15.00 msec (0x000c)
        Connection latency: 0 (0x0000)
        Supervision timeout: 2000 msec (0x00c8)
        Min connection length: 2.500 msec (0x0004)
        Max connection length: 3.750 msec (0x0006)
> HCI Event: Command Status (0x0f) plen 4                                         #169 [hci0] 20.862080
      LE Create Connection (0x08|0x000d) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 19                                         #170 [hci0] 21.168187
      LE Connection Complete (0x01)
        Status: Success (0x00)
        Handle: 64
        Role: Master (0x00)
        Peer address type: Public (0x00)
        Peer address: 09:16:8C:47:5F:71 (OUI 09-16-8C)
        Connection interval: 15.00 msec (0x000c)
        Connection latency: 0 (0x0000)
        Supervision timeout: 2000 msec (0x00c8)
        Master clock accuracy: 0x00
@ MGMT Event: Device Connected (0x000b) plen 13                               {0x0002} [hci0] 21.168288
        LE Address: 09:16:8C:47:5F:71 (OUI 09-16-8C)
        Flags: 0x00000000
        Data length: 0
@ MGMT Event: Device Connected (0x000b) plen 13                               {0x0001} [hci0] 21.168288
        LE Address: 09:16:8C:47:5F:71 (OUI 09-16-8C)
        Flags: 0x00000000
        Data length: 0
< HCI Command: LE Read Remote Used Features (0x08|0x0016) plen 2                  #171 [hci0] 21.168529
        Handle: 64
> HCI Event: Command Status (0x0f) plen 4                                         #172 [hci0] 21.169549
      LE Read Remote Used Features (0x08|0x0016) ncmd 1
        Status: Success (0x00)
> HCI Event: Command Complete (0x0e) plen 14                                      #173 [hci0] 21.169670
      LE Read Remote Used Features (0x08|0x0016) ncmd 1
        Status: Success (0x00)
        00 00 00 00 00 00 00 00 00 00                    ..........      
> HCI Event: LE Meta Event (0x3e) plen 12                                         #174 [hci0] 21.187752
      LE Read Remote Used Features (0x04)
        Status: Success (0x00)
        Handle: 64
        Features: 0x2f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
          LE Encryption
          Connection Parameter Request Procedure
          Extended Reject Indication
          Slave-initiated Features Exchange
          LE Data Packet Length Extension
< ACL Data TX: Handle 64 flags 0x00 dlen 7                                        #175 [hci0] 21.196651
      ATT: Exchange MTU Request (0x02) len 2
        Client RX MTU: 256
> ACL Data RX: Handle 64 flags 0x02 dlen 7                                        #176 [hci0] 21.232587
      ATT: Exchange MTU Response (0x03) len 2
        Server RX MTU: 247
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #177 [hci0] 21.236291
      ATT: Read By Group Type Request (0x10) len 6
        Handle range: 0x0001-0xffff
        Attribute group type: Primary Service (0x2800)
> HCI Event: Number of Completed Packets (0x13) plen 5                            #178 [hci0] 21.262750
        Num handles: 1
        Handle: 64
        Count: 2
> ACL Data RX: Handle 64 flags 0x02 dlen 24                                       #179 [hci0] 21.278151
      ATT: Read By Group Type Response (0x11) len 19
        Attribute data length: 6
        Attribute group list: 3 entries
        Handle range: 0x0001-0x0005
        UUID: Generic Access Profile (0x1800)
        Handle range: 0x0006-0x0009
        UUID: Generic Attribute Profile (0x1801)
        Handle range: 0x000a-0x0013
        UUID: Environmental Sensing (0x181a)
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #180 [hci0] 21.280267
      ATT: Read By Group Type Request (0x10) len 6
        Handle range: 0x0014-0xffff
        Attribute group type: Primary Service (0x2800)
> ACL Data RX: Handle 64 flags 0x02 dlen 9                                        #181 [hci0] 21.307537
      ATT: Error Response (0x01) len 4
        Read By Group Type Request (0x10)
        Handle: 0x0014
        Error: Attribute Not Found (0x0a)
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #182 [hci0] 21.310329
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x0001-0x0005
        Attribute type: Characteristic (0x2803)
> HCI Event: Number of Completed Packets (0x13) plen 5                            #183 [hci0] 21.322723
        Num handles: 1
        Handle: 64
        Count: 2
> ACL Data RX: Handle 64 flags 0x02 dlen 20                                       #184 [hci0] 21.337681
      ATT: Read By Type Response (0x09) len 15
        Attribute data length: 7
        Attribute data list: 2 entries
        Handle: 0x0002
        Value: 020300002a
        Handle: 0x0004
        Value: 020500012a
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #185 [hci0] 21.340017
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x0006-0x0009
        Attribute type: Characteristic (0x2803)
> ACL Data RX: Handle 64 flags 0x02 dlen 13                                       #186 [hci0] 21.367572
      ATT: Read By Type Response (0x09) len 8
        Attribute data length: 7
        Attribute data list: 1 entry
        Handle: 0x0007
        Value: 200800052a
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #187 [hci0] 21.368173
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x000a-0x0013
        Attribute type: Characteristic (0x2803)
> HCI Event: Number of Completed Packets (0x13) plen 5                            #188 [hci0] 21.382744
        Num handles: 1
        Handle: 64
        Count: 2
> ACL Data RX: Handle 64 flags 0x02 dlen 27                                       #189 [hci0] 21.398165
      ATT: Read By Type Response (0x09) len 22
        Attribute data length: 7
        Attribute data list: 3 entries
        Handle: 0x000b
        Value: 120c006e2a
        Handle: 0x000e
        Value: 120f006f2a
        Handle: 0x0011
        Value: 1212006d2a
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #190 [hci0] 21.398694
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x0009-0x0009
        Attribute type: Characteristic (0x2803)
> ACL Data RX: Handle 64 flags 0x02 dlen 9                                        #191 [hci0] 21.427558
      ATT: Error Response (0x01) len 4
        Read By Type Request (0x08)
        Handle: 0x0009
        Error: Attribute Not Found (0x0a)
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #192 [hci0] 21.428283
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x0013-0x0013
        Attribute type: Characteristic (0x2803)
> HCI Event: Number of Completed Packets (0x13) plen 5                            #193 [hci0] 21.442725
        Num handles: 1
        Handle: 64
        Count: 2
> ACL Data RX: Handle 64 flags 0x02 dlen 9                                        #194 [hci0] 21.472559
      ATT: Error Response (0x01) len 4
        Read By Type Request (0x08)
        Handle: 0x0013
        Error: Attribute Not Found (0x0a)
< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #195 [hci0] 21.474417
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x000b-0x000d
        Attribute type: Client Characteristic Configuration (0x2902)
> ACL Data RX: Handle 64 flags 0x02 dlen 9                                        #196 [hci0] 21.502541
      ATT: Error Response (0x01) len 4
        Read By Type Request (0x08)
        Handle: 0x000b
        Error: Attribute Not Found (0x0a)
> HCI Event: Number of Completed Packets (0x13) plen 5                            #197 [hci0] 21.661765
        Num handles: 1
        Handle: 64
        Count: 1

If we have a look at the parts with errors first error comes when Pi tries to discover primary service. I am getting this error several times. I have no idea why Pi starts handle range from 0x0014:

< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #180 [hci0] 21.280267
      ATT: Read By Group Type Request (0x10) len 6
        Handle range: 0x0014-0xffff
        Attribute group type: Primary Service (0x2800)
> ACL Data RX: Handle 64 flags 0x02 dlen 9                                        #181 [hci0] 21.307537
      ATT: Error Response (0x01) len 4
        Read By Group Type Request (0x10)
        Handle: 0x0014
        Error: Attribute Not Found (0x0a)

I interpreted this as there is no attribute with handle 0x0014 so it seems fine ( I am not 100% sure) as previously Pi can discover services as seen below when it scans for the all handle range:

< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #177 [hci0] 21.236291
      ATT: Read By Group Type Request (0x10) len 6
        Handle range: 0x0001-0xffff
        Attribute group type: Primary Service (0x2800)
> HCI Event: Number of Completed Packets (0x13) plen 5                            #178 [hci0] 21.262750
        Num handles: 1
        Handle: 64
        Count: 2
> ACL Data RX: Handle 64 flags 0x02 dlen 24                                       #179 [hci0] 21.278151
      ATT: Read By Group Type Response (0x11) len 19
        Attribute data length: 6
        Attribute group list: 3 entries
        Handle range: 0x0001-0x0005
        UUID: Generic Access Profile (0x1800)
        Handle range: 0x0006-0x0009
        UUID: Generic Attribute Profile (0x1801)
        Handle range: 0x000a-0x0013
        UUID: Environmental Sensing (0x181a)

Then it comes the error that prevents Pi from subscribing (I guess).

< ACL Data TX: Handle 64 flags 0x00 dlen 11                                       #195 [hci0] 21.474417
      ATT: Read By Type Request (0x08) len 6
        Handle range: 0x000b-0x000d
        Attribute type: Client Characteristic Configuration (0x2902)
> ACL Data RX: Handle 64 flags 0x02 dlen 9                                        #196 [hci0] 21.502541
      ATT: Error Response (0x01) len 4
        Read By Type Request (0x08)
        Handle: 0x000b
        Error: Attribute Not Found (0x0a)

I am currently reading this guide to fully understand what is going on. So it seems like Pi is asking for a descriptor but there is none in the server (Arduino).

Thank you for the tip with btmon.

In return I would recommend you investigate gatttool and use them together.

The following line should give you all BLE device nearby, make note of the MAC address

sudo hcitool lescan

Start gatttool (replace x with MAC address

sudo gatttool -b xx:xx:xx:xx:xx:xx -I

Connect to your device

[xx:xx:xx:xx:xx:xx][LE]> connect

Get all services

[xx:xx:xx:xx:xx:xx][LE]> primary

Get all characteristics

[xx:xx:xx:xx:xx:xx][LE]> characteristics

Get all descriptors

[xx:xx:xx:xx:xx:xx][LE]> char-desc

The descriptors with 0x2902 in it are the ones for notifications. Note the handle for one of them. e.g., handle: 0x0009, uuid: 00002902-0000-1000-8000-00805f9b34fb

Side note: Compare the entries in the last table with the first two.

  • the descriptors with 2800 describe the services
  • the descriptors with 2803 describe the characteristics
  • the descriptors with 2902 describe the notifications

Read the descriptor using the handle

[xx:xx:xx:xx:xx:xx][LE]> char-read-hnd 0x0009

Write the descriptor using the handle (Enable the notification for that characteristic)

[xx:xx:xx:xx:xx:xx][LE]> char-write-req 0x0009 01 01

Read the descriptor again. The value should have changed for the first byte from 00 to 01.
Characteristic value/descriptor: 01 00

In parallel you can watch how this look like in btmon.