910 Bluetooth Protocol Stack and GATT
910.1 Learning Objectives
By the end of this chapter, you will be able to:
- Describe the Bluetooth protocol stack from PHY to application layer
- Understand GATT architecture with services, characteristics, and descriptors
- Design custom GATT services for IoT sensor applications
- Choose between Notify and Indicate for different reliability requirements
- Implement standard Bluetooth SIG profiles for interoperability
910.2 Prerequisites
Before diving into this chapter, you should be familiar with:
- Bluetooth Overview: Basic understanding of Bluetooth technology
- Bluetooth Connection Establishment: How BLE connections are formed
910.3 Bluetooth Protocol Stack
910.3.1 Classic Bluetooth Stack
The Classic Bluetooth stack is designed for continuous data streaming:
| Layer | Purpose | Key Components |
|---|---|---|
| Application | User applications | Audio, file transfer |
| Profiles | Application behavior | A2DP, HFP, SPP, HID |
| RFCOMM | Serial port emulation | Virtual COM ports |
| L2CAP | Logical link control | Multiplexing, segmentation |
| Link Manager | Connection management | Security, power control |
| Baseband | Packet handling | Piconet, frequency hopping |
| Radio | Physical transmission | 2.4 GHz, GFSK modulation |
910.3.2 BLE Protocol Stack
BLE uses a simplified stack optimized for low power:
| Layer | Purpose | Key Components |
|---|---|---|
| Application | User applications | Sensor data, beacons |
| GATT | Data organization | Services, characteristics |
| ATT | Attribute protocol | Read, write, notify |
| L2CAP | Logical link | Fixed channels, CoC |
| Link Layer | Packets, connections | Advertising, data channels |
| PHY | Physical layer | 1M, 2M, Coded PHY |
910.3.3 Key Differences
| Aspect | Classic Bluetooth | BLE |
|---|---|---|
| Data Model | Stream-based (RFCOMM) | Attribute-based (GATT) |
| Connection | Persistent | Can be transient |
| Profiles | Complex (A2DP, HFP) | Simple (GATT services) |
| Discovery | Profile-specific | Universal GATT discovery |
910.4 GATT Architecture
GATT (Generic Attribute Profile) organizes data like a file system:
910.4.1 Hierarchy
Profile (Use case definition)
|
+-- Service (Group of related data)
|
+-- Characteristic (Individual data point)
|
+-- Value (The actual data)
+-- Descriptor (Metadata, configuration)
910.4.2 Services
Services group related characteristics:
Standard Services (Bluetooth SIG defined):
| UUID | Service | Description |
|---|---|---|
| 0x1800 | Generic Access | Device name, appearance |
| 0x1801 | Generic Attribute | Service change indication |
| 0x180A | Device Information | Manufacturer, model, serial |
| 0x180F | Battery Service | Battery level |
| 0x181A | Environmental Sensing | Temperature, humidity |
| 0x180D | Heart Rate | Heart rate measurement |
Custom Services:
Use 128-bit UUIDs for proprietary data:
// Custom service UUID (generated)
#define CUSTOM_SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"910.4.3 Characteristics
Characteristics are individual data values with properties:
Properties:
| Property | Code | Description |
|---|---|---|
| Read | 0x02 | Client can read value |
| Write | 0x08 | Client can write (with response) |
| Write No Response | 0x04 | Client can write (no ack) |
| Notify | 0x10 | Server can push (no ack) |
| Indicate | 0x20 | Server can push (with ack) |
Choosing Properties:
| Use Case | Properties | Example |
|---|---|---|
| On-demand sensor reading | Read | Temperature on request |
| Real-time streaming | Notify | Heart rate updates |
| Critical alerts | Indicate | Low battery warning |
| Configuration | Read + Write | Alarm threshold |
| Commands | Write No Response | LED on/off |
910.4.4 Descriptors
Descriptors provide metadata about characteristics:
Common Descriptors:
| UUID | Name | Purpose |
|---|---|---|
| 0x2900 | Characteristic Extended Properties | Additional properties |
| 0x2901 | Characteristic User Description | Human-readable name |
| 0x2902 | Client Characteristic Configuration | Enable notify/indicate |
| 0x2904 | Characteristic Presentation Format | Units, exponent |
CCCD (0x2902):
The Client Characteristic Configuration Descriptor is critical for notifications:
// Client writes to CCCD to enable/disable notifications
// 0x0000 = Disabled
// 0x0001 = Notifications enabled
// 0x0002 = Indications enabled910.5 Notification vs Indication
Understanding when to use each is crucial:
910.5.1 Notifications (Fire-and-Forget)
- Server pushes data without confirmation
- Lower latency (no round-trip)
- Possible data loss if packet dropped
- Best for: High-frequency telemetry
// Server sends notification
pCharacteristic->setValue(sensorData, sizeof(sensorData));
pCharacteristic->notify(); // No confirmation expected910.5.2 Indications (Confirmed Delivery)
- Server waits for client acknowledgment
- Higher latency (requires ACK)
- Guaranteed delivery (retries if failed)
- Best for: Critical data, configuration
// Server sends indication
pCharacteristic->setValue(alertData, sizeof(alertData));
pCharacteristic->indicate(); // Blocks until ACK received910.5.3 Comparison
| Aspect | Notify | Indicate |
|---|---|---|
| Acknowledgment | No | Yes |
| Latency | Lower | Higher |
| Throughput | Higher | Lower |
| Reliability | Best-effort | Guaranteed |
| Use Case | Sensor streaming | Alerts, config |
910.5.4 Common Pitfall
The Mistake: Using indications (confirmed) when notifications (unconfirmed) would suffice, or vice versa, leading to either unnecessary latency/overhead or silent data loss.
Why It Happens: Both seem to “push” data from peripheral to central. Developers don’t realize indications require ACK (adding round-trip latency) while notifications are fire-and-forget.
The Fix: Use notifications for high-frequency telemetry where occasional packet loss is acceptable (sensor readings, heart rate). Use indications only for critical data that must be confirmed (configuration changes, alarms). Never use indications for streaming data—the ACK overhead will throttle throughput to a fraction of potential.
910.6 Designing Custom GATT Services
910.6.1 Example: Environmental Sensor
Design a GATT service for a multi-sensor environmental monitor:
Service Definition:
Environmental Monitoring Service (Custom UUID)
|
+-- Temperature Characteristic
| UUID: 0x2A6E (standard)
| Properties: Read, Notify
| Format: sint16 (0.01 degree resolution)
|
+-- Humidity Characteristic
| UUID: 0x2A6F (standard)
| Properties: Read, Notify
| Format: uint16 (0.01% resolution)
|
+-- Pressure Characteristic
| UUID: Custom 128-bit
| Properties: Read, Notify
| Format: uint32 (0.1 Pa resolution)
|
+-- Sampling Rate Characteristic
UUID: Custom 128-bit
Properties: Read, Write
Format: uint16 (seconds)
910.6.2 Implementation
// Service UUIDs
#define ENV_SERVICE_UUID "181A" // Standard Environmental Sensing
#define TEMP_CHAR_UUID "2A6E" // Standard Temperature
#define HUMIDITY_CHAR_UUID "2A6F" // Standard Humidity
#define PRESSURE_CHAR_UUID "custom-uuid-here"
#define SAMPLING_CHAR_UUID "custom-uuid-here"
void setupGATT() {
// Create service
BLEService* pEnvService = pServer->createService(ENV_SERVICE_UUID);
// Temperature (Read + Notify)
pTempChar = pEnvService->createCharacteristic(
TEMP_CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pTempChar->addDescriptor(new BLE2902()); // Enable CCCD
// Humidity (Read + Notify)
pHumidityChar = pEnvService->createCharacteristic(
HUMIDITY_CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pHumidityChar->addDescriptor(new BLE2902());
// Sampling Rate (Read + Write for configuration)
pSamplingChar = pEnvService->createCharacteristic(
SAMPLING_CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pEnvService->start();
}910.6.3 Data Formatting
BLE uses little-endian byte order:
// Temperature: 25.50 degrees Celsius
// Format: sint16, resolution 0.01
int16_t tempValue = 2550; // 25.50 * 100
uint8_t tempData[2];
tempData[0] = tempValue & 0xFF; // Low byte first
tempData[1] = (tempValue >> 8) & 0xFF; // High byte second
pTempChar->setValue(tempData, 2);
pTempChar->notify();910.7 Standard Profiles
910.7.1 Heart Rate Service (0x180D)
Used by fitness trackers and medical devices:
Characteristics:
| UUID | Name | Properties |
|---|---|---|
| 0x2A37 | Heart Rate Measurement | Notify |
| 0x2A38 | Body Sensor Location | Read |
| 0x2A39 | Heart Rate Control Point | Write |
Data Format (0x2A37):
Byte 0: Flags
Bit 0: Heart Rate Value Format (0=uint8, 1=uint16)
Bit 1-2: Sensor Contact Status
Bit 3: Energy Expended Present
Bit 4: RR-Interval Present
Byte 1-2: Heart Rate Value (uint8 or uint16)
Byte 3-4: Energy Expended (optional, uint16)
Byte 5+: RR-Intervals (optional, uint16[])
910.7.2 Battery Service (0x180F)
Simple battery level reporting:
Characteristics:
| UUID | Name | Properties |
|---|---|---|
| 0x2A19 | Battery Level | Read, Notify |
Data Format:
Single byte, 0-100 representing percentage.
910.7.3 Device Information Service (0x180A)
Manufacturer and model information:
Characteristics:
| UUID | Name | Content |
|---|---|---|
| 0x2A29 | Manufacturer Name | “Acme Inc” |
| 0x2A24 | Model Number | “Sensor-v1” |
| 0x2A25 | Serial Number | “SN123456” |
| 0x2A26 | Firmware Revision | “1.2.3” |
| 0x2A27 | Hardware Revision | “A” |
| 0x2A28 | Software Revision | “2.0.0” |
910.8 Adaptive Frequency Hopping
Bluetooth uses frequency hopping to avoid interference:
Core Concept: Bluetooth mitigates 2.4 GHz interference by rapidly hopping between channels (1600 hops/second for Classic, variable for BLE) and adaptively blacklisting channels where interference is detected. Classic Bluetooth uses 79 channels (1 MHz each), while BLE uses 40 channels (2 MHz each) with 37 for data and 3 for advertising.
Why It Matters: The 2.4 GHz ISM band is the most crowded spectrum in IoT, shared by Wi-Fi, Zigbee, microwave ovens, and other Bluetooth devices. Without frequency hopping, a single interferer (like a Wi-Fi access point on channel 6) could completely block communication. AFH typically recovers from Wi-Fi interference within 5-10 seconds by mapping out and avoiding the occupied frequencies.
Key Takeaway: If you experience Bluetooth audio dropouts or BLE sensor disconnections, check for Wi-Fi channel overlap first. Move your Wi-Fi router to channel 1 or 11 (which overlap fewer Bluetooth data channels), or place the Bluetooth central device away from the Wi-Fi access point. For BLE, advertising channels 37, 38, and 39 are specifically positioned between Wi-Fi channels 1, 6, and 11 to ensure discovery works even in congested environments.
910.9 Summary
This chapter covered the Bluetooth protocol stack and GATT architecture:
- Classic Bluetooth uses RFCOMM for stream-based data (audio, file transfer)
- BLE uses GATT for attribute-based data (sensors, configuration)
- GATT hierarchy: Profile -> Service -> Characteristic -> Descriptor
- Properties (Read, Write, Notify, Indicate) control data access patterns
- Notifications are fast but unconfirmed; Indications are confirmed but slower
- Standard services (Heart Rate, Battery, Device Info) ensure interoperability
- Custom services use 128-bit UUIDs for proprietary data
- Little-endian byte order is mandatory for BLE data formatting
910.10 What’s Next
The next chapter, Bluetooth Profiles, explores the major Bluetooth profiles including SPP (Serial Port), HID (Human Interface Device), and A2DP (Advanced Audio Distribution) for audio streaming.