Characteristic is a fundamental data unit in Bluetooth Low Energy, through which a Central reads or writes information on a peripheral device. Each Characteristic belongs to a specific GATT service, has a unique UUID, and a set of properties (read, write, notify, indicate) that define the possible operations. According to Bluetooth Core Specification 5.4 (2023), Bluetooth SIG has specified over 500 standard characteristics for medical, fitness, and industrial devices. Developers create custom characteristics for transmitting any user data — from sensor readings to device control commands.
Key Takeaways
A Characteristic is a GATT protocol attribute that contains a value and metadata. In BLE architecture, data is not transferred directly between devices but through reading and writing characteristic values of a service. If a service is a folder, then a Characteristic is a file inside that folder.
Each Characteristic consists of three components: declaration, value, and descriptors. The declaration contains the characteristic's UUID and its properties. The value is the actual data transferred between Central and Peripheral. Descriptors provide additional configuration.
According to the Bluetooth Core Specification 5.4 (2023), all data exchanges in BLE occur through operations on characteristics. Even standard profiles such as Heart Rate Profile or Battery Service are built on a set of characteristics with predefined UUIDs. This ensures compatibility of devices from different manufacturers without prior configuration.
It is important for developers to understand: each Characteristic can support different property combinations. One characteristic may be read-only, another for writing, a third for notifications. The correct choice of properties determines the use case and power consumption of the device.
Properties of a characteristic define which operations are allowed on it. This is a byte mask where each bit enables or disables a specific operation. The main properties are listed below.
| Property | Bit | Description | Typical Use |
|---|---|---|---|
| Read | 0x02 | Central can read the current value | Status, battery level, configuration |
| Write | 0x08 | Central can write a new value | Control commands, settings |
| Notify | 0x10 | Peripheral sends value without confirmation | Streaming data (heart rate, temperature) |
| Indicate | 0x20 | Peripheral sends value with confirmation | Critical data (alerts, statuses) |
| Write Without Response | 0x04 | Write without waiting for server confirmation | High-speed command transmission |
Permissions are the access level at the GATT server layer. Unlike properties, which are declared in the characteristic declaration, permissions are checked on each operation. They may include encryption and authentication requirements.
Bluetooth SIG has specified over 500 standard characteristics that cover most common BLE use cases. Using standard UUIDs ensures that any receiving device correctly interprets the data without prior configuration.
Here are the most commonly used standard characteristics:
| UUID | Name | Data Type | Service |
|---|---|---|---|
| 0x2A19 | Battery Level | uint8 (0–100%) | Battery Service |
| 0x2A37 | Heart Rate Measurement | uint8 + flags | Heart Rate |
| 0x2A6E | Temperature | int16 (0.01°C) | Environmental Sensing |
| 0x2A6F | Humidity | uint16 (0.01%) | Environmental Sensing |
| 0x2A00 | Device Name | UTF-8 string | Generic Access |
| 0x2A01 | Appearance | uint16 | Generic Access |
If an existing standard characteristic covers your task, use it. This simplifies Bluetooth certification and improves ecosystem compatibility. Create custom characteristics only for unique data not in the SIG registry.
Creating a characteristic is done on the Peripheral side — the device that provides data. Let us look at implementations on iOS (Swift) and Android (Java).
Core Bluetooth provides the CBMutableCharacteristic class for creating a characteristic with a UUID, properties, and initial value.
import CoreBluetooth
let characteristicUUID = CBUUID("2A19") // Battery Level characteristic
let characteristic = CBMutableCharacteristic(
type: characteristicUUID,
properties: [.read, .notify],
value: nil,
permissions: [.readable]
)
// Update value on change
let batteryData = Data([batteryLevel]) // uint8
peripheralManager.updateValue(
batteryData,
for: characteristic,
onSubscribedCentrals: nil
)
On Android, a characteristic is created using BluetoothGattCharacteristic with a UUID, properties, and permissions.
import android.bluetooth.*;
UUID charUuid = UUID.fromString("00002A19-0000-1000-8000-00805F9B34FB");
BluetoothGattCharacteristic characteristic =
new BluetoothGattCharacteristic(
charUuid,
BluetoothGattCharacteristic.PROPERTY_READ
| BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ
);
// Set the value
characteristic.setValue(batteryLevel, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
gattServer.notifyCharacteristicChanged(device, characteristic, false);
Operations on a Characteristic fall into three types: read, write, and notify/indicate. The choice depends on the scenario: data on demand is read, commands are written, streaming data subscribes to notifications.
Read — Central sends a request to read the characteristic value. Peripheral responds with the current value. The operation is synchronous and requires an explicit request from each side. Used for data that rarely changes: firmware version, serial number, settings.
Write — Central sends data to Peripheral. There are two modes: Write with Response (confirmation from Peripheral) and Write Without Response (no confirmation). Write with Response guarantees delivery — Peripheral sends confirmation after writing. Write Without Response is faster but does not guarantee delivery.
Notify and Indicate — Peripheral initiates data transmission to Central. With Notify, data is sent without confirmation — if Central fails to receive the packet, it is lost. With Indicate, Central sends confirmation (PDU level), ensuring delivery. Indicate is slower but more reliable. To subscribe to notifications, Central writes the value 0x0001 to the CCCD (Client Characteristic Configuration Descriptor).
MTU (Maximum Transmission Unit) defines the maximum size of a single BLE data packet. By default, the MTU is 23 bytes, of which 3 bytes are the header — payload (ATT payload) equals 20 bytes. This is sufficient for most sensor data but not enough for file transfers or large configurations.
Bluetooth Core Specification 5.4 supports MTU negotiation — Central and Peripheral can agree on a larger packet size of up to 517 bytes. The process works as follows: Central sends an MTU Exchange request with its proposed MTU; Peripheral responds with its MTU; the smaller of the two values is used.
// iOS requests MTU on connect
// Max MTU in iOS is 185 bytes
func peripheral(
_ peripheral: CBPeripheral,
didDiscoverServices error: Error?
) {
// Request MTU for specific peripheral
peripheral.maximumWriteValueLength(for: .withResponse)
}
According to Bluetooth SIG (2023), increasing the MTU from 23 to 185 bytes reduces data transmission overhead by up to 80% due to fewer packets. For applications transmitting high-frequency readings (e.g., ECG or accelerometer), MTU increase is critical for stream stability.
Frequently Asked Questions
The BLE specification does not limit the number of characteristics in a service. In practice, the limitation is determined by the available memory of the GATT server and performance requirements. For embedded devices, no more than 10–15 characteristics per service is recommended.
Notify sends data without confirmation — the packet may be lost without notice to the sender. Indicate requires confirmation (ACK) at the protocol level, guaranteeing delivery. Indicate is slower but more reliable. For critical data (alerts, commands), use Indicate.
Yes, a characteristic can have a combination of properties. For example, a settings characteristic can support Read (reading the current value) and Write (changing the setting). Combine properties according to your use case.
Use MTU negotiation to increase the packet size to 185–517 bytes. If the data is still larger, implement fragmentation at the application level: split the data into several sequential requests with integrity control.
If your task is covered by a standard characteristic, use UUIDs from the Bluetooth SIG registry. This simplifies certification and ensures ecosystem compatibility. Create custom UUIDs only for unique third-party data.
Summary
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.
Read also