29  Bluetooth GATT Review

29.1 Learning Objectives

  • Diagnose and fix the CCCD enablement requirement for BLE notifications using descriptor writes
  • Apply standard GATT services (Environmental Sensing, Heart Rate) instead of custom UUIDs for interoperability
  • Calculate supervision timeout values using the formula: timeout > (1 + latency) × interval × 2
  • Design BLE connection parameters within iOS constraints (15ms-2s interval, max 30 latency)
  • Implement bonding key persistence using non-volatile storage to survive device reboots

GATT (Generic Attribute Profile) is how BLE devices organize and share their data. This review covers common mistakes and misconceptions about GATT, like confusing services with characteristics or misunderstanding notification behavior. Avoiding these pitfalls saves hours of debugging in real BLE projects.

In 60 Seconds

The most common BLE implementation mistakes involve forgetting to write to the CCCD before expecting notifications, using custom UUIDs when standard GATT services exist, and miscalculating supervision timeout relative to connection interval and peripheral latency. Design for Apple iOS parameter constraints first to ensure cross-platform compatibility.

Key Concepts
  • GATT Caching: BLE 5.1 feature where a client stores the peer’s GATT service structure (with a database hash) to skip service discovery on reconnection, reducing connection setup latency
  • ATT Error Response: 9-byte PDU returned when an ATT operation fails; error codes include 0x01 (Invalid Handle), 0x02 (Read Not Permitted), 0x0F (Insufficient Encryption)
  • BLE Privacy: Feature using Resolvable Private Addresses (RPAs) that change periodically; prevents device tracking by passive scanners while allowing bonded devices to recognize each other
  • LE Secure Connections OOB: Pairing method where ECDH public keys are exchanged out-of-band (NFC, QR code) before BLE connection, providing the highest level of MITM protection
  • Characteristic Value Truncation: Silent data loss that occurs when a notification payload exceeds (ATT MTU - 3) bytes without negotiating larger MTU; no error is returned to the application
  • BLE Connection Supervision: Link Layer mechanism that drops a connection after supervision_timeout milliseconds without a valid packet, independent of application layer health checks
  • Pairing vs Bonding: Pairing establishes a temporary session key (STK/LTK) for the current connection; bonding additionally stores the LTK in non-volatile memory for future reconnections without re-pairing
  • BLE Whitelist (Filter Accept List): Controller-level filter that only responds to advertising or connection requests from listed device addresses, used to implement private networks
Minimum Viable Understanding

The most common BLE implementation mistakes involve forgetting to write to the CCCD before expecting notifications, using custom UUIDs when standard GATT services exist, and miscalculating supervision timeout relative to connection interval and peripheral latency. Designing for Apple iOS parameter constraints first ensures cross-platform compatibility.

Series Navigation

This is Part 3 of the Bluetooth Comprehensive Review series:

  1. Overview and Visualizations - Protocol stacks, state machines, initial scenarios
  2. Advanced Scenarios - Medical security, battery drain, smart locks
  3. GATT Pitfalls (this chapter) - Common implementation mistakes
  4. Assessment - Visual gallery and understanding checks

29.2 Common GATT Implementation Pitfalls

Pitfall: Not Enabling CCCD Before Expecting Notifications

The Mistake: Creating a BLE peripheral with NOTIFY characteristics, but notifications never arrive at the central device. The central connects, discovers services, but never receives pushed data even though the peripheral is calling notify().

Why It Happens: BLE notifications require the client to explicitly enable them by writing to the Client Characteristic Configuration Descriptor (CCCD). This is a security and power-saving feature - peripherals don’t spam data until the client opts in. Many developers forget this step, especially when transitioning from other protocols.

The Fix: After discovering characteristics, the central must write to the CCCD descriptor (UUID 0x2902):

  • Discover the CCCD handle for the notifying characteristic, not only the characteristic value handle.
  • Enable the client callback path in the platform stack. On Android, this means calling setCharacteristicNotification(characteristic, true).
  • Write the subscription value to the CCCD: 0x01 0x00 for notifications or 0x02 0x00 for indications. The byte order is little-endian.
  • Wait for the descriptor-write success callback before expecting notification callbacks.
  • Gate peripheral notifications on CCCD state. The peripheral should update an internal notifications_enabled flag when the CCCD is written and call notify() only when that flag is true. Otherwise, clients must poll with READ.

Common symptoms: Works in nRF Connect (which auto-enables CCCD) but fails in custom apps. Debug by checking CCCD value after connection.

Pitfall: Using Custom UUIDs When Standard GATT Services Exist

The Mistake: Creating custom 128-bit UUIDs for common sensor data (temperature, heart rate, battery level) instead of using Bluetooth SIG standard services, breaking interoperability with existing apps and tools.

Why It Happens: Developers either don’t know standard services exist, or want “full control” over data format. Custom UUIDs require custom apps on every platform, while standard services work with generic BLE tools and OS integrations.

