GATT (Generic Attribute Profile) es un perfil de Bluetooth Low Energy (BLE) que define la estructura de datos y las reglas de intercambio de información entre dispositivos BLE. GATT se construye sobre el Protocolo de Atributos (ATT) y organiza los datos en una jerarquía: servicios, características y descriptores. Según Bluetooth SIG (2025), el perfil GATT se utiliza en el 98% de todas las aplicaciones BLE, desde rastreadores de fitness hasta cerraduras inteligentes y sensores médicos.
Puntos clave
GATT (Generic Attribute Profile) es un perfil de Bluetooth Low Energy (BLE) que define cómo dos dispositivos BLE intercambian datos a través del Protocolo de Atributos (ATT). GATT estandariza la estructura de datos: todos los datos se organizan en una jerarquía “servicio → característica → descriptor”. El perfil se introdujo en la especificación Bluetooth 4.0 (2010) junto con BLE y desde entonces sigue siendo el mecanismo principal de transferencia de datos para dispositivos Bluetooth de bajo consumo. A diferencia del Bluetooth clásico, donde los datos se transmiten a través de un puerto serie (SPP), GATT proporciona acceso estructurado a los datos mediante operaciones de lectura, escritura y notificaciones.
La pila BLE consta de varias capas: Capa Física (radio), Capa de Enlace (gestión de conexión), L2CAP (control de enlace lógico), ATT (Protocolo de Atributos — acceso a atributos) y GATT (perfil basado en ATT). GATT es la capa superior con la que trabajan los desarrolladores de aplicaciones. El ATT subyacente proporciona operaciones básicas: lectura, escritura, notificación e indicación de atributos. GATT añade semántica: define qué son un servicio y una característica, cómo se agrupan y qué reglas se aplican al leerlos y escribirlos. Según la Bluetooth Core Specification 5.4 (2023), GATT admite hasta 65 535 atributos (servicios + características + descriptores) en un solo dispositivo.
La jerarquía GATT consta de tres niveles. Un Servicio es un grupo lógico de características que resuelve una tarea (por ejemplo, “Servicio de Batería” o “Servicio de Frecuencia Cardíaca”). Una Característica es una unidad de datos con un tipo conocido: nivel de batería actual, lectura de sensor, estado de interruptor. Cada característica tiene un valor y uno o más Descriptores que describen metadatos: unidades de medida, configuración de notificaciones, rango de valores. UUID (Identificador Único Universal) identifica de forma única cada servicio y cada característica.
GATT admite cuatro tipos de operaciones para interactuar con características. Read — el cliente solicita el valor actual de una característica al servidor. Write — el cliente envía un nuevo valor al servidor. Notify — el servidor envía un valor al cliente sin confirmación (más rápido pero menos fiable). Indicate — el servidor envía un valor con confirmación de recepción (más fiable pero más lento). El desarrollador elige el tipo según el escenario: para lecturas de sensores de frecuencia cardíaca, Notify es suficiente; para escribir la configuración de una cerradura inteligente, se necesita Write con confirmación.
// Estructura de datos GATT en código Android
data class BleService(
val uuid: UUID,
val characteristics: List<BleCharacteristic>
)
data class BleCharacteristic(
val uuid: UUID,
val properties: Int, // READ, WRITE, NOTIFY, INDICATE
val descriptors: List<BleDescriptor>,
var value: ByteArray?
)
La especificación BLE define dos perfiles principales: GAP (Generic Access Profile) y GATT (Generic Attribute Profile). GAP se encarga del descubrimiento de dispositivos, establecimiento de conexión y gestión de visibilidad — es la “capa de red” de BLE. GATT se encarga del intercambio de datos después de establecer la conexión — la “capa de aplicación.” El desarrollador usa GAP para escanear y conectarse a un dispositivo, y GATT para leer, escribir y recibir notificaciones del dispositivo conectado.
| Característica | GAP | GATT |
|---|---|---|
| Propósito | Descubrimiento y conexión | Intercambio de datos |
| Roles | Central / Periférico | Cliente / Servidor |
| Protocolo | HCI, Capa de Enlace | ATT (Protocolo de Atributos) |
| Fase | Antes de la conexión | Después de la conexión |
| Clase principal | BluetoothAdapter | BluetoothGatt |
GATT define dos roles: GATT Server y GATT Client. El Servidor es un dispositivo que proporciona datos (por ejemplo, un rastreador de fitness que envía lecturas de frecuencia cardíaca). El Cliente es un dispositivo que solicita datos (por ejemplo, un teléfono inteligente que lee las lecturas). En la mayoría de los escenarios, la aplicación Android actúa como GATT Client y el periférico BLE como GATT Server. Sin embargo, Android también puede ser GATT Server — por ejemplo, cuando una aplicación emula un dispositivo BLE para otros dispositivos. El rol se determina en la fase de configuración de la conexión GATT y no cambia durante la sesión.
El proceso de comunicación GATT comienza después de establecer la conexión BLE entre los dispositivos. Primero, el GATT Client descubre los servicios en el GATT Server mediante Service Discovery — Android lo realiza automáticamente al conectarse a través de BluetoothGatt.discoverServices(). Tras el descubrimiento, el cliente obtiene una lista de servicios, características y descriptores disponibles. Luego, el cliente puede leer valores de características (Read), escribir nuevos valores (Write) o suscribirse a notificaciones (Set Notify/Indicate). El GATT Server puede enviar Notify/Indicate al cliente en cualquier momento después de establecer la conexión.
Por defecto, el tamaño de un paquete GATT (MTU, Maximum Transmission Unit) en BLE es de 23 bytes, de los cuales 3 bytes son el encabezado ATT y 20 bytes son datos útiles. Si la aplicación necesita transmitir más datos (por ejemplo, una actualización OTA de firmware), se puede solicitar un aumento del MTU hasta 517 bytes mediante requestMtu() en Android. Un MTU mayor reduce la cantidad de paquetes necesarios para transmitir un bloque de datos — en lugar de 50 paquetes pequeños, se pueden enviar 2 grandes, lo que reduce el consumo de energía y acelera la transmisión. El MTU máximo depende de la versión de BLE y las capacidades del chip — Bluetooth 5.0 admite hasta 517 bytes, las versiones anteriores hasta 247 bytes.
// Suscribirse a notificaciones de características
fun enableNotifications(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, true)
// Habilitar CCCD (Descriptor de Configuración de Característica de Cliente)
val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val descriptor = characteristic.getDescriptor(cccdUuid)
descriptor?.let {
it.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
gatt.writeDescriptor(it)
}
}
Para recibir notificaciones del GATT Server en Android, es necesario implementar la devolución de llamada BluetoothGattCallback.onCharacteristicChanged(). En este método, la aplicación recibe el valor actualizado de la característica cada vez que el servidor envía Notify o Indicate. Para el procesamiento de datos en tiempo real, use un búfer y corrutinas de Kotlin — esto evita bloquear el hilo de UI durante notificaciones intensivas (por ejemplo, más de 100 lecturas de frecuencia cardíaca por segundo). Para Indicaciones, debe llamar a gatt.sendResponse() en el lado del servidor — en el cliente Android, esto se maneja automáticamente.
En Android, el trabajo con GATT se implementa a través de las clases del paquete android.bluetooth: BluetoothGatt (conexión), BluetoothGattService (servicio), BluetoothGattCharacteristic (característica) y BluetoothGattDescriptor (descriptor). La conexión a un dispositivo BLE comienza llamando a BluetoothDevice.connectGatt() — este método devuelve un BluetoothGatt a través del cual se realizan todas las operaciones posteriores. Todas las devoluciones de llamada GATT llegan a BluetoothGattCallback — esta es una interfaz asíncrona que se invoca en el mismo hilo donde se creó BluetoothGatt. Importante: todas las operaciones GATT deben realizarse secuencialmente — llamar a múltiples operaciones simultáneamente en el mismo BluetoothGatt provoca errores.
La gestión adecuada del ciclo de vida de la conexión GATT es fundamental para la estabilidad de la aplicación BLE. Después de llamar a connectGatt(), la aplicación espera la devolución de llamada onConnectionStateChange() con el estado STATE_CONNECTED. Luego, el sistema inicia automáticamente Service Discovery, tras lo cual se llama a onServicesDiscovered(). Solo después de esto se pueden realizar lecturas, escrituras y suscripciones. Al finalizar, siempre llame a gatt.close() en onDestroy() o onPause() — las conexiones GATT no cerradas consumen batería y pueden bloquear la reconexión al mismo dispositivo en Android.
// Conectarse al dispositivo BLE a través de GATT
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val service = gatt.getService(UUID.fromString("180D"))
val characteristic = service?.getCharacteristic(
UUID.fromString("2A37")
)
characteristic?.let { enableNotifications(gatt, it) }
}
}
Bluetooth SIG (Special Interest Group) define docenas de perfiles y servicios GATT estándar con UUID fijos de 16 bits. Los más comunes: Battery Service (UUID 180F, característica Battery Level 2A19), servicio Heart Rate (UUID 180D, característica Heart Rate Measurement 2A37), servicio Device Information (UUID 180A, características Manufacturer Name y Serial Number). El uso de servicios estándar garantiza la compatibilidad entre dispositivos de diferentes fabricantes — cualquier pulsera de fitness con un servicio Heart Rate debería funcionar con cualquier aplicación Android que lo soporte.
Si los servicios estándar no cubren la tarea, el desarrollador puede crear servicios GATT personalizados con UUID de 128 bits. Al diseñar un servicio personalizado, debe: definir un grupo lógico de características (por ejemplo, “Servicio de Control de Cerradura”), asignar a cada característica las propiedades correctas (Read, Write, Notify), definir valores permitidos y unidades de medida a través del descriptor Characteristic Presentation Format. Para protocolos complejos, se recomienda incluir una característica Command con propiedad Write y una característica Status con Notify — esto sigue el patrón Command/Status adoptado en aplicaciones BLE industriales.
// Crear servicio GATT personalizado (Android como GATT Server)
private fun createCustomService(): BluetoothGattService {
val serviceUuid = UUID.fromString("12345678-1234-5678-1234-56789abcdef0")
val service = BluetoothGattService(
serviceUuid, BluetoothGattService.SERVICE_TYPE_PRIMARY
)
val charUuid = UUID.fromString("12345678-1234-5678-1234-56789abcdef1")
val characteristic = BluetoothGattCharacteristic(
charUuid,
BluetoothGattCharacteristic.PROPERTY_READ or
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ
)
service.addCharacteristic(characteristic)
return service
}
Preguntas frecuentes
GATT (Generic Attribute Profile) es un perfil de Bluetooth Low Energy que define la estructura de intercambio de datos entre dispositivos. Es necesario para estandarizar el acceso a la información: todos los dispositivos BLE organizan los datos en servicios y características, lo que permite a cualquier cliente leer lecturas de sensores, controlar dispositivos y recibir notificaciones.
GAP se encarga del descubrimiento y conexión de dispositivos BLE (escaneo, publicidad, establecimiento de conexión). GATT se encarga del intercambio de datos después de la conexión (lectura, escritura, notificaciones). GAP funciona antes de la conexión, GATT después. Ambos perfiles son obligatorios para BLE, pero realizan funciones diferentes: GAP es la “capa de red”, GATT es la “capa de aplicación.”
Los servicios estándar incluyen Battery Service (180F, nivel de batería), Heart Rate (180D, pulso), Device Information (180A, datos del dispositivo). Cada servicio contiene varias características con UUID de 16 bits. Los desarrolladores pueden crear servicios personalizados con UUID de 128 bits para tareas específicas — como el control de una cerradura inteligente o la transmisión de datos de un rastreador de fitness.
En Android, GATT se implementa a través de BluetoothGatt (conexión), BluetoothGattService (servicio), BluetoothGattCharacteristic (característica) y BluetoothGattDescriptor (descriptor). La conexión se realiza mediante connectGatt(), después de lo cual los eventos llegan a través de BluetoothGattCallback: onConnectionStateChange, onServicesDiscovered, onCharacteristicChanged (para notificaciones). Todas las operaciones GATT deben realizarse secuencialmente.
UUID (Identificador Único Universal) es un identificador de 16 o 128 bits que identifica de forma única un servicio o característica en GATT. Los servicios estándar de Bluetooth SIG utilizan UUID de 16 bits (por ejemplo, 180D para Heart Rate). Los servicios personalizados de desarrolladores utilizan UUID de 128 bits (por ejemplo, 12345678-1234-5678-1234-56789abcdef0). UUID permite al cliente encontrar los datos necesarios en el GATT Server.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también