Descriptor: what it is, Client Characteristic Configuration and examples

Author: IT Sectr Published: 2026-07-15 Reading time: 8 min

Descriptor is an auxiliary attribute of a BLE characteristic that provides metadata or configuration for its value. The Bluetooth Core Specification 5.4 defines descriptors as an integral part of the GATT profile: they describe units of measurement, value ranges, text names, and manage subscription to notifications. The most well-known descriptor is the Client Characteristic Configuration Descriptor (CCCD, 0x2902), which allows the Central to subscribe to notifications from the characteristic. According to Bluetooth SIG, 8 standard descriptors are specified, and custom descriptors can be created by the developer for any additional metadata.

Key Takeaways

  • Descriptor — an auxiliary attribute of a characteristic that contains metadata or configuration of the value.
  • CCCD (0x2902) — the most important descriptor: through it, the Central subscribes to notifications about characteristic changes.
  • Standard descriptors include User Description (0x2901) for text name and Presentation Format (0x2904) for data format.
  • Each characteristic can have multiple descriptors, but no more than one of each standard type.
  • Descriptors are automatically created by Core Bluetooth but can be customized for specific metadata.

What is a Descriptor in BLE?

Descriptor is a GATT protocol attribute that attaches to a Characteristic and provides additional information about its value or how to use it. In GATT terminology, the characteristic is the data, and the descriptor is the metadata of that data.

Each Characteristic can have zero or more descriptors. Descriptors are defined as separate attributes in the GATT table and have their own 16-bit UUIDs in the Bluetooth SIG space. Descriptors cannot exist independently — they are always tied to a specific characteristic.

According to the Bluetooth Core Specification 5.4 (2023), descriptors are divided into two categories: standard (defined by Bluetooth SIG) and custom (defined by the developer). Standard descriptors have UUIDs in the range 0x2900–0x2908 and perform strictly defined functions. Custom descriptors use 128-bit UUIDs, similar to custom services and characteristics.

Descriptors are a key mechanism for configuring the behavior of a BLE connection. Without CCCD (0x2902) it is impossible to subscribe to notifications, without Presentation Format (0x2904) the Central will not know how to interpret the raw bytes of the characteristic.

Client Characteristic Configuration Descriptor (CCCD)

CCCD with UUID 0x2902 is the most important and widely used descriptor in BLE. It manages the Central's subscription to Notify and Indicate from the characteristic. Without writing to CCCD, the characteristic will not send notifications, even if it has the Notify property.

CCCD is a 16-bit value where the least significant bit (0x0001) enables Notify, and the second bit (0x0002) enables Indicate. The Central writes the desired value to CCCD, thereby subscribing to notifications. If the Central writes 0x0000 — the subscription is cancelled.

The CCCD mechanism works as follows: when the Central discovers a characteristic that supports Notify, it finds its CCCD descriptor and writes 0x0001. After that, every time the Peripheral updates the characteristic value, it is automatically sent to the subscribed Central via Handle Value Notification (without a request from the Central).

According to the Apple Core Bluetooth Programming Guide, CCCD is automatically managed by the system when setNotifyValue is called for a characteristic. The developer does not need to manually find and write CCCD — Core Bluetooth does this automatically when notifications are enabled.

Programmatic subscription to notifications

In iOS, subscription is done via the setNotifyValue method on the CBPeripheral object.

swift
import CoreBluetooth

// Subscribe to characteristic notifications
peripheral.setNotifyValue(true, for: characteristic)

// Delegate receives updated values
func peripheral(
    _ peripheral: CBPeripheral,
    didUpdateValueFor characteristic: CBCharacteristic,
    error: Error?
) {
    // characteristic.value has new data
    guard let data = characteristic.value else { return }
}

On Android, subscription to notifications is done via BluetoothGatt.setCharacteristicNotification. The developer must also manually write the value 0x0001 to CCCD.

java
import android.bluetooth.*;

// Enable notifications
bluetoothGatt.setCharacteristicNotification(characteristic, true);

// Write 0x0001 to CCCD
BluetoothGattDescriptor descriptor = characteristic
    .getDescriptor(
        UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
    );
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);

Standard Bluetooth SIG Descriptors

Bluetooth SIG has defined 8 standard descriptors for common characteristic configuration tasks. Each descriptor has a strictly defined UUID and value format.

UUIDDescriptor NamePurposeValue Type
0x2900Characteristic Extended PropertiesAdditional characteristic propertiesuint16 (bit mask)
0x2901Characteristic User DescriptionText name of the characteristicUTF-8 string
0x2902Client Characteristic ConfigurationNotification managementuint16
0x2903Server Characteristic ConfigurationServer configuration for broadcastuint16
0x2904Characteristic Presentation FormatData presentation formatStructure (7 bytes)
0x2905Characteristic Aggregate FormatCombining multiple characteristicsList of UUIDs
0x2906Valid RangeRange of valid valuesuint16 × 2 (min, max)
0x2907External Report ReferenceExternal report referenceuint16
0x2908Report ReferenceInternal report referenceuint8 + uint16

Characteristic User Description (0x2901) is especially useful for UI: it contains a human-readable name of the characteristic that can be displayed in the application interface. For example, a Battery Level characteristic may have a User Description descriptor with the value “Battery charge level”.

Characteristic Presentation Format (0x2904) contains information on how to interpret the characteristic value: format (uint8, uint16, float), exponent, unit of measurement (via UUID from the Bluetooth SIG registry), namespace, and description. For example, temperature can be int16 with exponent -2 (division value 0.01°C) and unit of measurement “degree Celsius”.

How to Use Descriptors in iOS and Android

Access to descriptors differs on iOS and Android. Core Bluetooth automatically manages CCCD when notifications are enabled, while Android requires manual writing of the value to the descriptor. Let's look at both approaches.

iOS: Automatic CCCD Management

In iOS, the developer usually does not interact with descriptors directly. Core Bluetooth automatically finds CCCD and writes the required value when setNotifyValue is called. The discoverDescriptors method is used to read descriptors.

swift
// Discover descriptors
peripheral.discoverDescriptors(for: characteristic)

// Read User Description value
func peripheral(
    _ peripheral: CBPeripheral,
    didDiscoverDescriptorsFor characteristic: CBCharacteristic,
    error: Error?
) {
    for descriptor in characteristic.descriptors ?? [] {
        if descriptor.uuid == CBUUID("2901") {
            peripheral.readValue(for: descriptor)
        }
    }
}

Android: Manual CCCD Management

On Android, the developer must find the CCCD descriptor of the characteristic and write the value to enable notifications. This is an additional step compared to iOS.

java
// Read User Description descriptor value
BluetoothGattDescriptor userDesc = characteristic
    .getDescriptor(
        UUID.fromString("00002901-0000-1000-8000-00805F9B34FB")
    );

if (userDesc != null) {
    bluetoothGatt.readDescriptor(userDesc);
}

Creating Custom Descriptors

Custom descriptors are used when the standard Bluetooth SIG descriptors are not sufficient. For example, a medical sensor manufacturer might add a descriptor with sensor calibration information or a descriptor with an error log.

A custom descriptor is created with a 128-bit UUID, similar to a custom characteristic. On the Peripheral side, the developer adds it to the characteristic via the appropriate API. On the Central side, the descriptor is discovered automatically when discoverDescriptors is called.

When creating a custom descriptor, follow these rules: the value length must match the expected data type, and access rights (read/write) must be explicitly specified. Do not use UUIDs from the standard Bluetooth SIG descriptor range (0x2900–0x2908) for custom purposes.

Descriptor vs Characteristic

Characteristic and descriptor are different levels of GATT attributes. A characteristic is a data point, while a descriptor is its metadata or configuration. The difference manifests in several aspects: purpose, UUID, and access rules.

A characteristic has a mandatory declaration that is always present in the GATT table. A descriptor is optional — a characteristic may have no descriptors. A characteristic can be read, written, or send notifications independently. A descriptor is always tied to a characteristic and does not exist separately.

According to the Bluetooth Core Specification 5.4, the order of attributes in the GATT table is as follows: service declaration, characteristic declaration, characteristic value, characteristic descriptors (if any), then the next service. This order is mandatory for correct GATT client operation.

Frequently Asked Questions

Why is CCCD needed if the characteristic already has the Notify property?

CCCD is needed for subscription management: the Notify property only indicates that the characteristic can send notifications, but does not enable them. The Central must explicitly subscribe by writing 0x0001 to CCCD. This prevents unwanted data transmission to an unprepared Central.

How many descriptors can a single characteristic have?

The specification does not limit the number of descriptors. In practice, each characteristic usually has 1–3 descriptors: CCCD (for characteristics with Notify/Indicate), optionally User Description and Presentation Format. Too many descriptors increase the GATT table size.

Do I need to manually create descriptors on the Peripheral?

In most cases, no. Core Bluetooth on iOS and Android automatically add CCCD to characteristics that have Notify or Indicate properties. Custom descriptors are created manually only for specific metadata.

Can I write custom data to CCCD?

No, CCCD has a strictly defined format (uint16) and purpose — notification management. Writing other values violates the Bluetooth specification and may cause the subscription to stop working. For custom data, use custom descriptors with 128-bit UUIDs.

How is the Characteristic User Description useful for an application?

The User Description contains a human-readable name of the characteristic — for example, “Case temperature” instead of UUID 0x2A6E. The application can display this name in the interface, making interaction with the BLE device understandable for the user without hardcoded strings.

Summary

  • Descriptor — an auxiliary GATT attribute attached to a characteristic for storing metadata or configuration.
  • CCCD (0x2902) — the key descriptor that manages the Central's subscription to notifications and indications of the characteristic.
  • Bluetooth SIG has specified 8 standard descriptors (0x2900–0x2908) for common tasks — description, formatting, and configuration.
  • In iOS, CCCD is managed automatically; on Android, manual writing of the value 0x0001 is required to enable notifications.
  • Custom descriptors are created with a 128-bit UUID for unique metadata not covered by standard descriptors.
  • Descriptors do not exist independently — they are always tied to a characteristic and follow it in the GATT table.
  • Proper use of descriptors ensures correct BLE connection configuration and data clarity for the application.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also