The Fix: Check the Bluetooth SIG GATT specifications before creating custom services. Use standard UUIDs with defined data formats whenever they fit:

  • Temperature and humidity: Environmental Sensing Service 0x181A, Temperature 0x2A6E, Humidity 0x2A6F.
  • Battery level: Battery Service 0x180F, Battery Level 0x2A19.
  • Heart rate: Heart Rate Service 0x180D, Heart Rate Measurement 0x2A37.
  • Data encoding: Follow the SIG-defined representation. For example, temperature uses a signed 16-bit value in 0.01 degrees Celsius, so 23.45 C is sent as 2345.
  • Interoperability payoff: Generic tools such as nRF Connect and LightBlue can parse the value, operating systems can recognize it, and client developers do not need a private README to decode the payload.

When to use custom UUIDs: Only for truly proprietary functionality that has no standard equivalent (e.g., device-specific configuration, firmware update protocol, vendor-specific commands).

Supervision timeout must accommodate worst-case response time with peripheral latency. Use:

T_supervision > (1 + latency) x T_interval x 2

BLE permits connection intervals from 7.5 ms to 4 s, peripheral latency from 0 to 499, and supervision timeout from 100 ms to 32 s. Platform policies can be stricter.

Example 1: Power-optimized sensor

  • Interval: 400 ms.
  • Latency: 4.
  • Minimum timeout: (1 + 4) x 400 ms x 2 = 4000 ms = 4 s.
  • Recommended timeout: about 6 s after adding a 50% margin.

Example 2: Common underestimated timeout

  • Interval: 200 ms.
  • Latency: 10.
  • Configured timeout: 2 s.
  • Worst-case response gap: (1 + 10) x 200 ms = 2.2 s, which is already longer than the timeout.
  • Result: random idle disconnects when the peripheral uses its full latency allowance.

Example 3: iOS-compatible parameters

  • Interval: 100 ms.
  • Latency: 4.
  • Minimum timeout: (1 + 4) x 100 ms x 2 = 1 s.
  • Recommended timeout: 2 s, because iOS expects at least a 2 s supervision timeout.

Key insight: The factor-of-2 multiplier accounts for connection event retries. Always verify that your timeout exceeds the worst-case response time by at least 50% to handle RF interference and packet loss.

Pitfall: Setting Supervision Timeout Too Short for High-Latency Peripherals

The Mistake: Using a short supervision timeout (e.g., 1 second) for a BLE connection to a peripheral that uses peripheral latency to skip connection events, resulting in unexpected disconnections during normal operation when no data is being exchanged.

Why It Happens: Developers set aggressive timeouts thinking “faster disconnect detection is better,” without accounting for the interaction between connection interval, peripheral latency, and supervision timeout. If peripheral latency allows skipping 10 events at 400ms intervals, the peripheral may not respond for 4 seconds—triggering a 1-second timeout.

The Fix: The supervision timeout must accommodate the worst-case response time based on your connection parameters:

minimum supervision timeout = (1 + peripheral_latency) x connection_interval x 2

Use these checks during design review:

  • Fast response, no latency: 50 ms interval, latency 0, minimum timeout 100 ms. Use 500 ms to 1 s in practice to allow retries and RF loss.
  • Power-optimized sensor: 400 ms interval, latency 4, minimum timeout 4 s. Use 6 to 10 s for a stable field deployment.
  • Ultra-low-power peripheral: 4 s interval, latency 0, minimum timeout 8 s. Use 16 to 32 s if the application can tolerate slow disconnect detection.
  • Common mistake: 200 ms interval, latency 10, timeout 2 s. Worst-case response is 2.2 s before the factor-of-2 safety check, so the central can disconnect while the peripheral is behaving correctly.

Always verify: timeout > (1 + latency) × interval × safety_factor where safety_factor >= 2.

Pitfall: Ignoring iOS Connection Parameter Restrictions

The Mistake: Designing a BLE peripheral with connection parameters optimized for Android or embedded gateways (e.g., 500ms-4s connection intervals), then discovering that iPhones reject or override these parameters, causing connection failures or poor battery life on iOS.

Why It Happens: Apple enforces stricter connection parameter ranges than the BLE specification allows. Peripherals requesting parameters outside Apple’s limits will have their requests rejected or modified, leading to unexpected behavior that only appears during iOS testing.

The Fix: Design for Apple’s constraints first, then relax for Android or embedded gateways only after testing:

  • Connection interval: use 15 ms minimum and 2 s maximum for iOS compatibility. BLE permits 7.5 ms to 4 s, but iOS policy is stricter.
  • Peripheral latency: keep latency at or below 30, and keep latency x interval <= 2 s.
  • Supervision timeout: use 2 to 6 s and still satisfy timeout > (1 + latency) x interval x 2.
  • Responsive devices: 15 to 30 ms interval, latency 0, timeout 2000 ms.
  • Power-optimized sensors: 100 to 200 ms interval, latency 4, timeout 4000 ms.
  • Environmental sensors: 400 to 500 ms interval, latency 3, timeout 6000 ms.
  • Common mistake: a 4 s interval may work on Android and embedded centrals, but iOS can silently reduce it to 2 s, doubling radio wakeups and battery drain.

Always test on iOS devices early in development—parameter negotiation failures are silent!

29.3 Chapter Summary

Bluetooth has evolved from cable replacement to sophisticated IoT protocol. BLE revolutionized wireless sensors by enabling years of battery life.

Key Points:

  • Classic BT: Continuous connections (audio)
  • BLE: Intermittent, ultra-low power
  • Piconets: 7 active slaves max
  • Mesh: Scalable building automation
  • Profiles: Application-specific behavior
  • Security: Modern encryption & authentication

29.4 Original Source Figures (Alternative Views)

The following figures from the CP IoT System Design Guide provide alternative perspectives on Bluetooth concepts for review and comparison.

Complete Bluetooth protocol stack showing layered architecture from Radio layer through Baseband, Link Manager Protocol, L2CAP, RFCOMM, and Application Profiles including Serial Port Profile, Human Interface Device, Hands-Free Profile, and Advanced Audio Distribution Profile

Source: CP IoT System Design Guide, Chapter 4 - Networking

BLE-specific protocol stack showing simplified architecture compared to Classic Bluetooth: Physical Layer, Link Layer, L2CAP, ATT (Attribute Protocol), GATT (Generic Attribute Profile), and GAP (Generic Access Profile) for connection management

Source: CP IoT System Design Guide, Chapter 4 - Networking

Detailed comparison matrix of Bluetooth Classic, BLE, Zigbee, and Wi-Fi covering IEEE standards, frequency bands, data rates, range, power consumption, network size, and optimal use cases for informed protocol selection in IoT projects

Source: CP IoT System Design Guide, Chapter 4 - Networking

Bluetooth Classic packet format showing 72-bit access code for synchronization and piconet identification, 54-bit header with AM_ADDR, packet type, flow control and error checking fields, and variable-length payload section

Source: CP IoT System Design Guide, Chapter 4 - Networking

BLE data frame showing 1-byte preamble, 4-byte access address, variable PDU with header and payload, and 3-byte CRC for reliable data transmission in low-power applications

Source: CP IoT System Design Guide, Chapter 4 - Networking

Summary of Bluetooth operational modes comparing Active, Sniff, Hold, and Park states in terms of power consumption, response latency, and appropriate use cases for battery optimization in connected devices

Source: CP IoT System Design Guide, Chapter 4 - Networking

29.6 Summary

  • CCCD subscription is mandatory for BLE notifications – clients must write 0x0001 to the Client Characteristic Configuration Descriptor (UUID 0x2902) before the peripheral will push data
  • Use standard GATT services (e.g., Environmental Sensing 0x181A, Heart Rate 0x180D) whenever possible to ensure interoperability with generic BLE apps and OS integrations
  • Calculate supervision timeout carefully using the formula: timeout > (1 + peripheral_latency) x connection_interval x 2 to avoid random disconnections during idle periods
  • Design for iOS constraints first – Apple enforces stricter connection parameter limits (max 2s interval, max 30 latency) that silently override requests outside the allowed range
  • Store bonding keys in non-volatile storage (NVS/flash) to persist across reboots and avoid requiring re-pairing after device restarts
  • Enforce security at the device level, not just in the app – other BLE clients can bypass app-only controls, so GATT characteristic permissions and device-side timeouts are essential for regulated devices

Common Pitfalls

Setting all GATT characteristic permissions to “Read/Write No Security” exposes sensitive data (device configuration, health metrics, access credentials) to any nearby BLE device without authentication. Implement minimum required permissions: read-only data → Read without security; configuration data → Authenticated Write; keys/credentials → Encrypted Authenticated Read/Write.

BLE GATT clients that assume all operations succeed and do not parse ATT Error Responses will silently fail when a server returns errors. Common causes: insufficient encryption (write to characteristic requiring authenticated pairing), attribute handle changed after firmware update (handle stale from cache), or write value out of characteristic value range. Always implement ATT error handler and log the opcode, handle, and error code.

BLE pairing produces both a Short Term Key (STK, used only for the current pairing session) and, during bonding, a Long Term Key (LTK, stored for future sessions). Using the STK beyond its intended scope or misunderstanding that the LTK must be securely stored in NVS is a common implementation error. The STK should be discarded after the pairing session; only the LTK (and IRK, CSRK) should be persisted.

BLE GATT Write (without response) is fire-and-forget with no acknowledgment; packet loss is silent. GATT Write (with response) provides ATT acknowledgment but no application-level confirmation of processing. For reliable command delivery, implement an application-level acknowledgment: send a command via Write With Response, wait for a confirmation characteristic notification within 500 ms, and retry up to 3 times on timeout.

29.7 What’s Next

Continue with these chapters to apply and reinforce the GATT debugging patterns from this review: