
BLE under the Microscope (ATTs and GATTs...)
Part 1, Overview
It has been quite some time since the first specification for Bluetooth 4.0 was released. And while the topic of BLE is very interesting, it still deters many developers due to its complexity. In my previous articles, I mainly focused on the lowest levels: Link Layer and Physical Layer. This allowed me to avoid delving into complex and convoluted concepts such as the Attribute Protocol (ATT) and the Generic Attribute Profile (GATT). However, there’s no escaping it; without understanding these concepts, developing compatible devices is impossible. Today, I would like to share this knowledge with you. In my article, I will refer to the beginner's guide from Nordic. So, let's get started.
Why is it all so complicated?
In my opinion, it was immediately clear that controlling devices via smartphones is a very promising and long-term topic. Therefore, they decided to structure it right from the start as comprehensively as possible. This is to prevent manufacturers of various gadgets from inventing their own protocols that would later be incompatible. Hence, the complexity. Even in the first stage, the BLE protocol attempted to squeeze in everything that was possible. And it doesn't matter whether it will be useful later or not. Furthermore, provisions were made for future device list expansion.
Let's take a look at the diagram illustrating the BLE protocol. It consists of several layers. The lowest, physical layer (PHY) is responsible for the device's radio channel. The Link Layer (LL) contains the entire sequence of bytes in the transmitted message. In previous articles, we studied this layer specifically. The Host Controller Interface (HCI) is the protocol for communication between the layers or BLE chips if the Controller and Host are implemented on different chips. The Logical Link Control and Adaptation Protocol (L2CAP) is responsible for packet formation, fragmentation, error control, and packet assembly. The Security Manager Protocol (SMP) is in charge of encrypting packets. The Generic Access Profile (GAP) is responsible for the initial data exchange between devices to determine 'Who is who'. It also includes scanning and advertising. In this article, I will focus on the two remaining parts of the protocol — GATT and ATT. GATT is built on top of ATT, so they are closely intertwined.

For the sake of simplicity in storytelling, I would like to refer to an analogy. I heard it somewhere and would like to support it. Imagine a BLE device as a bookshelf with several shelves. Each shelf represents a different theme. For example, we have shelves for fiction, mathematics, and encyclopedias. On each shelf, there are books covering the specified topic. Some of these books even have paper bookmarks with notes. Additionally, we have a small paper catalog of all the books. If you remember school libraries, it's like a narrow drawer with paper cards. In this analogy, the bookshelf is the profile of our device. The shelves are the services, the books are the characteristics, and the catalog is the attribute table. The bookmarks in the books are the descriptors, which I will also discuss later in more detail.
Anyone who has developed devices knows that many projects have similar pieces of code. The fact is that many devices have similar functionalities. For example, if devices run on batteries, the problem of charging and monitoring their levels will be the same. The same applies to sensors. In fact, the object-oriented approach in programming provides the opportunity to create objects that connect properties and behaviors into a self-contained union that can then be reused multiple times.In my opinion, a similar approach has been attempted in BLE. The Bluetooth Special Interest Group (SIG) developed profiles. Devices from different manufacturers that have the same profiles should easily work with each other. Profiles, in turn, consist of services, and services consist of characteristics complemented by descriptors. Generally, this might look like:

