11.11. L2CAP-kanavat¶
GATT on avain/arvo-malli. Sen tarjoamat operaatiot (read, write, notify, indicate) siirtävät yhden lyhyen arvon kerrallaan, ja suurin yksittäinen hyötykuorma, jonka ne voivat kantaa, on neuvotellun MTU:n salliman verran – enimmillään muutama sata tavua. Tämä toimii hyvin sensorilukemille, komentorekistereille ja tilalipuille. Se pettää kilo- tai megatavuilla: pitkän blobin pilkkominen sadoiksi pieniksi kirjoituksiksi maksaa edestakaisia matkoja, joita radio on paljon nopeampi.
Massadatavirroille – kameran puhelimelle suoratoistama otettu kehys, langaton päivityskuva, mittausten eräajettu vienti – BLE tarjoaa vaihtoehtoisen reitin: Logical Link Control and Adaptation Protocol, L2CAP. L2CAP sijaitsee linkkikerroksen ja GATT:n välissä ja antaa sovelluksen vaatia oman yhteyssuuntautuneen kanavansa saman radioyhteyden päälle. Kanava on luottovirtaohjattu tavupolku, jolla on paljon suurempi pakettikohtainen MTU eikä GATT-kehystystä välissä.
11.11.1. Milloin L2CAP:ia kannattaa käyttää¶
L2CAP-kanavat ovat oikea työkalu, kun:
Siirto on suurempi kuin muutama sata tavua.
Molemmat päät tietävät, että L2CAP-kanavaa käytetään (sitä ei paljasteta mainoshyötykuormassa; asiakkaan on tiedettävä kanavan protocol/service multiplexer- eli PSM-numero kaistan ulkopuolelta).
Sovellus on valmis luopumaan GATT:n mukavuuksista: ei sisäänrakennettua osoitettavuutta UUID:llä, ei asiakkaan löydettävyyttä vakiosovellusten kautta, ei ilmoituksia.
Yleisin tapaus aioble-pohjaisissa sovelluksissa on binäärisen blobin siirtäminen kahden ohjelmiston välillä, jotka molemmat tuntevat PSM-sopimuksen – mukautettu kameran ja puhelimen välinen protokolla, kaksi keskenään keskustelevaa openmv-kameraa, sisäinen laiteohjelmiston päivitysreitti peripheraalin GATT-palvelun alla.
Kaikessa muussa pysy GATT:ssa. Lyhyt tila, ohjausrekisteri, sensorilukema – kaikki nuo kuuluvat ominaisuuteen.
11.11.2. Kanavan muodostaminen¶
L2CAP ajetaan olemassa olevan aioble.DeviceConnection-yhteyden päällä, joten GAP-puolen löytö- / mainostus- / yhdistämisvirta on täsmälleen sama kuin GATT:lla. Kun molemmat puolet pitävät yhteyttä, toinen puoli kuuntelee PSM:ää ja toinen puoli yhdistyy siihen.
PSM on vain pieni kokonaisluku. Bluetooth SIG varaa alueen pohjan standardoituun käyttöön (0x0001-0x007F); sovelluskohtaisille kanaville käytä numeroa dynaamiselta alueelta (0x0080-0x00FF kiinteille PSM:ille, 0x0040 eteenpäin tyypillisesti vapaa mukautettuun käyttöön). Molempien puolten on sovittava arvosta etukäteen.
L2CAP-kanavan MTU on suurin yksittäinen SDU (Service Data Unit), jonka kumpikaan puoli toimittaa yhdessä send()-kutsussa – ei BLE-linkin MTU. Aioble pilkkoo suuremmat hyötykuormat automaattisesti. Kameran BLE-isäntä rajaa L2CAP-MTU:n 1017 tavuun; 512 on järkevä oletusarvo, joka jättää tilaa molemmille puolille polttamatta RAM-muistia.
Kuuntelijapuolella (esim. kamera peripheraalina):
async def serve_l2cap(connection, image_bytes):
channel = await connection.l2cap_accept(psm=0x80, mtu=512)
async with channel:
# image_bytes is a bytearray -- e.g. csi0.snapshot().bytearray()
# or a compressed JPEG buffer. send() fragments into MTU-sized
# chunks automatically and awaits flow-control credits between.
await channel.send(image_bytes)
await channel.flush()
Yhdistäjäpuolella (esim. puhelin tai central):
async def open_l2cap(connection, total_bytes):
channel = await connection.l2cap_connect(psm=0x80, mtu=512)
async with channel:
image_bytes = bytearray(total_bytes)
view = memoryview(image_bytes)
received = 0
while received < total_bytes:
n = await channel.recvinto(view[received:])
if n == 0:
break
received += n
return image_bytes
l2cap_accept() estää, kunnes vertaislaite yhdistyy (tai timeout_ms laukeaa); l2cap_connect() estää, kunnes kuuntelija hyväksyy (tai epäonnistuu). Molemmat palauttavat aioble.L2CAPChannel-olion – joka on itse asynkroninen kontekstinhallinta, joka sulkee kanavan poistuttaessa.
11.11.3. Lähettäminen ja vastaanottaminen¶
Kanavan kaksi pääoperaatiota ovat send() (kirjoittaa tavuja vertaislaitteelle) ja recvinto() (lukee esivaratuun puskuriin). Molemmat ovat korutiineja.
send()pilkkoo puskurin MTU:n kokoisiin paloihin ja odottaa linkkikerroksen virtaohjausluottoja niiden välissä. Pitkä lähetys on yksiawaitsovelluksen näkökulmasta; sisäisesti se voi jonottaa monta pakettia ja keskeytyä aina, kun vertaislaitteen vastaanottoluotot loppuvat.recvinto()täyttää annetun puskurin sillä, mitä on saatavilla (kanavan MTU:hun asti) ja palauttaa tavumäärän. Odottaa, jos mitään ei ole saatavilla.available()palauttaa synkronisestiTrue, jos puskuroitua dataa on valmiina – hyödyllistä kyselyyn ilman keskeyttämistä.flush()odottaa, kunnes mahdollinen kesken oleva lähetys on lähetetty kokonaan ohjaimelle.
L2CAP-kanavat ovat virtamaisia siinä mielessä, että tavut saapuvat järjestyksessä ja häviöttä, mutta yksittäisen send-kutsun rajat säilytetään – jokainen SDU tulee ulos yhdestä recvinto-kutsusta. Tämä eroaa TCP:stä, jossa yhden send()-kutsun rajat voivat levitä useaan recv()-kutsuun.
11.11.4. Yhteyden katkeamisen käsittely¶
Kanava katoaa kolmessa tilanteessa: jompikumpi puoli kutsuu disconnect(), taustalla oleva GAP-yhteys katkeaa, tai L2CAP-tason katkaisu saapuu. Aktiiviset operaatiot nostavat aioble.L2CAPDisconnectedError-poikkeuksen. Kuten GATT-puolella, tämä ilmenee poikkeuksena siinä korutiinissa, joka oli odottamassa, ja async with channel -lohko poistuu siististi.
Jos kanavasta tulee tavoittamaton GAP-tason katkaisun kautta, sovellus palaa takaisin mainostamaan tai skannaamaan samalla tavalla kuin se tekisi GATT-katkaisun yhteydessä.
11.11.5. Muistikustannus¶
Suuremmat MTU:t ja pidemmät jonot käyttävät enemmän RAM-muistia molemmilla puolilla. 512 tavun MTU plus kanavakohtainen vastaanottopuskuri on noin 1 KB kanavaa kohti – ei ilmaista pienellä kameralla, jos useita kanavia on auki yhtä aikaa. Pidättäydy yhdessä kanavassa yhteyttä kohti ja valitse MTU, joka vastaa odotettua viestin kokoa; oletus yksi L2CAPChannel DeviceConnection-yhteyttä kohti riittää useimpiin sovelluksiin.
L2CAP on BLE:n varoventtiili. GATT on se, mihin lähes jokainen sovellus tarttuu ensin, ja tämän osion muut central- / peripheral-esimerkit pysyvät GATT:ssa. Kanavatyylinen API on vastaus, kun sovellus kasvaa avain/arvo-mallia suuremmaksi.