GATT (Generic Attribute Profile) là một hồ sơ Bluetooth Low Energy (BLE) xác định cấu trúc dữ liệu và quy tắc trao đổi thông tin giữa các thiết bị BLE. GATT được xây dựng trên Giao thức Thuộc tính (ATT) và tổ chức dữ liệu theo hệ thống phân cấp: dịch vụ (services), đặc tính (characteristics) và bộ mô tả (descriptors). Theo Bluetooth SIG (2025), hồ sơ GATT được sử dụng trong 98% tất cả các ứng dụng BLE — từ máy theo dõi sức khỏe đến khóa thông minh và cảm biến y tế.
Những điểm chính
GATT (Generic Attribute Profile) là một hồ sơ Bluetooth Low Energy (BLE) xác định cách hai thiết bị BLE trao đổi dữ liệu qua Giao thức Thuộc tính (ATT). GATT chuẩn hóa cấu trúc dữ liệu: tất cả dữ liệu được tổ chức theo hệ thống phân cấp “dịch vụ → đặc tính → bộ mô tả”. Hồ sơ này được giới thiệu trong đặc tả Bluetooth 4.0 (2010) cùng với BLE và từ đó vẫn là cơ chế truyền dữ liệu chính cho các thiết bị Bluetooth tiết kiệm năng lượng. Không giống như Bluetooth cổ điển, nơi dữ liệu được truyền qua cổng nối tiếp (SPP), GATT cung cấp quyền truy cập có cấu trúc vào dữ liệu thông qua các hoạt động đọc, ghi và thông báo.
Ngăn xếp BLE bao gồm nhiều lớp: Lớp Vật lý (radio), Lớp Liên kết (quản lý kết nối), L2CAP (điều khiển kênh logic), ATT (Giao thức Thuộc tính — truy cập thuộc tính) và GATT (hồ sơ dựa trên ATT). GATT là lớp cao nhất mà các nhà phát triển ứng dụng làm việc cùng. ATT bên dưới cung cấp các hoạt động cơ bản: đọc, ghi, thông báo và chỉ thị thuộc tính. GATT thêm ngữ nghĩa: nó xác định dịch vụ và đặc tính là gì, chúng được nhóm như thế nào và quy tắc nào áp dụng khi đọc và ghi chúng. Theo Bluetooth Core Specification 5.4 (2023), GATT hỗ trợ tới 65.535 thuộc tính (dịch vụ + đặc tính + bộ mô tả) trên một thiết bị.
Hệ thống phân cấp GATT bao gồm ba cấp độ. Dịch vụ là một nhóm logic các đặc tính giải quyết một nhiệm vụ (ví dụ: “Dịch vụ Pin” hoặc “Dịch vụ Nhịp tim”). Đặc tính là một đơn vị dữ liệu với một kiểu đã biết: mức pin hiện tại, giá trị cảm biến, trạng thái công tắc. Mỗi đặc tính có một giá trị và một hoặc nhiều Bộ mô tả mô tả siêu dữ liệu: đơn vị đo lường, cài đặt thông báo, phạm vi giá trị. UUID (Định danh duy nhất toàn cầu) xác định duy nhất mỗi dịch vụ và mỗi đặc tính.
GATT hỗ trợ bốn loại hoạt động để tương tác với các đặc tính. Read — máy khách yêu cầu giá trị hiện tại của một đặc tính từ máy chủ. Write — máy khách gửi một giá trị mới đến máy chủ. Notify — máy chủ gửi một giá trị đến máy khách mà không xác nhận (nhanh hơn nhưng kém tin cậy hơn). Indicate — máy chủ gửi một giá trị kèm xác nhận đã nhận (tin cậy hơn nhưng chậm hơn). Nhà phát triển chọn loại dựa trên kịch bản: cho giá trị cảm biến nhịp tim, Notify là đủ; để ghi cấu hình khóa thông minh, cần Write kèm xác nhận.
// Cấu trúc dữ liệu GATT trong mã 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?
)
Đặc tả BLE xác định hai hồ sơ chính: GAP (Generic Access Profile) và GATT (Generic Attribute Profile). GAP chịu trách nhiệm phát hiện thiết bị, thiết lập kết nối và quản lý khả năng hiển thị — nó là “lớp mạng” của BLE. GATT chịu trách nhiệm trao đổi dữ liệu sau khi thiết lập kết nối — “lớp ứng dụng”. Nhà phát triển sử dụng GAP để quét và kết nối với thiết bị, và GATT để đọc, ghi và nhận thông báo từ thiết bị đã kết nối.
| Đặc điểm | GAP | GATT |
|---|---|---|
| Mục đích | Phát hiện và kết nối | Trao đổi dữ liệu |
| Vai trò | Trung tâm / Ngoại vi | Máy khách / Máy chủ |
| Giao thức | HCI, Lớp Liên kết | ATT (Giao thức Thuộc tính) |
| Giai đoạn | Trước khi kết nối | Sau khi kết nối |
| Lớp chính | BluetoothAdapter | BluetoothGatt |
GATT xác định hai vai trò: Máy chủ GATT và Máy khách GATT. Máy chủ là thiết bị cung cấp dữ liệu (ví dụ: máy theo dõi sức khỏe gửi giá trị nhịp tim). Máy khách là thiết bị yêu cầu dữ liệu (ví dụ: điện thoại thông minh đọc giá trị). Trong hầu hết các kịch bản, ứng dụng Android hoạt động như Máy khách GATT và thiết bị ngoại vi BLE hoạt động như Máy chủ GATT. Tuy nhiên, Android cũng có thể là Máy chủ GATT — ví dụ: khi một ứng dụng mô phỏng thiết bị BLE cho các thiết bị khác. Vai trò được xác định ở giai đoạn thiết lập kết nối GATT và không thay đổi trong suốt phiên.
Quá trình giao tiếp GATT bắt đầu sau khi kết nối BLE giữa các thiết bị được thiết lập. Đầu tiên, Máy khách GATT khám phá các dịch vụ trên Máy chủ GATT thông qua Khám phá Dịch vụ — Android thực hiện việc này tự động khi kết nối qua BluetoothGatt.discoverServices(). Sau khi khám phá, máy khách nhận được danh sách các dịch vụ, đặc tính và bộ mô tả có sẵn. Sau đó, máy khách có thể đọc giá trị đặc tính (Read), ghi giá trị mới (Write) hoặc đăng ký nhận thông báo (Set Notify/Indicate). Máy chủ GATT có thể gửi Notify/Indicate đến máy khách bất kỳ lúc nào sau khi kết nối được thiết lập.
Theo mặc định, kích thước của một gói GATT (MTU) trong BLE là 23 byte, trong đó 3 byte là tiêu đề ATT và 20 byte là dữ liệu hữu ích. Nếu ứng dụng cần truyền nhiều dữ liệu hơn (ví dụ: cập nhật chương trình cơ sở OTA), có thể yêu cầu tăng MTU lên tới 517 byte qua requestMtu() trong Android. MTU tăng lên làm giảm số lượng gói cần thiết để truyền một khối dữ liệu — thay vì 50 gói nhỏ, bạn có thể gửi 2 gói lớn, giúp giảm mức tiêu thụ điện năng và tăng tốc độ truyền. MTU tối đa phụ thuộc vào phiên bản BLE và khả năng của chip — Bluetooth 5.0 hỗ trợ tới 517 byte, các phiên bản cũ hơn tới 247 byte.
// Đăng ký nhận thông báo đặc tính
fun enableNotifications(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, true)
// Bật CCCD (Bộ mô tả cấu hình đặc tính máy khách)
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)
}
}
Để nhận thông báo từ Máy chủ GATT trên Android, bạn cần triển khai callback BluetoothGattCallback.onCharacteristicChanged(). Trong phương thức này, ứng dụng nhận được giá trị đặc tính đã cập nhật mỗi khi máy chủ gửi Notify hoặc Indicate. Để xử lý dữ liệu thời gian thực, hãy sử dụng bộ đệm và coroutine Kotlin — điều này ngăn chặn việc chặn luồng UI trong quá trình thông báo cường độ cao (ví dụ: hơn 100 giá trị nhịp tim mỗi giây). Đối với Indicate, bạn phải gọi gatt.sendResponse() ở phía máy chủ — trên máy khách Android, việc này được xử lý tự động.
Trên Android, làm việc với GATT được triển khai thông qua các lớp của gói android.bluetooth: BluetoothGatt (kết nối), BluetoothGattService (dịch vụ), BluetoothGattCharacteristic (đặc tính) và BluetoothGattDescriptor (bộ mô tả). Kết nối đến thiết bị BLE bắt đầu bằng cách gọi BluetoothDevice.connectGatt() — phương thức này trả về một BluetoothGatt qua đó tất cả các hoạt động tiếp theo được thực hiện. Tất cả các callback GATT đến trong BluetoothGattCallback — đây là một giao diện bất đồng bộ được gọi trên cùng luồng nơi BluetoothGatt được tạo. Quan trọng: tất cả các hoạt động GATT phải được thực hiện tuần tự — gọi nhiều hoạt động đồng thời trên cùng một BluetoothGatt sẽ dẫn đến lỗi.
Quản lý đúng vòng đời kết nối GATT là rất quan trọng cho sự ổn định của ứng dụng BLE. Sau khi gọi connectGatt(), ứng dụng chờ callback onConnectionStateChange() với trạng thái STATE_CONNECTED. Sau đó, hệ thống tự động bắt đầu Khám phá Dịch vụ, sau khi hoàn thành, onServicesDiscovered() được gọi. Chỉ sau đó bạn mới có thể thực hiện đọc, ghi và đăng ký. Khi kết thúc, luôn gọi gatt.close() trong onDestroy() hoặc onPause() — các kết nối GATT không đóng sẽ tiêu hao pin và có thể chặn việc kết nối lại với cùng một thiết bị trên Android.
// Kết nối đến thiết bị BLE qua 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 xác định hàng tá hồ sơ và dịch vụ GATT tiêu chuẩn với UUID 16 bit cố định. Phổ biến nhất: Battery Service (UUID 180F, đặc tính Battery Level 2A19), dịch vụ Heart Rate (UUID 180D, đặc tính Heart Rate Measurement 2A37), dịch vụ Device Information (UUID 180A, đặc tính Manufacturer Name và Serial Number). Sử dụng các dịch vụ tiêu chuẩn đảm bảo khả năng tương thích giữa các thiết bị từ các nhà sản xuất khác nhau — bất kỳ vòng đeo sức khỏe nào có dịch vụ Heart Rate đều phải hoạt động với bất kỳ ứng dụng Android nào hỗ trợ nó.
Nếu các dịch vụ tiêu chuẩn không bao phủ nhiệm vụ, nhà phát triển có thể tạo dịch vụ GATT tùy chỉnh với UUID 128 bit. Khi thiết kế một dịch vụ tùy chỉnh, bạn cần: xác định một nhóm logic các đặc tính (ví dụ: “Dịch vụ điều khiển khóa”), gán cho mỗi đặc tính các thuộc tính chính xác (Read, Write, Notify), xác định các giá trị cho phép và đơn vị đo lường thông qua bộ mô tả Characteristic Presentation Format. Đối với các giao thức phức tạp, nên bao gồm một đặc tính Command với thuộc tính Write và một đặc tính Status với Notify — điều này tuân theo mẫu Command/Status được áp dụng trong các ứng dụng BLE công nghiệp.
// Tạo dịch vụ GATT tùy chỉnh (Android như Máy chủ GATT)
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
}
Câu hỏi thường gặp
GATT (Generic Attribute Profile) là một hồ sơ Bluetooth Low Energy xác định cấu trúc trao đổi dữ liệu giữa các thiết bị. Nó cần thiết để chuẩn hóa quyền truy cập thông tin: tất cả các thiết bị BLE tổ chức dữ liệu thành các dịch vụ và đặc tính, cho phép bất kỳ máy khách nào đọc giá trị cảm biến, điều khiển thiết bị và nhận thông báo.
GAP chịu trách nhiệm phát hiện và kết nối thiết bị BLE (quét, quảng bá, thiết lập kết nối). GATT chịu trách nhiệm trao đổi dữ liệu sau khi kết nối (đọc, ghi, thông báo). GAP hoạt động trước khi kết nối, GATT sau đó. Cả hai hồ sơ đều bắt buộc cho BLE, nhưng thực hiện các chức năng khác nhau: GAP là “lớp mạng”, GATT là “lớp ứng dụng”.
Các dịch vụ tiêu chuẩn bao gồm Battery Service (180F, mức pin), Heart Rate (180D, nhịp tim), Device Information (180A, dữ liệu thiết bị). Mỗi dịch vụ chứa nhiều đặc tính với UUID 16 bit. Nhà phát triển có thể tạo các dịch vụ tùy chỉnh với UUID 128 bit cho các tác vụ cụ thể — như điều khiển khóa thông minh hoặc truyền dữ liệu máy theo dõi sức khỏe.
Trên Android, GATT được triển khai qua BluetoothGatt (kết nối), BluetoothGattService (dịch vụ), BluetoothGattCharacteristic (đặc tính) và BluetoothGattDescriptor (bộ mô tả). Kết nối được thực hiện qua connectGatt(), sau đó các sự kiện đến qua BluetoothGattCallback: onConnectionStateChange, onServicesDiscovered, onCharacteristicChanged (cho thông báo). Tất cả các hoạt động GATT phải được thực hiện tuần tự.
UUID (Định danh duy nhất toàn cầu) là một định danh 16 bit hoặc 128 bit xác định duy nhất một dịch vụ hoặc đặc tính trong GATT. Các dịch vụ tiêu chuẩn Bluetooth SIG sử dụng UUID 16 bit (ví dụ: 180D cho Heart Rate). Các dịch vụ tùy chỉnh của nhà phát triển sử dụng UUID 128 bit (ví dụ: 12345678-1234-5678-1234-56789abcdef0). UUID cho phép máy khách tìm dữ liệu cần thiết trên Máy chủ GATT.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm