Bluetooth 4.0, that includes the Low Energy specification, brings two new protocols to the standard: ATT (Attribute Protocol) and GATT (Generic Attribute Profile). They are mainly targeted at Low Energy mode, and every LE profile is expected to use them. But they can also be used over Classic Bluetooth (BR/EDR).
ATT is a wire application protocol, while GATT dictates how ATT is employed in service composition. Every Low Energy profile must be based on GATT. So, ultimately, every LE service uses ATT as the application protocol.
Binding LE profiles into these protocols brings several advantages:
The last point is very important. It allows for app-level profile support. In BT Classic, supporting a new profile demands a system upgrade (that may never come for lesser-common profiles). There are also devices with proprietary or non-standard profiles. For either reason, many BT Classic devices resorted to SPP (Serial Port Profile) instead of the "correct" profile for their classes.
Now, let's take a deeper look into each protocol.
The sole building block of ATT is the attribute. An attribute is composed by three elements:
From the point of view of ATT, the value is amorphous; it is an array of bytes of any size. The actual meaning of the value is based on the UUID, but ATT does not check whether the value length is compatible with the given UUID.
There may be many attributes with the same UUID in a given device.
ATT itself does not define the meaning of any UUID. This is left to GATT and higher-level profiles.
An ATT server stores attributes. An ATT client queries server attributes. Queries can read from and also write to attributes.
There may be security permissions associated with each attribute. They are stored somewhere inside the value, and are defined by higher-level profiles. ATT itself does not "know" them, and does not try to interpret attribute values to test permissions. This is GATT's (and profile's) problem.
The ATT wire protocol has some nice features, e.g. searching attributes by UUID, getting all attributes given a handle range, etc. The client does not need to know handle numbers beforehand, and higher-level profiles are not bound to particular handle numbers either.
But handles are expected to be stable in every particular device. The client can cache the server layout upon first discovery, using less packets and less energy and later sessions. This layout may change e.g. after a firmware upgrade, so the higher-level profiles should specify a way to hint clients to discard old cached info.
Most of the ATT protocol is purely client-server: client takes the initiative, server answers. But ATT has notification and indication capabilities, in which the server takes the initiative of notifying a client an attribute value has changed. So the clients don't need to poll, which would be a waste of energy.
The wire protocol never sends the length of a value; it is implied from PDU size, and the client is expected to know the value layout for the UUID types it claims to understand.
The MTU of LE packets is only 23 bytes in BLE 4.0 (it seems it was increased in later versions). The small MTU is not a problem at all for large attribute values: ATT offers "read long" and "write long" operations to transfer big attributes in chunks.
ATT is very, very generic. By itself, it would leave too much for the higher-level profiles to define. We need additional structure to make sure services wouldn't clash in a multi-service device. There is a single ATT handlespace per device, so multiple services must share it cooperatively.
Fortunately, we have GATT, which shapes and delimits how the attributes are used.
GATT is a base profile for all top-level LE profiles. It defines how a bunch of ATT attributes are grouped together into meaningful services.
The cornerstone of a GATT service is an attribute with UUID 0x2800. All following attributes (i.e. attributes with handle value higher than the cornerstone) belong to this service, until another attribute 0x2800 is found.
For example, a device with three services might have the following attribute layout:
Handle | UUID | Description |
---|---|---|
0x0100 | 0x2800 | Service A definition |
... | ... | Service details |
0x0150 | 0x2800 | Service B definition |
... | ... | Service details |
0x0300 | 0x2800 | Service C definition |
... | ... | Service details |
An attribute does not "know" by itself to which service it belongs. GATT needs to figure it out based on handle ranges, and ranges are discovered solely on basis of UUID 0x2800 "cornerstones".
Suddenly, the handle value is loaded with additional meaning. In the example, for an attribute to belong to service B, its handle must be between 0x0151 and 0x02ff.
The UUID 0x2800 defines primary services. There are secondary services (UUID 0x2801) meant to be included by primary services.
It is opportune to reinforce the difference between profiles from services. A GATT service is a grouping of ATT attributes that achieves a certain computing task. A profile is a high-level description of a device class. A profile specifies many things beyond the GATT service, for example: communication timeouts, power budget, notifications and advertisements, and so on.
A profile uses one or more services. A given service might be used by different profiles (though most services are indeed employed by a single profile). There are separate spec documents for each profile and each service to forster this kind of reuse.
Ok, let's go back to GATT services. How do I know if a given service is a thermometer, of keyfob, or GPS? By reading its value. The service attribute value contains a UUID: the service UUID.
So, each service definition attribute has actually two UUIDs in its body: 0x2800 or 0x2801 as the attribute UUID, and another UUID stored as the value, which specifies the service.
For example, suppose an hypothetical LE thermometer service has UUID 0x1816. The complete service attribute becomes:
Handle | UUID | Description | Value |
---|---|---|---|
0x0100 | 0x2800 | Thermometer service definition | UUID 0x1816 |
... | ... | Service details | ... |
0x0150 | 0x2800 | Service B definition | 0x18xx |
... | ... | Service details | ... |
0x0300 | 0x2800 | Service C definition | 0x18xx |
... | ... | Service details | ... |
How the ATT client finds the three different services? By using the attributes with UUID 0x2800 as service boundaries (remember the ATT protocol offers retrieval by UUID). Once the 0x2800 attributes are read and their values (which are service UUIDs) are known, the client knows which services are available.
Even if the client does not understand one or more services offered by the device, it won't choke at discovery phase, because up to this point it only used common ATT operations. The client may then dig deeper on services it does understand.
Important note: the thermometer UUID 0x1816 is not official! If you want the official UUIDs and data formats, download and read the specs from Bluetooth site. The official profile for LE thermometers did not exist by the time this article was written.
Each GATT service has a number of characteristics. The characteristics contain the actual "payload" as well as their permissions.
For example, a thermometer service is expected to have at least a "temperature" characteristic (read-only). And possibly a date/time for timestamping (read/write).
Handle | UUID | Description | Value |
---|---|---|---|
0x0100 | 0x2800 | Thermometer service definition | UUID 0x1816 |
0x0101 | 0x2803 | Characteristic: temperature | UUID 0x2A2B Value handle: 0x0102 |
0x0102 | 0x2A2B | Temperature value | 20 degrees |
0x0110 | 0x2803 | Characteristic: date/time | UUID 0x2A08 Value handle: 0x0111 |
0x0111 | 0x2A08 | Date/Time | 1/1/1980 12:00 |
There may be several characteristics per service. In the same fashion of services, each characteristic owns a range of handles, which are discovered by GATT by finding the "cornerstone" characteristic attributes (UUID 0x2803).
The value of an attribute UUID 0x2803 is equal to a handle. That handle points to the characteristic value, which is the "payload". In the example above, the handle 0x0101 (type 0x2803) delimits a characteristic, and it points to handle 0x0102 (type 0x2A2B), which contains the actual temperature value of the thermometer.
Each characteristic has at least two attributes: the cornerstone (0x2803) and the value. There may be additional attributes, and we know which ones belong to the characteristic because their handles fall between cornerstones.
Clients are not obligated to interpret every possible characteristic of a service; but they are required not to choke on characterisics they don't understand. In the context of the example above, suppose that Thermometer Service 2.0 added the timestamp characteristic, The 1.0 clients should still be fine, because they (are expected to) ignore the unexpected new characteristic.
Important note: the thermometer service layout shown in table is completely fictitious. It was put together just to explain the concept of characteristics. If you are going to implement a true thermometer, please grab and read the official spec.
Apart from the characteristic value, there may be other attributes within every characteristic. In GATT lingo, those extra attributes are called descriptors.
For example, we may need to identify the temperature unit of our thermometer, and this may be carried out by a descriptor:
Handle | UUID | Description | Value |
---|---|---|---|
0x0100 | 0x2800 | Thermometer service definition | UUID 0x1816 |
0x0101 | 0x2803 | Characteristic: temperature | UUID 0x2A2B Value handle: 0x0102 |
0x0102 | 0x2A2B | Temperature value | 20 degrees |
0x0104 | 0x2A1F | Descriptor: unit | Celsius |
0x0110 | 0x2803 | Characteristic: date/time | UUID 0x2A08 Value handle: 0x0111 |
0x0111 | 0x2A08 | Date/Time | 1/1/1980 12:00 |
GATT "knows" that handle 0x0104 is a descriptor that belongs to characteristic 0x0101 because
The descriptor value is interpreted accordingly to the attribute UUID. In the example, the descriptor's UUID was 0x2A1F. A client may safely ignore a descriptor whose UUID is unknown, allowing for easy extension and upgrade of services while keeping interoperability with old clientes.
Each service may define its own descriptors, but GATT defines many useful standard descriptors, like
One particularly important standard descriptor is the client characteristic configuration.
This descriptor, whose UUID is 0x2902, has a read/write 16-bit bitmap.
This is not some kind of client-side descriptor. It is server-side like every ATT attribute. But the server is required to store and present a separate instance of this descriptor for each bonded client, and each client can only see its own copy. Hence the name.
The first two bits are already taken by GATT specification. They configure characteristic notification and indication. The remaining bits are currently reserved.
Remember we said ATT has notification capabilities, so the client does not need to poll for updates? By setting CCC, the client tells the server it wants to be notified when the characteristic changes. It can make sense for e.g. a thermometer, and definitively makes sense for HID devices like keyboards.
Let's see the thermometer service layout with CCC included:
Handle | UUID | Description | Value |
---|---|---|---|
0x0100 | 0x2800 | Thermometer service definition | UUID 0x1816 |
0x0101 | 0x2803 | Characteristic: temperature | UUID 0x2A2B Value handle: 0x0102 |
0x0102 | 0x2A2B | Temperature value | 20 degrees |
0x0104 | 0x2A1F | Descriptor: unit | Celsius |
0x0105 | 0x2902 | Client characteristic configuration descriptor | 0x0000 |
0x0110 | 0x2803 | Characteristic: date/time | UUID 0x2A08 Value handle: 0x0111 |
0x0111 | 0x2A08 | Date/Time | 1/1/1980 12:00 |
As usual, GATT knows the CCC belongs to the temperature characteristic because the handle is in the range 0x0102..0x010F. And it knows it is a CCC because of the distinctive UUID (0x2902).
Since GATT uses ATT attributes to describe services, there is no need for a separate service discovery protocol, like the SDP we have in BR/EDR.
GATT works over BR/EDR, but the specification mandates the service discovery must still happen via SDP, even if the service uses GATT for data exchange.
The idea is to segregate LE-only services from dual-mode ones, without having to flag them. If a service can only be found via GATT, it is LE-only. If it can be found via GATT and SDP, it is dual-mode.
If a given profile uses GATT for data exchange and is meant to be dual-mode, it must publish a SDP record which contains the ATT handle range for the service. The service itself is discovered via SDP, and from that moment on, GATT is employed to find the characteristics.
As this text was written, there is no profile that is dual-mode. "Old" profiles are BR/EDR-only and have not been adapted to GATT; and LE profiles in current development are LE-only.
BLE devices are either central or peripheral. A central device is connected to mains or to a big battery, so the energy consumed by the Bluetooth radio is not a concern. Therefore, the radio is turned on all the time. Computers, smartphones, car audio systems, loudspeakers, etc. fall into this category.
On the other hand, a peripheral device has energy constraints (e.g. fed by coin cells or button cells) and device operation is typically intermitent. For example, a body thermometer or blood pressure monitor is used very infrequently, and their batteries are expected to last for years. Therefore, such devices must conserve energy, and Bluetooth should radiate only when strictly necessary.
Notifications and indications are mechanisms that allow the server to send a message to the client. Thanks to them, the client does not have to poll the server for new data.
But the stereotypical GATT server is exactly the "small", peripheral device, like a thermometer. Given the energy restrictions, peripheral LE devices cannot take the initiative of a Bluetooth connection. But then, how the notification can be sent at all?
In BLE, when a server has data to send, it goes into Advertising mode, which sends some radio signal. Each profile specifies the time span and frequency that a device should advertise, balancing energy consumption against the probability of being found, as appropriate for the use cases covered by the profile.
The central device is in "listening mode" all the time. When it listens an advertisement, and the advertising device is known (i.e. it is paired or whitelisted with that central device) it should connect to the peripheral device.
Once the connection is up, the GATT communication can flow and the notification can be delivered. The typical sequence is: 1) server advertises; 2) client connects; 3) server notifies.
At this point, either side can establish a timeout for disconnection if no more notifications are received. The "best" timeout depends on the use case; a service with infrequent notifications and no real-time ambitions may just disconnect immediately to save energy.
The BLE advertisement/reconnection mechanism is very, very fast — a major improvement from classic Bluetooth. So it does not add much latency, and may be used even by HID devices like keyboards and mouses.
As mentioned, the typical GATT server is a peripheral device (and the typical GATT client is a central device), but this is not mandatory. We may have peripheral clients and central servers. In this case, the client would go to Advertising mode when it wants to query the server.