BLE sub microscop (ATT-uri GATT-uri…)

BLE sub microscop (ATT-uri GATT-uri...)

BLE sub microscop (ATT-uri GATT-uri…)

Partea 1, revizuire

A trecut deja o perioadă destul de mare de timp de când a apărut prima specificație pentru Bluetooth 4.0. Și, deși subiectul BLE este foarte interesant, încă îi sperie pe mulți dezvoltatori din cauza complexității sale. În articolele mele anterioare, am abordat în principal cel mai de jos nivel, Link Layer și Physical Layer. Acest lucru a permis să nu fie necesară abordarea unor concepte atât de complexe și confuze precum protocolul atributelor (ATT) și profilul general al atributelor (GATT). Totuși, nu este de ales, fără a le înțelege, nu este posibil să se dezvolte dispozitive compatibile. Astăzi aș dori să împărtășesc cu voi aceste cunoștințe. În articolul meu, mă voi baza pe tutorial pentru începători de pe site-ul Nordic. Deci, să începem.

De ce este totul atât de complicat?

Din punctul meu de vedere, a fost imediat clar că gestionarea dispozitivelor prin intermediul smartphone-urilor este un domeniu foarte promițător și de lungă durată. De aceea, s-a decis să fie structurat de la început și cât mai mult posibil. Ca să nu vină producătorii de diverse gadget-uri cu propriile lor protocoale, care ulterior nu vor fi compatibile. De aici provine complexitatea. Chiar și în prima etapă, protocolul BLE a încercat să încorporeze tot ce era posibil. Și nu contează dacă se va dovedi util ulterior sau nu. În plus, s-a prevăzut posibilitatea extinderii listei de dispozitive pentru viitor.

Să ne uităm la imaginea care ilustrează schema protocolului BLE. Acesta este compus 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 acesta. Host Controller Interface (HCI) este protocolul de comunicație între straturi sau cipurile BLE, dacă Controller și Host sunt implementate pe cipuri diferite. Formarea pachetelor, divizarea în cadre, controlul erorilor și asamblarea pachetelor sunt responsabilitățile Logical Link Control and Adaptation Protocol (L2CAP). Pentru criptarea pachetelor este responsabil Security Manager Protocol (SMP). Profilul de acces general (GAP) se ocupă de schimbul inițial de date între dispozitive, pentru a determina „Cine este cine”. Acesta include de asemenea scanarea și publicitatea. În acest articol mă voi opri asupra celor două părți rămase ale protocolului — GATT și ATT. GATT este un overlay asupra ATT, prin urmare, acestea sunt foarte interconectate.

BLE sub microscop (ATT-uri GATT-uri...)

Pentru a simplifica narațiunea, aș dori să fac o analogie. Am auzit-o undeva și aș vrea să o susțin. Imaginați-vă un dispozitiv BLE ca un bibliotecă cu mai multe rafturi. Fiecare raft reprezintă o temă distinctă. De exemplu, avem rafturi cu literatură științifico-fantastică, matematică, enciclopedii. Pe fiecare raft stau cărți pe tema respectivă. Iar în unele cărți există chiar și semne de carte cu însemnări. În plus, avem un mic catalog pe hârtie cu toate cărțile. Dacă vă amintiți de bibliotecile școlare — este un sertar îngust cu cărți de hârtie. În această analogie, biblioteca este profilul dispozitivului nostru. Rafturile sunt serviciile, cărțile sunt caracteristicile, iar catalogul este tabelul atributelor. Semnele de carte din cărți reprezintă descriptoarele, despre care voi vorbi mai în detaliu mai târziu.

Toți cei care au dezvoltat dispozitive știu că în multe proiecte există segmente de cod asemănătoare. Asta pentru că multe dispozitive au funcționalități similare. De exemplu, dacă dispozitivele funcționează pe baterii, problema încărcării și monitorizării nivelului acestora va fi la fel. Același lucru se aplică și senzorilor. Practic, abordarea orientată pe obiect în programare „oferă posibilitatea de a crea obiecte care combină proprietăți și comportamente într-o uniune autonomă, care poate fi folosită de mai multe ori”. În opinia mea, BLE a încercat o abordare similară. Grupul Bluetooth Special Interest Group (SIG) a dezvoltat profile. Dispozitivele de la diferiți producători, care au profiluri identice, ar trebui să funcționeze fără probleme unele cu altele. Profilele, la rândul lor, sunt compuse din servicii, iar serviciile din caracteristici, completate de descriptoare. În general, aceasta poate arăta astfel:

BLE sub microscop (ATT-uri GATT-uri...)

