11.6. Servicii și caracteristici

Odată ce GAP a adus două dispozitive într-o conexiune deschisă, stratul aflat deasupra sa – Generic Attribute Profile, GATT – trebuie să dea un sens octeților care circulă prin acea conexiune. Alegerea făcută de BLE aici este neobișnuită. Acolo unde TCP expune un flux brut de octeți și lasă aplicația să-și inventeze propria încadrare, GATT expune o mică bază de date cheie/valoare pe care o parte o găzduiește, iar cealaltă o citește, o scrie sau se abonează la ea.

Acea bază de date este lucrul la care proiectanții de aplicații își petrec cea mai mare parte a timpului dedicat BLE. Ceea ce camera publică spre un telefon, ceea ce urmărește pe un senzor la distanță, modul în care o tastatură Bluetooth îi spune gazdei ce tastă a fost apăsată – toate sunt valori de caracteristici dintr-o bază de date GATT undeva.

11.6.1. Două axe de roluri, nu una

O sursă frecventă de confuzie: peripheral / central și server / client sunt două axe independente, nu sinonime.

  • Peripheral și central sunt roluri GAP, stabilite la momentul conexiunii. Peripheral-ul emite anunțuri și este conectat la el; central-ul scanează și inițiază conexiunea. Acest lucru se fixează în momentul în care legătura se stabilește și nu se schimbă.

  • Server și client sunt roluri GATT, stabilite per operație de caracteristică. Server-ul găzduiește caracteristica; client-ul o citește, o scrie sau se abonează la ea.

Cele două axe sunt decuplate de specificație. Un peripheral este de obicei server-ul (o bandă de măsurare a ritmului cardiac își publică citirile), iar un central este de obicei client-ul (un telefon le citește), însă BLE permite orice combinație – un peripheral poate descoperi o caracteristică pe central-ul la care tocmai s-a conectat, ori o singură conexiune poate găzdui servicii pe ambele părți deodată.

Majoritatea aplicațiilor de cameră rămân la asocierea convențională (peripheral + server, sau central + client), așa că restul acestei secțiuni le tratează ca pe o singură axă atunci când cazul convențional este cel descris. Când distincția contează, ambii termeni sunt menționați explicit.

11.6.2. În interiorul bazei de date

O bază de date GATT este un arbore. Frunzele poartă octeții propriu-ziși. Ramurile grupează frunzele înrudite în unități cu semnificație pentru om.

Un arbore cu un nod superior etichetat "GATT database". Sub el, trei noduri de tip Service etichetate "Generic Access (0x1800)", "Battery (0x180F)" și "Environmental Sensing (0x181A)". Fiecare Service are noduri-copil de tip Characteristic; serviciul Battery are "Battery Level (0x2A19)" cu un Descriptor-copil "CCCD". Serviciul Environmental Sensing are "Temperature (0x2A6E)" și "Humidity (0x2A6F)".

O bază de date GATT. Serviciile grupează caracteristici; caracteristicile poartă octeții aplicației; descriptorii poartă metadate despre caracteristică.

Există trei tipuri de noduri:

  • Un service este un grup logic de valori înrudite. Bluetooth SIG publică definiții standard de servicii pentru cazuri de utilizare comune – Battery Service pentru nivelul bateriei, Environmental Sensing pentru temperatură / umiditate / presiune, Heart Rate pentru monitoarele de ritm cardiac – astfel încât o aplicație generică de pe un telefon poate recunoaște un serviciu pe care nu l-a mai văzut niciodată. O aplicație este de asemenea liberă să-și definească propriile servicii pentru lucruri pe care SIG nu le-a standardizat.

  • O characteristic este o valoare numită din interiorul unui serviciu. Serviciul Battery are o singură caracteristică – Battery Level, un procentaj de un octet. Environmental Sensing are caracteristici separate pentru temperatură, umiditate, presiune și așa mai departe. O caracteristică este unitatea operațiilor GATT – citești o caracteristică, scrii o caracteristică, te abonezi la o caracteristică.

  • Un descriptor este metadată atașată unei caracteristici. Unii descriptori sunt standardizați – Client Characteristic Configuration Descriptor (CCCD) este cel celebru, deoarece scrierea în el este modul în care un client îi spune server-ului „trimite-mi notificări pe această caracteristică”. Alții sunt definiți de utilizator și poartă lucruri precum formatul de prezentare sau proprietăți extinse.

Un server GATT (de regulă peripheral-ul) își declară baza de date o singură dată la pornire, iar baza de date nu se schimbă în timpul rulării. Un client GATT (de regulă central-ul) descoperă ce se află în baza de date după conectare – parcurgând arborele, citind UUID-urile serviciilor pe care le găsește, apoi caracteristicile din fiecare.

11.6.3. UUID-uri

Fiecare serviciu, caracteristică și descriptor are un UUID (Universally Unique IDentifier) care identifică ce fel de lucru este. UUID-urile vin în trei lățimi:

  • 16 biți. Rezervate pentru standardele definite de Bluetooth SIG. Battery Service este 0x180F. Battery Level (o caracteristică) este 0x2A19. Lista completă este publicată pe site-ul de numere atribuite al Bluetooth SIG, la https://www.bluetooth.com/specifications/assigned-numbers/.

  • 32 de biți. Un teren intermediar rar folosit.

  • 128 de biți. Ceea ce folosesc toți ceilalți – un furnizor sau o aplicație generează unul la întâmplare și îl folosește pentru serviciul ori caracteristica personalizată. Camerele care își definesc propriul protocol se află aici.

Clasa bluetooth.UUID acceptă oricare dintre cele trei lățimi:

import bluetooth

BATTERY_SERVICE = bluetooth.UUID(0x180F)
CUSTOM_SERVICE = bluetooth.UUID("12345678-1234-5678-9abc-def012345678")

Un UUID pe 16 biți se codifică într-o încărcătură de anunț mică, ceea ce este unul dintre motivele pentru care serviciile standard sunt de preferat atunci când există unul – o bandă de ritm cardiac care anunță 0x180D (Heart Rate) costă doi octeți; un UUID personalizat costă șaisprezece. Pentru aplicațiile care nu au nevoie de interoperabilitate standard, un UUID pe 128 de biți generat este răspunsul corect.

11.6.4. Ce îți oferă serviciile standardizate de SIG

Argumentul pentru folosirea unui serviciu standard este simplu: aplicațiile existente știu deja cum să comunice cu el. Un dispozitiv care anunță serviciul Heart Rate (0x180D) și expune caracteristica Heart Rate Measurement (0x2A37) funcționează cu fiecare aplicație de fitness de pe planetă fără ca cineva să scrie cod nou. Un dispozitiv care reimplementează aceleași date cu UUID-uri personalizate are nevoie de propria aplicație companion și de propriul document de protocol.

Standardele vin totuși cu un cost. Aranjamentele de octeți din interiorul fiecărei caracteristici sunt specificate – SIG a decis că Heart Rate Measurement este un câmp de indicatori de un octet urmat fie de o valoare a ritmului cardiac pe 8 biți, fie pe 16 biți, urmată opțional de intervale R-R – iar un dispozitiv conform trebuie să respecte aceste aranjamente. Serviciile personalizate sunt libere de această constrângere.

Răspunsul pragmatic pentru camere: folosește serviciul standard atunci când există unul pentru tipul de date pe care îl ai (Battery Service, Environmental Sensing) și definește unul personalizat cu un UUID pe 128 de biți pentru orice este specific aplicației tale.

11.6.5. Obiecte pe partea de server și pe partea de client

Pentru aceleași blocuri conceptuale (serviciu, caracteristică, descriptor), fiecare bibliotecă GATT expune două seturi paralele de obiecte: