11.8. Modul aioble

Specifikace Bluetooth Core poskytuje slovník, který se mapuje na dva moduly MicroPython.

  • bluetoothnízkoúrovňová vazba na BLE řadič. Synchronní, řízená událostmi přes callback ve stylu IRQ a strukturovaná kolem bajtových bufferů, handlů a holých GATT primitiv. Vystavuje protokol takový, jaký je, nikoli takový, jak ho chtějí konzumovat aplikace v Pythonu.

  • aioble – vyšší úrovňový obal napsaný v Pythonu nad bluetooth, který každou vzdálenou operaci proměňuje na korutinu asyncio a každý BLE objekt (služby, charakteristiky, připojení, výsledky skenování, kanály L2CAP) na ergonomickou třídu v Pythonu. Skenování se stává asynchronními iterátory; připojení se stávají asynchronními kontextovými správci; oznámení se stávají awaitable.

11.8.1. Kdy sáhnout po nízkoúrovňovém modulu

bluetooth je stále tou správnou odpovědí pro dva úzké případy:

  • Píšete druh kódu, ze kterého je vystavěn sám aioble – nový vzor, který potřebuje řízení protokolu na úrovni IRQ.

  • Běžíte na hardwarové platformě, kde balíček aioble není k dispozici, a tenká mezivrstva kolem řadiče je jedinou možností.

Pro každou kamerovou aplikaci je aioble tou správnou odpovědí.

11.8.2. Části programu aioble

Každá aplikace založená na aioble má malou množinu pohyblivých částí, bez ohledu na to, jaké role hraje.

  • Dlouho běžící smyčka událostí asyncio. Vše v aioble je korutina, takže aplikace je strukturována jako jedna nebo více úloh na jediné smyčce událostí. Podrobnosti o smyčce, úlohách a výjimkách viz Asyncio.

  • Zapnuté rádio. aioble aktivuje BLE rádio implicitně při prvním použití, ale lze ho také řídit explicitně pomocí aioble.config() (které předává volání bluetooth.BLE.config() poté, co zajistí, že je rádio v provozu) a vypnout pomocí aioble.stop().

  • Jedna nebo více rolí najednou v běhu. Na straně periferie: registrovaná sada GATT služeb (viz aioble.register_services()) a běžící korutina aioble.advertise(). Na straně centrálního zařízení: běžící iterátor aioble.scan() nebo čekající aioble.Device.connect(). Rádio práci multiplexuje; aplikace vidí každou roli jako nezávislou úlohu.

11.8.3. Minimální periferie

Nejmenší užitečný program aioble – periferie, která inzeruje jedinou charakteristiku pouze pro čtení – je krátký:

import aioble
import asyncio
import bluetooth

SERVICE_UUID = bluetooth.UUID(0x181A)            # Environmental Sensing
TEMP_UUID = bluetooth.UUID(0x2A6E)               # Temperature

service = aioble.Service(SERVICE_UUID)
temp = aioble.Characteristic(service, TEMP_UUID, read=True)
aioble.register_services(service)

async def main():
    while True:
        conn = await aioble.advertise(
            interval_us=250000,
            name="openmv-temp",
            services=[SERVICE_UUID],
        )
        async with conn:
            await conn.disconnected()

asyncio.run(main())

Centrální zařízení, které nedělá nic víc než se připojí a jednou přečte, je podobně krátké:

import aioble
import asyncio
import bluetooth

SERVICE_UUID = bluetooth.UUID(0x181A)
TEMP_UUID = bluetooth.UUID(0x2A6E)

async def main():
    device = None
    async with aioble.scan(duration_ms=5000, active=True) as scanner:
        async for result in scanner:
            if SERVICE_UUID in result.services():
                device = result.device
                break
    if device is None:
        return

    async with await device.connect() as conn:
        service = await conn.service(SERVICE_UUID)
        char = await service.characteristic(TEMP_UUID)
        print(await char.read())

asyncio.run(main())

Oba programy mají kolem patnácti řádků a pokrývají celý tok od „rádio je vypnuté“ po „užitečná práce hotova“.

11.8.4. Vypnutí rádia

Na kameře napájené z baterie je BLE rádio největším volitelným odběrem z rozpočtu. Záleží na dvou regulátorech.

První je implicitní: aioble aktivuje rádio při prvním použití a rádio mezi naplánovanými událostmi (inzertní dávky, události připojení, skenovací okna) automaticky usíná. Volba delších intervalů u aioble.advertise() / aioble.scan() a dohoda na delším intervalu připojení v čase connect() udržuje rádio vypnuté úměrně více času. Praktickým vodítkem je zde tabulka inzerce v Vysílání (advertising) a skenování.

Druhým je explicitní vypnutí:

import aioble

await do_burst_of_ble_work()
aioble.stop()                             # radio deactivated; in-flight tasks unwound
await asyncio.sleep(60)                   # sleep with the radio off
# ... next aioble call brings the radio back up automatically

aioble.stop() deaktivuje podkladové BLE rádio a strhne vše, co je v běhu – otevřená připojení vypadnou, skenery a inzerující se zruší, kanály L2CAP se zavřou. Korutiny, které na tyto operace čekaly, vyvolají své obvyklé výjimky (DeviceDisconnectedError a podobné), což je úklidový mechanismus, pro který byly okolní bloky async with napsány. Volání jakékoli korutiny aioble poté znovu aktivuje rádio ze studeného stavu.

Typický vzor pro periodickou senzorovou kameru napájenou z baterie je:

  • Probudit se podle plánu (časovač, pohybový senzor, tlačítko).

  • Spustit dávku BLE práce – inzerovat, přijmout připojení, odeslat hodnotu, odpojit.

  • Zavolat aioble.stop() a spát do dalšího probuzení.

11.8.5. Co aioble nedělá

aioble záměrně pokrývá GATT, GAP a L2CAP – vrstvy, které aplikace používá. Tři části jsou mimo rozsah:

  • Cokoli pod linkovou vrstvou. Výběr kanálu, frekvenční skákání, potvrzování paketů a šifrování na linkové vrstvě se vše odehrává uvnitř BLE portu a křemíku řadiče; aioble na této úrovni nevystavuje žádné háčky.

  • Klasický Bluetooth. aioble je pouze pro BLE. Audio spoje, RFCOMM, A2DP a další funkce klasických profilů nejsou součástí API.

  • Bluetooth Mesh. Vrstva síťování typu mesh od Bluetooth SIG (samostatný stack nad BLE inzercí) není na kameře implementována. Kamera může inzerovat a pozorovat, ale nemůže se účastnit rolí relay / friend / proxy v mesh síti.

11.8.6. Výjimky

Z aioble vychází čtyři typy výjimek. Každá vystřelí zevnitř korutiny, která čekala na operaci, když se něco pokazilo; bloky async with se při jejich propagaci čistě rozvinou.

  • aioble.DeviceDisconnectedError – BLE spoj k protějšku vypadl, zatímco byla v běhu GATT operace (read, write, notified, indicated, subscribe, exchange_mtu, …). Vyvolána uvnitř té korutiny, která čekala. Zdaleka nejběžnější výjimka; zachyťte ji v jakémkoli kódu, který se má při ztrátě znovu připojit.

  • aioble.GattError – GATT operace dosáhla protějšku, ale dokončila se s nenulovým ATT stavem (write-with-response odmítnuto, indicate nepotvrzeno, read-not-permitted, …). Stavový kód je na atributu _status výjimky.

  • aioble.L2CAPDisconnectedError – kanál L2CAP vypadl, zatímco bylo v běhu send(), recvinto() nebo flush(). Kanál mohla zavřít kterákoli ze stran, nebo zaniklo podkladové GAP připojení.

  • aioble.L2CAPConnectionError – vyvolána l2cap_connect(), když naslouchající odmítl nebo řadič selhal při sestavení kanálu. Stavový kód Bluetooth je prvním poziční argumentem.

Operace, které berou explicitní timeout_ms (volání connect / discovery / read / write / pair, plus timeout() jako obal), navíc vyvolají asyncio.TimeoutError z asyncio, když časový limit uplyne dříve, než se operace dokončí.