For example, let's consider the profile of a heart rate monitor (fitness tracker). It consists of two services and several characteristics. This immediately clarifies the hierarchy of the profile. The characteristic of the control point resets the overall calorie expenditure count to zero.
1. The heart rate service includes three characteristics (0x180D):
a) Mandatory characteristic of heart rate (0x2A37)
b) Optional characteristic of body sensor location (0x2A38)
c) Conditional characteristic of heart rate control point (0x2A39)
2. Battery service (0x180F):
a) Obligatorisk batteriniveauegenskab (0x2A19)
UUID
For at vi entydigt kan referere til profilens elementer (tjenester, egenskaber og beskrivelser) er det nødvendigt at nummerere dem. Til dette formål introduceres et begreb kaldet Universally Unique ID (UUID) eller Universel unik identifikator. Den nødvendige UUID er angivet i parentes i hver linje. Der er en særlig egenskab ved dette. For UUID besluttede man at bruge en kode med en længde på 16 og 128 bit. Hvorfor, spørger du? I BLE-protokollen er alt underordnet energibesparelse. Derfor er dimensionen på 16 bit ganske fornuftig. Det er usandsynligt, at der i den nærmeste fremtid vil blive skabt mere end 65.000 unikke tjenester og egenskaber. Lige nu har vi allerede talt alt, hvad vi kunne (husker du hvor det kommer fra — "han har også talt dig" :-)) Nummererede elementer , , og du kan se det via links.
Men jeg tror, at alle husker historien om de 4 byte IP-adresser på internettet. Først troede man, det var tilstrækkeligt, men nu kan vi ikke komme videre til 6-byte adresser. For ikke at gentage denne fejl og give plads til nysgerrige hænder fra opfindere, besluttede SIG straks at introducere 128-bit UUID'er. Det minder mig personligt om det ikke-licensekrævende bånd på 433 MHz, som blev givet til diverse opfindere inden for radiokanalen. I vores tilfælde blev det 128-bit identifikator for tjenester og egenskaber givet videre. Det betyder, at vi for vores tjenester og enheder kan bruge praktisk talt enhver 128-bit værdi. Sandsynligheden for at finde en identisk UUID nærmer sig nul.
Faktisk har de korte 16-bit UUID'er deres udvidelse til en 128-bit værdi. I specifikationen kaldes denne udvidelse Bluetooth Base UUID og har værdien 00000000-0000-1000-8000-00805F9B34FB. Hvis en 16-bit UUID for en attribut har værdien 0x1234, så vil den ækvivalente 128-bit UUID have værdien 00001234-0000-1000-8000-00805F9B34FB. Der gives endda den tilsvarende formel:
128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID
Hvor dette magiske tal kommer fra, ved jeg ikke. Hvis nogen af læserne ved det — så skriv gerne i kommentarerne (Brugeren med navnet Sinopteek har allerede gjort det. Se kommentarerne). Hvad angår generering af 128-bit UUID'er, kan man i princippet bruge en speciel , som vil gøre det for dig.
ATT'er GATT'er…
Her begynder det egentlige interessante. Jeg minder om, at ATT er baseret på et klient-server-forhold. Nu ser vi på serverens enhed. Den indeholder information som sensorværdier, status for lyskontakten, placeringsdata osv. Nu, hvor alle "deltagerne i vores parade" er nummereret, skal de placeres i enhedens hukommelse. For dette placerer vi dem i en tabel, der kaldes attributtabel. Husk dette godt. Det er hjertet af BLE. Det er det, vi vil undersøge nærmere. Nu vil vi kalde hver række en attribut. Denne tabel ligger dybt i stakken, og som regel har vi ikke direkte adgang til den. Vi initialiserer den og tilgår den, men hvad der sker inden i, er skjult for os bag syv segl.
Lad os se på billedet fra specifikationen, men før det vil jeg gerne henlede opmærksomheden på den hyppige forvirring i terminologien, især med hensyn til deskriptorer. Deskriptorens rolle er at supplere beskrivelsen af en egenskab. Når det er nødvendigt at udvide dens funktioner, anvendes deskriptorer. De er også attributter og placeres ligesom tjenester og egenskaber i attributtabellen. Vi vil gå grundigt igennem dem i del to af artiklen. Men nogle gange kaldes deskriptorer også linjenummeret i attributtabellen. Det er vigtigt at have in mente. For at undgå forvirring vil vi bruge termen "attributpeger" til disse formål.

Så attribut er en diskret værdi, der har følgende tilknyttede egenskaber:
1. Attributpeger (Attribute Handle) – det er indekset for tabellen, der svarer til attributten.
2. Attributtype (Attribute Type) – det er UUID'en, som beskriver dens type.
3. Attributværdien (Attribute Value) – det er dataene, der indekseres af attributpegeren.
4. Attributtilladelser (Attribute Permissions) – det er en del af attributten, tilladelser, der ikke kan læses eller skrives ved hjælp af attributprotokollen.
Hvordan forstår man alt dette? Attributpegeren er, så at sige, dens nummer i vores tabel.
Det giver kunden mulighed for at referere til attributten i læse- eller skriveanmodninger. Vi kan nummerere vores rækker (attributter) fra 0x0001 til 0xFFFF. I vores association med reolen er dette nummeret på kortet i det fysiske katalog. Tilsvarende, som i et biblioteks katalog, er kortene placeret i stigende rækkefølge efter nummer. Nummeret på hver efterfølgende række skal være større end det forrige. Ligesom i biblioteket, kan nogle kort gå tabt, og således kan der være huller i nummereringen af rækker. Dette er tilladt. Det vigtigste er, at de følger en stigende rækkefølge.
Attributtypen bestemmer, hvad den pågældende attribut repræsenterer. På samme måde som i C-sprog,
hvor der findes boolske, numeriske variabler og strenge, her er det det samme. Gennem attributtypen kan vi finde ud af,
hvad vi har med at gøre, og hvordan vi skal arbejde med denne attribut fremad. Nedenfor vil vi se på nogle specifikke typer attributter. For eksempel "serviceerklæring" (0x2800), "karakteristikserklæring" (0x2803), "descriptor-erklæring" (0x2902).
Attributværdien er i grunden dens værdi, undskyld for tautologien. Hvis attributtypen er en streng, kan værdien f.eks. være sloganet "Hello World !!!". Hvis attributtypen er "serviceerklæring", er værdien den selve service. Og nogle gange er det information om, hvor man kan finde andre attributter og deres egenskaber.
Attributtilladelser giver serveren mulighed for at forstå, om læse- eller skriverettigheder er tilladt.
Bemærk, at disse tilladelser kun gælder for attributværdien, og ikke for pegeren, typen og selve tilladelsesfeltet. Det vil sige, hvis skrivning af attributten er tilladt, kan vi ændre for eksempel rækken "Hello World !!!" til rækken "Good morning". Men vi kan ikke nægte at skrive en ny række eller ændre attributtypen og angive rækken som "serviceerklæring". Når klienten henvender sig til serveren, anmoder klienten om dens attributter. Dette gør det muligt for klienten at finde ud af, hvad serveren kan tilbyde. Selvom det ikke nødvendigvis er nødvendigt at læse og skrive værdier.
How it looks
GATT-konceptet handler om at gruppere attributter i en attributtabel sammen på en meget specifik og logisk måde. Lad os tage et tættere kig på profilen for hjertefrekvensen, som er angivet nedenfor. Den venstre kolonne i denne tabel er valgfri. Den beskriver blot, hvad denne række (attribut) er. Alle de andre kolonner er allerede kendte for os.

Øverst i hver gruppe har vi altid attributten for serviceannonceret. Dens type er altid lig med 0x2800, og pegepinden afhænger af, hvor mange attributter der allerede er til stede i tabellen. Dens tilladelser er altid kun tilgængelige for læsning, uden nogen form for godkendelse eller autorisation. Vi vil tale mere om disse begreber senere. Værdien er endnu en UUID, der definerer, hvilken service det er. I tabellen er værdien lig med 0x180D, som er defineret af Bluetooth SIG som en hjertefrekvensservice.
Efter serviceannonceret følger der en karakteristikannonce. I formen ligner den serviceannoncen. Dens UUID har altid værdien 0x2803, og tilladelserne er også altid kun tilgængelige for læsning uden nogen form for godkendelse eller autorisation. Lad os tage et kig på feltet Attributværdi, som inkluderer nogle data. Det indeholder altid en pegepind, UUID og et sæt egenskaber. Disse tre elementer beskriver den efterfølgende annoncering af karakteristikværdien. Pegepinden angiver naturligvis placeringen af karakteristikværdien i attributtabellen. UUID'en beskriver, hvilken type information eller værdi vi kan forvente. For eksempel temperaturværdi, tilstand af en lyskontakt eller en hvilken som helst anden vilkårlig værdi. Og endelig egenskaberne, som beskriver, hvordan man kan interagere med karakteristikværdien.
Her venter der os endnu en fælde. Den er relateret til attributtilladelser og karakteristikegenskaber. Lad os tage et kig på billedet af bitfeltets egenskaber fra specifikationen.

Som du kan se, er der også felter, der giver læse- og skrivemuligheder. Du kunne undre dig over, hvorfor vi har læsetilladelser/skrivning for attributten og egenskaben.
Læsninger/skrivninger for karakteristikværdi? Skal de ikke altid være ens? Sandheden er, at egenskaberne for karakteristikværdien blot er anbefalinger til kunden, der anvendes i GATT og applikationslagene. Det er simpelthen forslag om, hvad kunden kan forvente af attributten for karakteristikken. Lad os dykke lidt dybere ned i dette. Hvilke typer tilladelser findes der for attributten?
1. Adgangstilladelser:
— læsning
— skrivning
— læsning og skrivning
2. Autentifikationstilladelser:
— autentifikation påkrævet
— autentifikation ikke påkrævet
3. Autorisationstilladelser:
— autorisation påkrævet
— autorisation ikke påkrævet
Den primære forskel mellem tilladelser for attributter og egenskaber for karakteristikker er, at de førstnævnte relaterer til servere, mens de sidstnævnte relaterer sig til kunder. En server kan have tilladelse til at læse karakteristikværdien, men samtidig have krav om autentifikation eller autorisation. Derfor, når en kunde anmoder om egenskaberne for karakteristikken, vil vi få, at læsning er tilladt. Men ved forsøg på at læse vil vi få en fejl. Så det kan med sikkerhed siges, at tilladelserne har forrang over egenskaberne. Vi, fra kundens side, kan ikke få viden om, hvilke tilladelser der er for attributten.
Descriptor
Lad os vende tilbage til vores tabel. Efter erklæringen af karakteristikværdien er følgende erklæringer for attributter mulige:
1. Ny karakteristikserklæring (der kan være mange karakteristika i tjenesten)
2. Ny tjenesteerklæring (der kan være mange i tabellen)
3. Erklæring om descriptor
Når det kommer til målingen af puls, ledsages offentliggørelsen af måleværdien af en beskrivelse. Beskrivelsen er en attribut med yderligere information om målingen. Der findes flere typer af beskrivelser. Det vil vi tale nærmere om i anden del af denne artikel. Men nu vil vi kun berøre den beskrivelse, der relaterer til kundeegenskaber (Client Characteristic Configuration Descriptor – CCCD). Den har et UUID der svarer til 0x2902. Ved hjælp af denne beskrivelse kan kunden aktivere serverens indikation eller notifikation. Forskellen mellem dem er lille, men eksisterer. Notifikationen kræver ikke bekræftelse fra kunden, mens indikation kræver dette, selvom det foregår på GATT-niveauet, uden at det når op til applikationsniveauet. Hvorfor er det sådan, spørger du? Desværre er jeg ikke klar over det. Jeg kan kun sige, at specialister fra Nordic anbefaler at bruge notifikation. Især fordi integritetstjekket (via CRC) udføres i begge tilfælde.
Konklusion
Til slut i artiklen vil jeg gerne sige dette. Den sidste tabel er lidt forvirrende. Men jeg har valgt den, fordi den er nævnt i , som jeg baserer mig på. I anden del af min artikel planlægger jeg at dykke ned i specifikationen for Bluetooth 4.0. Her vil vi finde mere korrekte diagrammer og illustrationer. I tredje del vil jeg gerne gennemgå loggen opnået med programmet Wireshark fra en af gadgets og se "live" den teori, vi studerer.
Medarbejder i Gruppefirmaet
Pechersky Vladimir
Kilde: habr.com