De exemplu, să luăm în considerare schema profilului monitorului de ritm cardiac (brățară fitness). Acesta constă din două servicii și mai multe caracteristici. Din aceasta devine imediat clară ierarhia profilului. Caracteristica punctului de control resetează numărătoarea totală a caloriilor la zero.

1. Serviciul de ritm cardiac include trei caracteristici (0x180D):
    a) Caracteristica obligatorie a frecvenței cardiace (0x2A37)
    b) Caracteristica opțională a poziției senzorului corporal (0x2A38)
    c) Caracteristica condiționată a punctului de control al ritmului cardiac (0x2A39)
2. Serviciul de întreținere a bateriei (0x180F):
    a) Caracteristica obligatorie a nivelului de încărcare a bateriei (0x2A19)

UUID

Pentru a putea face referire clară la elementele profilului (servicii, caracteristici și descriptori), trebuie să le numerotăm în vreun fel. Pentru această scop se introduce noțiunea de Universally Unique ID (UUID) sau Identificator Unic Universal. În parantezele fiecărei linii este indicat UUID-ul. Și aici există o particularitate. Pentru UUID s-a decis să se folosească un cod cu o lungime de 16 și 128 de biți. De ce, vă întrebați? În protocolul BLE totul este subordonat economisirii energiei. Prin urmare, dimensiunea de 16 biți este complet rezonabilă. Este puțin probabil ca, în viitorul apropiat, să fie create mai mult de 65 de mii de servicii și caracteristici unice. Până acum, tot ce au putut, au fost deja numărate (amintiți-vă de unde provine — „el v-a numărat și pe voi” :-)) Elementele numerotate profiluri, servicii, caracteristici și descriptori puteți vizualiza prin linkuri.

Cu toate acestea, cred că toți își amintesc povestea cu cei 4 biți ai adresei IP în internet. La început s-a crezut că este suficient, iar acum încă nu putem trece la adresa de 6 biți. Pentru a nu repeta această greșeală și a oferi libertate unor mâini iscusite, SIG a decis imediat să introducă și UUID-uri de 128 de biți. Acest lucru îmi amintește personal de banda neautorizată de 433MHz, care a fost lăsată în seama diverselor meșteșuguri de radior. În cazul nostru, i-a fost încredințat un identificator de servicii și caracteristici de 128 de biți. Aceasta înseamnă că putem folosi practic orice valoare de 128 de biți pentru serviciile și dispozitivele noastre. Oricum, probabilitatea de a inventa un UUID identic tinde spre zero.

De fapt, UUID-urile scurte de 16 biți au o extensie la valoarea 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 atributului de 16 biți are valoarea 0x1234, atunci UUID-ul corespunzător de 128 de biți va avea valoarea 00001234-0000-1000-8000-00805F9B34FB. Și este, de asemenea, oferită o formulă corespunzătoare:

                                128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID

De unde provine acest număr magic, nu știu. Dacă cineva dintre cititori știe — să scrie în comentarii (Utilizatorul cu pseudonimul Sinopteek a făcut deja asta. Consultați comentariile). Cât despre inventarea UUID-urilor de 128 de biți, practic se poate folosi un generator, care va face acest lucru pentru dumneavoastră.

ATT-urile GATT…

Aici începe cu adevărat partea interesantă. Vreau să vă reamintesc că ATT se bazează pe o relație client-server. Acum analizăm structura serverului. Acesta conține informații precum valorile senzorilor, starea întrerupătorului de lumină, datele de localizare etc. Acum, când toți «participanții paradei noastre» sunt numerotați, trebuie să îi plasăm cumva în memoria dispozitivului. Pentru aceasta, îi punem într-un tabel, care se numește tabelul atributelor. Țineți minte acest lucru bine. Asta este inima BLE. Tocmai acesta îl vom explora mai departe. Acum, fiecare linie o vom numi atribut. Acest tabel se află adânc în stivă și, de obicei, nu avem acces direct la el. Îl inițiem și ne adresăm lui, dar ce se întâmplă acolo înăuntru, este ascuns de noi sub șapte pecete.

Să examinăm imaginea din specificație, dar înainte de asta, vreau să subliniez imediat confuzia frecventă din termeni, mai precis în descriptorii. Rolul descriptorului este de a completa descrierea caracteristicii. Când trebuie să extindem capacitățile acesteia, atunci utilizăm descriptorii. Aceștia sunt, de asemenea, atribute, și la fel, împreună cu serviciile și caracteristicile, se află în tabelul atributelor. Vom analiza în detaliu acest lucru în a doua parte a articolului. Totuși, uneori descriptorii sunt numiți numărul liniei din tabelul atributelor. Trebuie să avem asta în vedere. Noi, pentru a nu ne confunda, vom folosi termenul „indicator de atribut” pentru aceste scopuri.
BLE sub microscop (ATT-uri GATT-uri...)

Așadar, atributul este o valoare discretă care are următoarele proprietăți asociate:
1. Indicatorul atributului (Attribute Handle) — este indexul din tabelul care corespunde atributului
2. Tipul atributului (Attribute Type) — este UUID-ul care descrie tipul acestuia
3. Valoarea atributului (Attribute Value) — este datele, indexate de indicatorul atributului
4. Permisiunile atributului (Attribute Permissions) — aceasta este partea atributului, permisiuni care nu pot fi citite sau scrise folosind protocolul atributelor

Cum să înțelegem toate acestea? Indicatorul atributului este, în termeni generali, numărul său în tabelul nostru.
Acesta permite clientului să se refere la un atribut în cererile de citire sau scriere. Putem numerota liniile noastre (atributele) de la 0x0001 la 0xFFFF. În asociația noastră cu un raft de cărți - este numărul cărții din catalogul de hârtie. La fel ca în catalogul unei biblioteci, cărțile sunt aranjate în ordinea creșterii numerelor. Numărul fiecărei linii ulterioare trebuie să fie mai mare decât cel precedent. Asemenea unei biblioteci, uneori se pierd anumite cărți, așa că și în numerotarea liniilor pot exista intervale. Acest lucru este acceptabil. Principalul lucru este ca acestea să fie ordonate în ordine crescătoare.

Tipul atributului determină ce reprezintă acest atribut. Prin analogie cu limbajul C,
unde există variabile booleene, numerice și șiruri, așa și aici. După tipul atributului aflăm
cu ce avem de-a face și cum trebuie să lucrăm ulterior cu acest atribut. Mai jos vom analiza unele tipuri specifice de atribute. De exemplu, „declarația de serviciu” (0x2800), „declarația caracteristicii” (0x2803), „declarația descriptorului” (0x2902).

Valoarea atributului este, de fapt, valoarea sa, scuze pentru tautologie. Dacă tipul atributului este șir, atunci valoarea atributului poate fi, de exemplu, sloganul „Hello World !!!”. Dacă tipul atributului este „declarația de serviciu”, atunci valoarea sa este serviciul în sine. Uneori este informația despre unde se pot găsi alte atribute și proprietățile lor.

Permisiunile atributelor permit serverului să înțeleagă dacă accesul la citire sau scriere este permis.
Rețineți că aceste permisiuni se aplică doar valorii atributului, nu și pointerului, tipului și câmpului de permisiuni în sine. Adică, dacă scrierea atributului este permisă, putem schimba, de exemplu, șirul „Hello World !!!” cu șirul „Good morning”. Dar nu putem interzice scrierea unui nou șir sau schimba tipul atributului și să denumim șirul „declarația de serviciu”. Atunci când clientul se adresează serverului, acesta solicită atributele sale. Acest lucru permite clientului să afle ce poate oferi serverul. Cu toate acestea, nu este obligatoriu să citească și să scrie valori.

Cum arată asta

Conceptul GATT are ca scop gruparea atributelor din tabelul atributelor într-o ordine foarte specifică și logică. Să analizăm mai atent profilul frecvenței cardiace prezentat mai jos. Coloana din stânga a acestui tabel nu este obligatorie. Aceasta ne descrie ce reprezintă această linie (atribut). Toate celelalte coloane ne sunt deja cunoscute.

BLE sub microscop (ATT-uri GATT-uri...)

În partea de sus a fiecărei grupe, avem întotdeauna atributul de declarație a serviciului. Tipul său este întotdeauna 0x2800, iar pointerul depinde de câte atribute sunt deja prezente în tabel. Permisiunile sale sunt întotdeauna disponibile doar pentru citire, fără vreo verificare de autenticitate sau autorizare. Aceste concepte le vom discuta mai târziu. Valoarea este un alt UUID, care definește ce este acest serviciu. În tabel, valoarea este 0x180D, definită de Bluetooth SIG ca serviciu de frecvență cardiacă.

