As discussed in previous topics, the ATT layer defines attributes and how data is exposed between a client and a server. As such, one of the main functions of GATT is the hierarchal structuring of attributes stored in a GATT server into standardized entities (services and characteristics) providing seamless interoperability between different Bluetooth LE devices.
The ATT layer defines how data is stored and accessed in a server’s database. The data is stored in the form of data structures called Attributes. Attributes are the core data units that both the ATT and GATT layers are based on. Attributes hold user data as well as metadata describing the attribute itself, its type, security permissions, etc. Data exchange occurring between ATT servers and clients, or, GATT servers and clients, is in the form of attributes. When discussing only attributes, they are said to be stored in an ATT server. Whereas, as we will see further in this lesson, when we start classifying attributes into services and characteristics, we refer to that data structure as a GATT server.
An attribute consists of 4 blocks of data:
A UUID is an abbreviation you will see a lot in the Bluetooth LE world. It is a unique number used to identify attributes and tells us about their significance. UUIDs have two types.
The first type is the SIG-defined 16-bit UUID. For example, the SIG-defined Heart rate service has the UUID 0x180D and one of its enclosed characteristics, the Heart Rate Measurement characteristic, has the UUID 0x2A37. The 16-bit UUID is energy and memory efficient, but since it only provides a relatively limited number of unique IDs, there is a need for more UUID to cover all vendors, users, and use cases.
The second type is a 128-bit UUID, sometimes referred to as a vendor-specific UUID. This is the type of UUID you need to use when you are making your own custom services and characteristics. It looks something like this: 4A98-xxxx-1CC4-E7C1-C757-F1267DD021E8 and is called the “base UUID”. The four x’s represent a field where you will insert your own 16-bit IDs for your custom services and characteristics and use them just like a predefined UUID. This way you can store the base UUID once in memory, forget about it, and work with 16-bit IDs as normal.
Let’s start by examining what constitutes a service and how attributes are hierarchically structured in a given service. As shown in the below figure, attributes are the main building blocks for services. A service definition (commonly referred to as a service) is comprised of multiple attributes arranged in a GATT-specified format which facilitates standardized data exchange between Bluetooth LE devices.
Service definitions always start with a service declaration attribute. This attribute holds metadata about the service, it also indicates the beginning of a service in the sequence of services stored on a GATT server.
The Handle is similar to a row number by which the attribute is addressed. The service declaration attribute’s Type field holds the UUID (0x2800
) which is a unique SIG-defined UUID used only to indicate the beginning of a service.
The Permissions field here indicates “read only” and no authentication needed. This is expected in a service declaration attribute as there is no reason to have a write-permission for it, it only declares the beginning of a service.
Lastly, the Value field holds the UUID of the service being declared. For example, the Heart Rate Service is a SIG-defined service and is referred to by the UUID 0x180D
which is stored in the Value field of the Heart Rate Service-Service declaration attribute.
Subsequently, a service can have zero or more characteristic definitions (commonly referred to as characteristics). A characteristic is comprised of at least two attributes and optionally more.
Similar to a service definition, a characteristic definition starts with a declaration attribute, to indicate the beginning of a characteristic in the sequence of characteristics in a service definition. This is followed by the characteristic value attribute which holds the actual user data. Optionally, a characteristic can also have one or more characteristic descriptor attributes.
A characteristic definition starts with a characteristic declaration attribute, to indicate the beginning of a characteristic in the sequence of characteristics in a service definition. The characteristic declaration attribute’s Type field holds the UUID (0x2803
) used only to declare a characteristic. The declaration attribute has read-only Permissions, ensuring that clients can read the value but not write to it.
The Value field holds important information about the characteristic being declared, specifically three separate fields:
After the attribute declaring the characteristic comes the characteristic value attribute. This is where the actual user data is stored. Its Handle and Type are the ones referred to in the Characteristic Declaration Attribute Value field. Naturally, its Value field is where the actual user data is stored. The Permissions field indicates whether the client can read and/or write to this attribute.
The characteristic descriptor attributes are optional. They hold additional metadata about the characteristic, giving the client more information about the nature of the characteristic. There are several kinds of descriptors, but they are generally divided into two categories, GATT-defined and custom.
Descriptors also allow the client to set permissions for certain server-initiated GATT operations. In this course, we will focus on the the GATT-defined Client Characteristic Configuration Descriptor (CCCD) as it is the most commonly used.
The Client characteristic configuration descriptor (CCCD) is a specific type of characteristic descriptor that is necessary when the characteristic supports server-initiated operations (i.e Notify and Indicate). This is a writable descriptor that allows the GATT client to enable and disable notifications or indications for that characteristic. The GATT client can subscribe to the characteristic that it wishes to receive updates about, by enabling either Indications or Notifications in the CCCD of that specific characteristic.
For example, in the Heart Rate Service, there is a characteristic called the Heart Rate Measurement. The GATT client (your mobile phone for instance) can use the CCCD of this characteristic to receive updates about this characteristic. So it subscribes to the Heart Rate Measurement characteristic by enabling either Indications or Notifications in the CCCD of said characteristic. This means the GATT server (most likely a heart rate sensor device) will push these measurements to your phone, without your phone having to poll for these measurements.
The CCCD attribute’s format is as pictured below. The UUID for CCCDs is 0x2902
. A CCCD must always be readable and writable. Descriptors with the Type CCCD only have 2 bits in their Value field. The first bit signals whether Notifications are enabled, and the second bit is for Indications.
We will take a closer look at how to do this in exercise 2 of this lesson.