Subscribing to Bluefruit LE Friend with ArduinoBLE not working

I've been using an Arduino Nano 33 BLE to communicate with my PC over the BlueFruit LE Friend's UART characteristic. Using the default ArduinoBLE library, I'm able to connect to the BlueFruit from the Nano 33 as a central device, and write to the BlueFruit and have it show up in a serial terminal. I can get the Nano 33 to read from the Bluefruit as well, by sending a Characteristic.read() request and a subsequent Charcterisitc.readValue() command. Subscribing to the rx characterisitic on the Nano 33 does NOT work, however, even though the Bluefruit's info page says that the rx characterisitc (address 0x0003) is capable of Notify. I need to be able to subscribe, so that I can use the characteristic.valueUpdated() function to know when new data is available. Here is the relevant code:

if (peripheral.connect()) {
    Serial.println("Connected");
  } else {
    Serial.println("Failed to connect!");
    return;
  }

  // discover peripheral attributes
  Serial.println("Discovering attributes ...");
  displayNum('A');
  if (peripheral.discoverAttributes()) {
    Serial.println("Attributes discovered");
    displayNum(-1);
  } else {
    Serial.println("Attribute discovery failed!");
    peripheral.disconnect();
    displayError('A');
    return;
  }

  // retrieve the TX characteristic
  BLECharacteristic txCharacteristic = peripheral.characteristic("6e400002-b5a3-f393-e0a9-e50e24dcca9e");
  BLECharacteristic rxCharacteristic = peripheral.characteristic("6e400003-b5a3-f393-e0a9-e50e24dcca9e");

  // make sure BlueFruit UART characteristics have been loaded properly are of
  // proper format
  if (!txCharacteristic) {
    Serial.println("Peripheral does not have TX characteristic!");
    peripheral.disconnect();
    return;
  } else if (!txCharacteristic.canWrite()) {
    Serial.println("Peripheral does not have a writable TX characteristic!");
    peripheral.disconnect();
    return;
  }
  if (!rxCharacteristic) {
    Serial.println("Peripheral does not have RX characteristic!");
    peripheral.disconnect();
    return;
  } else if (!rxCharacteristic.canSubscribe()) {
    Serial.println("rxCharacteristic is not subscribable!");
    peripheral.disconnect();
    return;
  } else if (!rxCharacteristic.subscribe()) {
    Serial.println("rxCharacteristic subscription failed!");
    peripheral.disconnect();
    return;
  }

What's strange is that Characteristic.canSubscribe() method returns true, and it is only when I actually try to subscribe that my error message is triggered. This code is also basically taken directly from the ArduinoBLE SensorTag example, so I'm not sure if this is an issue with the Arduino library or the Bluefruit, so any input would be appreciated.

As an update, I'm able to get notifications through the BlueFruit Connect app, which makes me think that the issue has something more to do with the Arduino library.

When you say the "canSubscribe" is not working. What do you mean exactly.

The library code seems fine:

bool BLECharacteristic::canSubscribe()
{
  if (_remote) {
    return (properties() & (BLENotify | BLEIndicate)) != 0;
  }

  return false;
}

So, is your code working as intended. Do you get a "rxCharacteristic is not subscribable!" message or do you get a "rxCharacteristic subscription failed!" message or neither?

UPDATE

Sorry, I just noticed you answered my question...What's strange is that Characteristic.canSubscribe() method returns true

That is not strange. That is how your logic is working... suggest removing last "else if" and just use an if statement and see what happens.

if (!rxCharacteristic.subscribe()) {
    Serial.println("rxCharacteristic subscription failed!");
    peripheral.disconnect();
    return;
}

Thanks for the reply - changing to a plain if statement results in the same error, "rxCharacteristic subscription failed!" is printed out.

I also just tried the SensorTagButton example, just exchanging the service and characterisitc UUIDs for BlueFruit ones, to make sure there wasn't some other error in my logic - I am still unable to subscribe while canSubscribe() returns true.

Then maybe change this to an "if" just to be sure it is checking this condition.

 else if (!rxCharacteristic.canSubscribe()) {
    Serial.println("rxCharacteristic is not subscribable!");
    peripheral.disconnect();
    return;
}

I have both bluefruit and nano ble 33 boards so can check too if needed.

I did that too, using this sequence:

if (!rxCharacteristic) {
    Serial.println("Peripheral does not have RX characteristic!");
    peripheral.disconnect();
    return;
  } 
  if (!rxCharacteristic.canSubscribe()) {
    Serial.println("rxCharacteristic is not subscribable!");
    peripheral.disconnect();
    return;
  } 
  if (!rxCharacteristic.subscribe()) {
    Serial.println("rxCharacteristic subscription failed!");
    peripheral.disconnect();
    return;
  }

and got the same thing. Additionally, I used the same sort of sequence in the SensorTagButton example:

// retrieve the simple key characteristic
  BLECharacteristic simpleKeyCharacteristic = peripheral.characteristic("6e400003-b5a3-f393-e0a9-e50e24dcca9e");

  // subscribe to the simple key characteristic
  Serial.println("Subscribing to simple key characteristic ...");
  if (!simpleKeyCharacteristic) {
    Serial.println("no simple key characteristic found!");
    peripheral.disconnect();
    return;
  }
  if (!simpleKeyCharacteristic.canSubscribe()) {
    Serial.println("simple key characteristic is not subscribable!");
    peripheral.disconnect();
    return;
  }
  if (!simpleKeyCharacteristic.subscribe()) {
    Serial.println("subscription failed!");
    peripheral.disconnect();
    return;
  }

to no avail.

There's two parts to a BLE connection. If Central function/code appears to be ok, then one has to put assumption aside and look at the peripheral code. Just to be sure.

Which Bluefruit example did you base it off. Did you test with your own defined characteristic or just rely on the default characteristic etc. As such it would help if we see the Bluefruit code to see how the GATT service characteristics were defined.

You can ignore my previous comment. I decided to test this issue using my Bluefruit device as peripheral and nano BLE 33 as central.

I modified the LEDcontrol example for the nano to include the characteristics as you’ve done and I too get the same error.

I had a look at the BLE central library’s BLECharacteristic.cpp file and for some reason this does not return true.

bool BLECharacteristic::subscribe()
{
  if (_remote) {
    return _remote->writeCccd((properties() & BLEIndicate) ? 0x0002 : 0x0001);
  }

  return false;
}

In the BLERemoteCharacteristic.cpp file we have the function:

bool BLERemoteCharacteristic::writeCccd(uint16_t value)
{
  int numDescriptors = descriptorCount();

  for (int i = 0; i < numDescriptors; i++) {
    BLERemoteDescriptor* d = descriptor(i);

    if (strcmp(d->uuid(), "2902") == 0) {
      return d->writeValue((uint8_t*)&value, sizeof(value));
    }
  }

  if (_properties & (BLENotify | BLEIndicate)) {
    // no CCCD descriptor found, fallback to _valueHandle + 1
    BLERemoteDescriptor cccd(NULL, 0, _connectionHandle, _valueHandle + 1);

    return cccd.writeValue((uint8_t*)&value, sizeof(value));
  }

  return false;
}

Well here's a surprise. It appears to be a Bluefruit issue??

Thanks to similar BLE libraries being written by the same author for Arduino 101 and BBC microbit, I was able to test different combo's.

I uploaded a similar peripheral example onto a BBC microbit and this worked with the Nano!

The Arduino Nano BLE did not have this subscribe issue.

I then uploaded a similar central example onto my Arduino 101 (curieBLE) and this too had no problems with the subscribing to BBC microbit but strangely enough it did have problems with my Bluefruit device.

That's really unfortunate, since there really doesn't seem to be an alternative to the BlueFruit in terms of interfacing windows with BLE (since windows support of BLE is basically nil). You wouldn't happen to know of any alternatives would you?

I just tried using ESP32 as a BLE central device as I know that library is quite different to those other libraries tested and it too does the "can it notify" and then apply the notify subscribe request.

Well, surprise surprise, the ESP32 works and does not have a problem with the Bluefruit device when requesting to be notified.

So something in the central library for the nano BLE (and the retired curieBLE library for Arduino 101) is not working.

I had a look on ArduinoBLE GitHub repository and it appears this problem with the subscribe was raised back in Sept 2019 and is still open.

Indeed, I read through that thread in trying to diagnose my issue. I’m going to buy another an ESP32 and replace the Nano33, and hopefully I’ll have better luck!