După declarația serviciului, urmează declarația caracteristicii. Ca formă, aceasta este similară declarației serviciului. UUID-ul său are întotdeauna valoarea 0x2803, iar permisiunile sunt, de asemenea, disponibile doar pentru citire, fără vreo verificare de autenticitate sau autorizare. Să ne uităm la câmpul Valoarea Atributului, care include anumite date. Acesta conține întotdeauna un pointer, UUID și un set de proprietăți. Aceste trei elemente descriu declarația ulterioară a valorii caracteristicii. Pointerul desemnează, în mod natural, locul declarației valorii caracteristicii în tabelul atributelor. UUID-ul descrie ce tip de informație sau valoare putem aștepta. De exemplu, valoarea temperaturii, starea întrerupătorului de lumină sau orice altă valoare arbitrară. Și, în final, proprietățile, care descriu cum putem interacționa cu valoarea caracteristicii.

Aici ne așteaptă încă o capcană. Aceasta este legată de permisiunile atributelor și proprietățile caracteristicilor. Să ne uităm la imaginea proprietăților câmpului bit din specificație.

BLE sub microscop (ATT-uri GATT-uri...)

După cum vedeți, sunt prezente și câmpuri care oferă posibilități de citire și scriere. Vă puteți întreba de ce avem permisiuni de citire/scriere pentru atribut și proprietate.
Citirea/scrierea pentru valoarea caracteristicii? Nu ar trebui să fie întotdeauna aceleași? Faptul este că proprietățile pentru valoarea caracteristicii sunt, de fapt, doar recomandări pentru client, folosite în GATT și în straturile aplicației. Acestea sunt doar sugestii despre ce poate aștepta clientul de la atributul declarației caracteristicii. Să ne ocupăm mai în detaliu de aceasta. Ce tipuri de permisiuni există pentru atribut?

1. Permisiuni de acces:
     — citire
     — scriere
     — citire și scriere
2. Permisiune de autentificare:
     — autentificarea este necesară
     — autentificarea nu este necesară
3. Permisiune de autorizare:
     — autorizarea este necesară
     — autorizarea nu este necesară

Principala diferență între permisiunile atributelor și proprietățile caracteristicilor este că primele se referă la servere, iar cele din urmă la clienți. Un server poate avea permisiunea de a citi valoarea caracteristicii, dar poate avea și cerința de autentificare sau autorizare. Prin urmare, atunci când clientul solicită proprietățile caracteristicii, vom obține că citirea este permisă. Dar, la încercarea de a citi, vom obține o eroare. De aceea, putem afirma cu certitudine că permisiunile au prioritate asupra proprietăților. Cunoașterea permisiunilor de care dispune un atribut nu poate fi obținută din partea clientului.

Descriptor

Să ne întoarcem la tabelul nostru. După declarația valorii caracteristicii, sunt posibile următoarele declarații ale atributelor:
1. O nouă declarație a caracteristicii (în serviciu pot exista multe caracteristici)
2. O nouă declarație a serviciului (în tabel pot exista multe)
3. Declarația descriptorului

În cazul caracteristicii de măsurare a frecvenței cardiace, în tabelul nostru, anunțul valorii caracteristicii este însoțit de anunțul descriptorului. Descriptorul este un atribut cu informații suplimentare despre caracteristică. Există mai multe tipuri de descriptor. Vom discuta despre acestea în detaliu în a doua parte a acestui articol. Acum, ne vom ocupa doar de descriptorul de configurare a caracteristicilor clientului (Client Characteristic Configuration Descriptor - CCCD). Acesta are un UUID egal cu 0x2902. Prin intermediul acestui descriptor, clientul are posibilitatea de a activa pe server indicația sau notificarea. Diferența dintre ele este mică, dar totuși există. Notificarea nu necesită confirmare din partea clientului. Indicația însă cere asta, deși se întâmplă la nivel GATT, fără a ajunge la nivelul aplicației. De ce așa, veți întreba? Din păcate, acest lucru nu îmi este cunoscut. Pot spune doar că specialiștii de la Nordic recomandă utilizarea notificării. Cu atât mai mult, deoarece verificarea integrității pachetului (prin intermediul CRC) se realizează în ambele cazuri.

Concluzie

La finalul articolului, aș vrea să spun următorul lucru. Ultimul tabel este oarecum confuz. Totuși, m-am oprit asupra lui pentru că este prezentat în pe care l-ați citit, pe care mă bazez. În a doua parte a articolului meu intenționez să mă aprofundez în specificația Bluetooth 4.0. Acolo ne așteaptă scheme și desene mai corecte. În a treia parte, aș dori să analizez un log obținut cu ajutorul programului Wireshark de la unul dintre gadgeturi și să văd „în direct” toată teoria pe care o studiem.

Angajat al Grupului de Companii „Cezar Satelit”
Peceriști Vladimir

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster