11.7. GATT műveletek

Egy karakterisztika csak egy névvel ellátott értékként ücsörög a GATT adatbázisban. Attól válik hasznossá, hogy egy kliens egy kicsi, jól meghatározott műveletkészletet futtathat rajta. Minden karakterisztika egy tulajdonság-bitmaszkban (property bitmask) deklarálja, hogy mely műveleteket támogatja – egy kiszolgáló, amelynek nincs mit közzétennie, csak olvasható értéket publikálhat, egy vezérlőregiszter lehet csak írható, egy frissítéseket közvetítő érzékelő pedig beállítaná a notify bitet. A kliens a felfedezés során megismeri a bitmaszkot, és tiszteletben tartja azt.

Az öt művelet a read, write, write without response, notify és indicate. Két csoportra oszlanak – pull (a kliens kérdez) és push (a kiszolgáló küld).

11.7.1. Pull: olvasás és írás

Ez a kettő a legegyszerűbb, és pontosan úgy néz ki, mint a függvényhívások.

  • Read. A kliens elkéri az aktuális értéket, a kiszolgáló pedig visszaküldi. Egy oda-vissza forduló, a kliens megkapja azokat a bájtokat, amelyeket a kiszolgáló az adott karakterisztikához beállított, a kiszolgáló pedig semmit sem tud meg arról, ki olvasott.

  • Write. A kliens új bájtokat küld, a kiszolgáló pedig eltárolja azokat (és opcionálisan alkalmazáslogikát futtat az új értéken). Két változata létezik:

    • Write with response – a kiszolgáló nyugtáz, és nem nulla állapot esetén bármilyen alkalmazáshibát kivált. Megbízható, egy oda-vissza forduló.

    • Write without response – a kiszolgáló csendben eltárolja a bájtokat; a kliens egyáltalán nem kap nyugtázást. Gyorsabb (nincs a nyugtára váró oda-vissza forduló) és hasznos közvetítéshez, cserébe a hibákról csak mellékcsatornás visszaolvasással lehet tudomást szerezni.

Az aioble-ben a kliensoldali API egyetlen aioble.ClientCharacteristic.write() metódus mögé rejti a választást egy response kulcsszóval (True / False / None az automatikus kiválasztáshoz aszerint, amit a társ hirdet).

11.7.2. Push: notify és indicate

A pull modell nem megfelelő érzékelőadatokhoz. Egy pulzusmérő öv, amelyet a telefonnak másodpercenként le kell kérdeznie, akkumulátort égetne száz felesleges rádióeseményen; az viszont, amelyik csak akkor küld értéket, ha új mérése van, eleve a BLE lényege.

A GATT ezt kiszolgáló-kezdeményezésű (server-initiated) műveletekkel oldja meg. A kliens feliratkozik egy karakterisztikára; ettől a ponttól kezdve minden alkalommal, amikor a kiszolgáló frissíti az értéket, az új érték áttolódik a kapcsolaton a kliensnek. Két változat:

  • Notify. Küldd-és-felejtsd. A kiszolgáló sorba állít egy értesítést, a link réteg a következő kapcsolati esemény alatt elküldi, a kliens pedig megkapja. GATT szinten nincs nyugtázás; a link réteg szokásos újraküldése kezeli a veszteséget a rádió oldalán, de az alkalmazás nem lát megerősítést arról, hogy az értéket feldolgozták.

  • Indicate. A kiszolgáló küld egy értesítést, és megvárja a kliens GATT szintű megerősítését, mielőtt a következőt elküldené. Egyszerre egy jelzés. Akkor használatos, amikor a kiszolgálónak tudnia kell, hogy a kliens valóban látta az értéket – egy kritikus-riasztás karakterisztika, egy konfigurációs nyugtázás.

Két egymás melletti ábra egy kiszolgálóról és egy kliensről. A bal oldalon a kliens "read" üzenetet küld, a kiszolgáló pedig az értékkel válaszol. Három egymást követő olvasás, mindegyik egy nyílpárral. A jobb oldalon a kliens egyetlen "subscribe" üzenetet küld, majd a kiszolgáló az általa választott időpontokban három "notify" csomagot tol át, anélkül, hogy közben bármilyen kliens kérés lenne.

Pull (read) kontra push (notify). Értesítések esetén a kliens egyszer feliratkozik, a kiszolgáló pedig új értékeket tol át, valahányszor azok megváltoznak.

A feliratkozás úgy történik, hogy a kliens ír egy, a karakterisztikához csatolt leíróba – a Client Characteristic Configuration Descriptor-ba (CCCD, 0x2902). A 0x0001 írása engedélyezi az értesítéseket, a 0x0002 engedélyezi a jelzéseket, a 0x0000 mindkettőt letiltja. A aioble.ClientCharacteristic.subscribe() metódus elvégzi helyetted az írást, notify=True és indicate=True kulcsszavas jelzőkkel.

A feliratkozás után a kliens a notified() és indicated() metódusokkal várja a beérkező tolásokat – mindkettő olyan aszinkron korutin, amely a következő tolás megérkezéséig felfüggeszt.

11.7.3. Az MTU szabályozza a hasznos adat méretét

Minden műveletet korlátoz a kapcsolat által a felépüléskor beállított, megtárgyalt MTU. Az alapértelmezett MTU 23 bájt, ami a GATT fejléc után 20 bájtot hagy a karakterisztika értékbájtjainak. Bármi, ami ennél nagyobb, vagy be kell, hogy férjen egy nagyobb MTU-ba (a aioble.DeviceConnection.exchange_mtu() segítségével felfelé tárgyalva, a kamerán akár 512 bájtig), vagy több karakterisztikára vagy több értesítésre kell felosztani.

Az MTU-nál nagyobb értékek kliens-kezdeményezésű olvasásait és írásait a GATT long eljárásai kezelik a háttérben (Read Long / Prepare-Write + Execute-Write); az aioble ezeket átláthatóan futtatja, így a read() / write() túlméretes értékkel való hívása csak több oda-vissza fordulóba kerül. A kiszolgáló-kezdeményezésű értesítéseket és jelzéseket nem darabolja fel – egy tolást az MTU korlátoz, és az alkalmazás bármit, ami ennél nagyobb, több értesítésre bont, vagy teljesen elhagyja a GATT-ot.

Valóban nagy átvitelekhez – egy rögzített képkockához, egy mérési kötegekhez, egy firmware-bloblhoz – a helyes válasz általában az, hogy teljesen elhagyjuk a GATT-ot, és helyette egy L2CAP csatornát használunk (lásd L2CAP-csatornák).

11.7.4. A két oldal egy pillantásra

Az öt művelet a kapcsolat mindkét oldalán másképp jelenik meg: