GATT — qué es, Generic Attribute Profile e implementación en Android

Autor: IT Sectr Publicado: 2026-07-14 Tiempo de lectura: 10 min

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 — un perfil BLE que define la estructura de transferencia de datos a través de servicios, características y descriptores
  • Jerarquía — un servicio contiene características, cada característica tiene un valor con descriptores opcionales
  • UUID — cada servicio y característica se identifica mediante un UUID de 16 bits (estándar) o 128 bits (personalizado)
  • Roles — GATT Server (proporciona datos) y GATT Client (lee/escribe datos)
  • API de Android — BluetoothGatt, BluetoothGattService, BluetoothGattCharacteristic — clases principales para trabajar con GATT

¿Qué es GATT?

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.

El rol de GATT en la pila BLE

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.

Arquitectura GATT: servicios, características y descriptores

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.

Tipos de operaciones con características

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.

kotlin
// 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?
)

GATT y GAP: diferencias entre los perfiles BLE

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ísticaGAPGATT
PropósitoDescubrimiento y conexiónIntercambio de datos
RolesCentral / PeriféricoCliente / Servidor
ProtocoloHCI, Capa de EnlaceATT (Protocolo de Atributos)
FaseAntes de la conexiónDespués de la conexión
Clase principalBluetoothAdapterBluetoothGatt

Roles en GATT

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.

Cómo funciona la comunicación GATT en BLE

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.

MTU — Unidad Máxima de Transmisió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.

kotlin
// 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)
    }
}

Manejo de notificaciones

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.

Implementación de GATT en Android

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.

Ciclo de vida de la conexión GATT

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.

kotlin
// 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) }
    }
}

Perfiles y servicios GATT estándar

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.

Diseño de servicios GATT personalizados

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.

kotlin
// 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

¿Qué es GATT en BLE y por qué es necesario?

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.

¿En qué se diferencia GATT de GAP?

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.”

¿Qué servicios y características GATT existen?

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.

¿Cómo funciona GATT en Android?

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.

¿Qué es un UUID en GATT?

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

  • GATT — un perfil BLE para el intercambio estructurado de datos a través de una jerarquía de servicios, características y descriptores, basado en el Protocolo de Atributos (ATT)
  • Jerarquía de datos — servicio (grupo lógico), característica (valor específico con tipo), descriptor (metadatos — unidades, configuración de notificaciones)
  • Roles GATT — Servidor (proporciona datos, generalmente periférico BLE) y Cliente (lee/escribe, generalmente teléfono Android)
  • Operaciones — Read, Write, Notify (sin confirmación, más rápido) e Indicate (con confirmación, más fiable); la elección depende del escenario
  • API de Android — BluetoothGatt, BluetoothGattCallback, BluetoothGattService, BluetoothGattCharacteristic; llamadas asíncronas secuenciales a través de devoluciones de llamada
  • Perfiles estándar — Battery Service, Heart Rate, Device Information — garantizan la compatibilidad entre dispositivos de diferentes fabricantes
  • Servicios personalizados — UUID de 128 bits para tareas únicas; se diseñan con el patrón Command/Status con propiedades correctas (Read, Write, Notify, Indicate)

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.

Discutir el proyecto

Lea también