
BLE sub microscop (ATTы GATTы...)
Partea 1, prezentare generală
A trecut deja destul de mult timp de când a fost lansată prima specificație pentru Bluetooth 4.0. Și, deși subiectul BLE este foarte interesant, tot amână mulți dezvoltatori din cauza complexității sale. În articolele mele anterioare, m-am uitat în principal la cel mai de jos nivel, Stratul de legătură și Stratul fizic. Acest lucru ne-a permis să evităm să recurgem la concepte atât de complexe și confuze precum Protocolul de atribut (ATT) și Profilul de atribut general (GATT). Cu toate acestea, nu există încotro, fără a le înțelege, este imposibil să dezvolte dispozitive compatibile. Astăzi aș dori să vă împărtășesc aceste cunoștințe. În articolul meu mă voi baza pentru începători de pe site-ul nordic. Asadar, haideti sa începem.
De ce este totul atât de dificil?
În opinia mea, a fost imediat clar că gestionarea dispozitivelor prin intermediul smartphone-urilor este un subiect foarte promițător și de lungă durată. Prin urmare, au decis să o structureze imediat și la maximum. Pentru ca producătorii de diverse gadget-uri să nu vină cu propriile protocoale, care vor fi apoi incompatibile. De aici și dificultatea. Deja în prima etapă, ei au încercat să stoarce tot ceea ce era posibil în protocolul BLE. Și nu contează dacă va fi util mai târziu sau nu. În plus, au prevăzut posibilitatea extinderii listei de dispozitive pentru viitor.
Să aruncăm o privire la imaginea în care este desenată diagrama protocolului BLE. Este format din mai multe straturi. Cel mai de jos, stratul fizic (PHY) este responsabil pentru canalul radio al dispozitivului. Link Layer(LL) conține întreaga secvență de octeți din mesajul transmis. În articolele anterioare am studiat exact acest lucru. Interfața controlerului gazdă (HCI) este un protocol de schimb între straturi sau cipuri BLE dacă controlerul și gazda sunt implementate pe cipuri diferite. Logical Link Control and Adaptation Protocol (L2CAP) este responsabil pentru formarea pachetelor, încadrarea, controlul erorilor și asamblarea pachetelor. Security Manager Protocol (SMP) este responsabil pentru criptarea pachetelor. Profilul de acces general (GAP) este responsabil pentru schimbul inițial de date între dispozitive pentru a determina „Cine este cine”. Include, de asemenea, scanare și publicitate. În acest articol mă voi concentra asupra celor două părți rămase ale protocolului - GATT și ATT. GATT este o suprastructură a ATT, deci sunt strâns legate între ele.

Pentru a simplifica povestea, aș dori să apelez la o analogie. Am auzit-o undeva și aș vrea să o susțin. Gândiți-vă la un dispozitiv BLE ca la o bibliotecă cu mai multe rafturi. Fiecare raft este o temă separată. De exemplu, avem rafturi cu science fiction, matematică și enciclopedii. Pe fiecare raft sunt cărți cu o anumită temă. Și unele cărți au chiar semne de carte de hârtie cu note. În plus, avem un mic catalog de hârtie cu toate cărțile. Dacă vă amintiți, bibliotecile școlare sunt o cutie îngustă cu cartonașe de hârtie. Cu această analogie, dulapul este profilul dispozitivului nostru. Rafturile sunt servicii, cărțile sunt caracteristici, iar catalogul este un tabel de atribute. Semnele de carte din cărți sunt descriptori, despre care voi vorbi și mai târziu mai detaliat.
Oricine a dezvoltat dispozitive știe că multe proiecte au bucăți de cod similare. Faptul este că multe dispozitive au funcționalități similare. De exemplu, dacă dispozitivele sunt alimentate cu baterii, atunci problema încărcării și monitorizării nivelului acestora va fi aceeași. Același lucru este valabil și pentru senzori. De fapt, o abordare orientată pe obiecte a programării „oferă capacitatea de a crea obiecte care combină proprietăți și comportamente într-o uniune autonomă care poate fi apoi reutilizată”. În opinia mea, BLE a încercat o abordare similară. Profilurile au fost dezvoltate de Bluetooth Special Interest Group (SIG). Dispozitivele de la diferiți producători care au aceleași profiluri ar trebui să funcționeze între ele fără dificultate. Profilurile, la rândul lor, constau în servicii și servicii de caracteristici, completate de descriptori. În general, ar putea arăta astfel:

De exemplu, luați în considerare diagrama de profil a unui monitor de ritm cardiac (brățară de fitness). Este format din două servicii și mai multe caracteristici. Din aceasta ierarhia profilului devine imediat clară. Caracteristica punctului de control resetează numărul total de calorii la zero.
1. Serviciul de ritm cardiac include trei caracteristici (0x180D):
a) Caracteristica ritmului cardiac obligatoriu (0x2A37)
b) Caracteristica opțională de poziție a senzorului de corp (0x2A38)
c) Caracteristicile condiționate ale punctului de control al ritmului cardiac (0x2A39)
2. Serviciu de întreținere a bateriei (0x180F):
a) Caracteristică obligatorie a nivelului de încărcare a bateriei (0x2A19)
UUID
Pentru a putea accesa în mod unic elementele de profil (servicii, caracteristici și descriptori), trebuie să le numerotăm pe toate cumva. În acest scop, este introdus un concept precum Universally Unique ID (UUID) sau Universally Unique Identifier. UUID-ul este indicat în parantezele fiecărei linii. Și există o particularitate aici. Pentru UUID, am decis să folosim un cod de 16 și 128 de biți lungime. De ce intrebi? În protocolul BLE, totul este despre conservarea energiei. Prin urmare, dimensiunea de 16 biți este destul de rezonabilă. Este puțin probabil ca mai mult de 65 de mii să fie create în viitorul apropiat. servicii și caracteristici unice. Momentan, tot ce puteau fi numărat deja (amintește-ți de unde a venit asta - „și el te-a numărat pe tine” :-)) Elemente numerotate , , и te poti uita la link-uri.
Cu toate acestea, cred că toată lumea își amintește povestea cu 4 octeți de adrese IP pe Internet. La început am crezut că este suficient, dar acum încă nu putem trece la o adresă de 6 octeți. Pentru a nu repeta această greșeală și a da frâu liber mâinilor jucăușe ale bricolatorilor, SIG a decis imediat să introducă UUID-uri pe 128 de biți. Acest lucru îmi amintește personal de banda de 433 MHz fără licență, care a fost dată la tot felul de Kulibin de pe canalul de radio. În cazul nostru, a fost eliminat un identificator de 128 de biți al serviciilor și caracteristicilor. Aceasta înseamnă că noi, pentru serviciile și dispozitivele noastre, putem folosi aproape orice valoare de 128 de biți. Cu toate acestea, probabilitatea de a veni cu același UUID tinde spre zero.
De fapt, UUID-urile scurte de 16 biți au extensia lor la o valoare de 128 de biți. În specificație, această extensie se numește Bluetooth Base UUID și are valoarea 00000000-0000-1000-8000-00805F9B34FB. Dacă, de exemplu, UUID-ul de atribut pe 16 biți are valoarea 0x1234, atunci UUID-ul echivalent pe 128 de biți va avea valoarea 00001234-0000-1000-8000-00805F9B34FB. Și chiar și formula corespunzătoare este dată:
128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID
Nu știu de unde a venit acest număr magic. Dacă vreunul dintre cititori știe, lăsați-i să scrie în comentarii (Un utilizator cu porecla Sinopteek a făcut deja acest lucru. Vezi comentariile). În ceea ce privește venirea cu UUID-uri pe 128 de biți, în principiu, puteți folosi un special cine o va face pentru tine.
ATTy GATTy...
De fapt, atunci începe distracția. Permiteți-mi să vă reamintesc că ATT se bazează pe o relație client-server. Acum ne uităm la dispozitivul server. Conține informații precum valorile senzorilor, starea comutatorului de lumină, datele despre locație etc. Acum că toți „participanții la parada noastră” sunt numerotați, trebuie să-i plasăm cumva în memoria dispozitivului. Pentru a face acest lucru, le punem într-un tabel numit tabel de atribute. Amintește-ți bine asta. Aceasta este însăși inima BLE. Acesta este ceea ce vom lua în considerare în continuare. Acum vom numi fiecare linie un atribut. Această masă este situată adânc în stivă și, de regulă, nu avem acces direct la el. Îl inițializam și îl accesăm, dar ceea ce se întâmplă în interior ne este ascuns în spatele a șapte sigilii.
Să ne uităm la imaginea din specificație, dar înainte de asta, aș dori să atrag imediat atenția asupra confuziei frecvente în termeni, și anume în descriptori. Rolul descriptorului este de a completa descrierea caracteristicii. Când este necesar să-și extindă capacitățile, atunci se folosesc descriptori. Ele sunt, de asemenea, atribute și, la fel ca serviciile și caracteristicile, sunt localizate în tabelul cu atribute. Le vom examina în detaliu în a doua parte a articolului. Cu toate acestea, uneori descriptorii se referă la numărul rândului din tabelul cu atribute. Acest lucru trebuie reținut. Pentru a evita confuzia, vom folosi termenul „indicator de atribut” în aceste scopuri.

Deci, un atribut este o valoare discretă care are asociate următoarele proprietăți:
1. Mânerul de atribut este indexul tabelului corespunzător atributului
2. Tipul de atribut este un UUID care descrie tipul acestuia
3. Valoarea atributului este datele indexate de indicatorul de atribut
4. Permisiunile de atribut sunt partea unui atribut, permisiunile, care nu pot fi citite sau scrise folosind protocolul de atribut
Cum să înțelegi toate acestea? Indicatorul de atribut este, relativ vorbind, numărul său din tabelul nostru.
Permite unui client să facă referire la un atribut în cererile de citire sau scriere. Ne putem numerota liniile (atributele) de la 0x0001 la 0xFFFF. În asocierea noastră cu bibliotecă, acesta este numărul cardului din catalogul de hârtie. În mod similar, ca și în catalogul bibliotecii, cardurile sunt aranjate în ordine crescătoare a numărului. Numărul fiecărei linii ulterioare trebuie să fie mai mare decât cel precedent. La fel ca în bibliotecă, uneori unele cărți se pierd, așa că la noi, pot exista lacune în numerotarea rândurilor. Acest lucru este permis. Principalul lucru este că acestea merg progresiv.
Tipul de atribut determină ce reprezintă atributul. Prin analogie cu limbajul C,
unde există variabile booleene, numerice și șiruri, deci este aici. După tipul de atribut recunoaștem
cu ce avem de-a face și cum putem continua să lucrăm cu acest atribut. Mai jos vom analiza câteva tipuri specifice de atribute. De exemplu, „declarație de serviciu” (0x2800), „declarație caracteristică” (0x2803), „declarație descriptor” (0x2902).
Valoarea unui atribut este sensul său real, iartă tautologia. Dacă tipul de atribut este un șir, atunci valoarea atributului poate fi, de exemplu, sloganul „Hello World !!!”. Dacă tipul de atribut este o „declarație de serviciu”, atunci valoarea acestuia este serviciul însuși. Și uneori acestea sunt informații despre unde să găsiți alte atribute și proprietățile lor.
Permisiunile de atribut permit serverului să înțeleagă dacă este permis accesul la citire sau la scriere.
Rețineți că aceste permisiuni se aplică numai valorii atributului și nu indicatorului, tipului sau câmpului de permisiune în sine. Acestea. dacă înregistrarea atributelor este permisă, atunci putem schimba, de exemplu, linia „Hello World !!!” la linia „Bună dimineața”. Dar nu putem interzice scrierea unei linii noi sau modifica tipul de atribut și desemnăm linia drept „declarație de serviciu”. Când un client contactează un server, clientul solicită atributele acestuia. Acest lucru permite clientului să știe ce poate oferi serverul. Deși nu este necesar să citiți și să scrieți valorile.
Cum arată?
Conceptul GATT este de a grupa atributele într-un tabel de atribute într-o ordine foarte specifică și logică. Să aruncăm o privire mai atentă la profilul ritmului cardiac de mai jos. Coloana din stânga a acestui tabel este opțională. Pur și simplu ne descrie ce este această linie (atribut). Toate celelalte coloane ne sunt deja familiare.

În vârful fiecărui grup avem întotdeauna un atribut de declarație de serviciu. Tipul său este întotdeauna 0x2800, iar indicatorul depinde de câte atribute sunt deja prezente în tabel. Permisiunile sale sunt întotdeauna numai în citire, fără nicio autentificare sau autorizare. Vom vorbi despre aceste concepte puțin mai târziu. Valoarea este un alt UUID care identifică care este serviciul. În tabel, valoarea este 0x180D, care este definită de Bluetooth SIG ca un serviciu de frecvență cardiacă.
În urma anunțului serviciului, vine anunțul caracteristicii. Este similară ca formă cu o declarație de serviciu. UUID-ul său este întotdeauna 0x2803, iar permisiunile sale sunt întotdeauna numai pentru citire, fără nicio autentificare sau autorizare. Să ne uităm la câmpul Valoare atribut, care include câteva date. Conține întotdeauna un pointer, un UUID și un set de proprietăți. Aceste trei elemente descriu declararea ulterioară a valorii caracteristice. Pointerul indică în mod natural locația declarației valorii caracteristice în tabelul cu atribute. UUID-ul descrie la ce tip de informații sau valoare ne putem aștepta. De exemplu, valoarea temperaturii, starea comutatorului de lumină sau o altă valoare arbitrară. Și în sfârșit proprietăți, care descriu cum poate fi interacționată valoarea caracteristică.
O altă capcană ne așteaptă aici. Este asociat cu permisiunile de atribut și proprietățile caracteristice. Să ne uităm la imaginea proprietăților câmpului de biți din specificație.

După cum puteți vedea, există și câmpuri aici care oferă capacități de citire și scriere. S-ar putea să vă întrebați de ce avem permisiuni de citire/scriere pentru atribut și proprietate
citire/scriere pentru valoarea caracteristică? Nu ar trebui să fie mereu la fel? Faptul este că proprietățile pentru valoarea caracteristică sunt de fapt doar recomandări pentru clientul utilizat în GATT și straturile de aplicație. Acestea sunt doar indicii despre ce s-ar putea aștepta clientul de la atributul declarație caracteristică. Să ne uităm la asta mai detaliat. Ce tipuri de permisiuni are un atribut?
1. Permisiuni de acces:
- citind
- record
- Citeste si scrie
2. Permisiune de autentificare:
- Autentificare necesara
- nu este necesară autentificarea
3. Permisiune de autorizare:
— autorizație necesară
- nu necesita autorizatie
Principala diferență dintre rezoluția atributelor și proprietățile caracteristice este că primele se aplică serverelor, iar cele din urmă clienților. Serverului i se poate permite să citească valoarea caracteristică, dar poate necesita autentificare sau autorizare. Prin urmare, atunci când clientul solicită proprietățile caracteristicii, vom primi că citirea este permisă. Dar când încercăm să citim, primim o eroare. Prin urmare, putem vorbi în siguranță despre prioritatea permisiunilor față de proprietăți. Din partea clientului, nu putem obține cunoștințe despre permisiunile pe care le are un atribut.
Descriptor
Să revenim la masa noastră. După declararea valorii unei caracteristici, sunt posibile următoarele declarații de atribute:
1. Noua declaratie de caracteristici (un serviciu poate avea multe caracteristici)
2. Declarație de serviciu nouă (pot fi multe dintre ele în tabel)
3. Declararea unui mâner
În cazul caracteristicii de măsurare a frecvenței cardiace, în tabelul nostru, declarația valorii caracteristice este însoțită de declarația descriptorului. Un descriptor este un atribut cu informații suplimentare despre o caracteristică. Există mai multe tipuri de descriptori. Vom vorbi despre ele în detaliu în a doua parte a acestui articol. Deocamdată, vom atinge doar Descriptorul de configurare a caracteristicilor clientului (CCCD). Are un UUID egal cu 0x2902. Folosind acest descriptor, clientul are capacitatea de a activa indicarea sau notificarea pe server. Diferența dintre ele este mică, dar încă există. Notificarea nu necesită confirmarea primirii din partea clientului. Indicarea impune acest lucru, deși apare la nivelul GATT, neatingând nivelul de aplicare. De ce așa, întrebi? Vai, nu știu asta. Permiteți-mi să spun doar că experții nordici recomandă utilizarea notificărilor. Mai mult, verificarea integrității pachetului (folosind CRC) are loc în ambele cazuri.
Concluzie
La sfârșitul articolului aș vrea să spun asta. Ultimul tabel este un pic confuz. Totuși, l-am ales pentru că este dat , pe care mă bazez. În a doua parte a articolului meu, intenționez să aprofundez specificația BlueTooth 4.0. Mai multe diagrame și desene corecte ne așteaptă acolo. În a treia parte, aș dori să analizez jurnalul obținut folosind programul Wireshark de la unul dintre gadget-uri și să văd „în direct” toată teoria pe care o studiem.
Angajat al Grupului de Companii
Pecherskikh Vladimir
Sursa: www.habr.com
