Bluno notifiable characteristic missing the client configuration descriptor

The DFRobot Bluno Boards are a series of Arduino-compatible development boards — such as Bluno Uno and Bluno Nano — developed by DFRobot with an integrated Bluetooth 4.0 (BLE) module based on the TI CC2540 chip. They add wireless communication, smartphone control, and even wireless programming to Arduino projects. The boards are compatible with Arduino Nano, Arduino UNO, Arduino Mega 1280, and Arduino Mega 2560.

The boards work well with Android and iOS applications because DFRobot provides mobile SDKs for those platforms. On Windows, however, the boards cannot be used with applications that rely on the standard Microsoft Bluetooth LE API. Connecting, reading, and writing attributes all work, but characteristic change notifications never arrive. The board firmware is missing the client configuration descriptor on its notifiable characteristic, and the Windows WinRT GATT API requires one.

The Bluetooth Framework solves this. With the ForceNotifications property set to true, an application can subscribe to the characteristic and receive change notifications as expected.


How it works

To test the feature, use the GattClient sample application from the Bluetooth Framework package. Start the sample, click Discover, and select the board in the found devices list. Enable the Force indications checkbox and click Connect. The board is paired during connection using the default PIN, which is 0000. If the board uses a different PIN, change it in the OnPinRequest event handler.

When the connection is established — the Connected event fires with 0x00000000 in the Error parameter — the application can start working with the board. After reading services and characteristics, select the notifiable one and click Subscribe. The application then receives characteristic change notifications.

To work with Bluno boards in code, use the wclGattClient class. The ForceNotifications property must be set to true to enable characteristic change notifications from Bluno boards, or from any other GATT-enabled device that does not have the client configuration descriptor. Because the board is paired during connection, the application must handle the OnPinRequest event of the wclBluetoothManager class. The default PIN is 0000.

Once the connection is established, the application starts receiving notifications by calling the Subscribe method of the wclGattClient class. It must not call WriteClientConfiguration in this case, because the boards do not have the client configuration descriptor configured for the characteristic.


Code example

The snippets below are taken from the GattClient sample. They show the three essential steps: handling the PIN request, setting ForceNotifications from the Force indications checkbox, and calling Subscribe after discovering characteristics.

procedure TfmMain.wclBluetoothManagerPinRequest(Sender: TObject;
  const Radio: TwclBluetoothRadio; const Address: Int64;
  out Pin: String);
begin
  Pin := '0000';
end;

procedure TfmMain.btConnectClick(Sender: TObject);
var
  Res: Integer;
  Item: TListItem;
begin
  Item := lvDevices.Selected;
  wclGattClient.ForceNotifications := cbForceIndications.Checked;
  Res := wclGattClient.Connect(TwclBluetoothRadio(Item.Data));
  // Check Res for errors...
end;

procedure TfmMain.btSubscribeClick(Sender: TObject);
var
  Characteristic: TwclGattCharacteristic;
begin
  // FCharacteristics is populated after reading characteristics
  Characteristic := FCharacteristics[lvCharacteristics.Selected.Index];
  wclGattClient.Subscribe(Characteristic);
end;
void __fastcall TfmMain::wclBluetoothManagerPinRequest(
    TObject *Sender, const TwclBluetoothRadio *Radio,
    const __int64 Address, String& Pin)
{
    Pin = "0000";
}

void __fastcall TfmMain::btConnectClick(TObject *Sender)
{
    TListItem* Item = lvDevices->Selected;
    wclGattClient->ForceNotifications =
        cbForceIndications->Checked;
    int Res = wclGattClient->Connect(
        (TwclBluetoothRadio*)Item->Data);
    // Check Res for errors...
}

void __fastcall TfmMain::btSubscribeClick(TObject *Sender)
{
    TwclGattCharacteristic Characteristic =
        FCharacteristics[lvCharacteristics->Selected->Index];
    wclGattClient->Subscribe(Characteristic);
}
void Manager_OnPinRequest(Object Sender,
    wclBluetoothRadio Radio, Int64 Address, out String Pin)
{
    Pin = "0000";
}

private void btConnect_Click(Object sender, EventArgs e)
{
    ListViewItem Item = lvDevices.SelectedItems[0];
    Client.ForceNotifications = cbForceIndications.Checked;
    Int32 Res = Client.Connect((wclBluetoothRadio)Item.Tag);
    // Check Res for errors...
}

private void btSubscribe_Click(Object sender, EventArgs e)
{
    wclGattCharacteristic Characteristic =
        FCharacteristics[
            lvCharacteristics.SelectedItems[0].Index];
    Client.Subscribe(Characteristic);
}
Private Sub Manager_OnPinRequest(ByVal Sender As Object,
      ByVal Radio As wclBluetooth.wclBluetoothRadio,
      ByVal Address As Long, ByRef Pin As String)
      Handles Manager.OnPinRequest
  Pin = "0000"
End Sub

Private Sub btConnect_Click(ByVal sender As System.Object,
      ByVal e As System.EventArgs) Handles btConnect.Click
  Dim Item As ListViewItem = lvDevices.SelectedItems(0)
  Client.ForceNotifications = cbForceIndications.Checked
  Dim Res As Int32 = Client.Connect(
      CType(Item.Tag, wclBluetoothRadio))
  ' Check Res for errors...
End Sub

Private Sub btSubscribe_Click(ByVal sender As System.Object,
      ByVal e As System.EventArgs) Handles btSubscribe.Click
  Dim Characteristic As wclGattCharacteristic =
      FCharacteristics(
          lvCharacteristics.SelectedItems(0).Index)
  Client.Subscribe(Characteristic)
End Sub
void CGattClientDlg::wclBluetoothManagerPinRequest(
    void* Sender, CwclBluetoothRadio* const Radio,
    const __int64 Address, tstring& Pin)
{
    Pin = _T("0000");
}

// Connect button click
void CGattClientDlg::OnBnClickedButtonConnect()
{
    int Item = lvDevices.GetNextSelectedItem(Pos);
    wclGattClient.ForceNotifications =
        (cbForceIndications.GetCheck() != FALSE);
    int Res = wclGattClient.Connect(
        (CwclBluetoothRadio*)lvDevices.GetItemData(Item));
    // Check Res for errors...
}

void CGattClientDlg::OnBnClickedButtonSubscribe()
{
    wclGattCharacteristics::iterator it =
        FCharacteristics.begin();
    std::advance(it,
        lvCharacteristics.GetNextSelectedItem(Pos));
    wclGattCharacteristic Characteristic = (*it);
    wclGattClient.Subscribe(Characteristic);
}

Frequently asked questions

Why can't I receive notifications from DFRobot Bluno boards on Windows?
The boards are missing a client configuration descriptor on their notifiable characteristic, and the Windows WinRT GATT API requires one. The Bluetooth Framework bypasses this bug.
How to enable notifications on Bluno boards with Bluetooth Framework?
Set the wclGattClient.ForceNotifications property to true before subscribing. The framework then handles the missing descriptor and lets the application receive characteristic change notifications.
What is the default PIN for pairing with Bluno boards?
The default PIN is 0000. The application must handle the OnPinRequest event of wclBluetoothManager and provide this PIN during pairing.