11.8. De aioble-module¶
De Bluetooth Core-specificatie geeft een vocabulaire dat op twee MicroPython-modules afgebeeld kan worden.
bluetooth– de low-level binding met de BLE-controller. Synchroon, gebeurtenisgestuurd via een IRQ-achtige callback, en opgebouwd rond bytebuffers, handles en de kale GATT-primitieven. Het stelt het protocol bloot zoals het is, niet zoals Python-toepassingen het willen consumeren.aioble– een hoger niveau wrapper, geschreven in Python bovenopbluetooth, die elke externe bewerking omzet in eenasyncio-coroutine en elk BLE-object (services, characteristics, verbindingen, scanresultaten, L2CAP-kanalen) in een ergonomische Python-klasse. Scans worden async-iterators; verbindingen worden async-contextmanagers; notificaties worden awaitable.
11.8.1. Wanneer naar de low-level module te grijpen¶
bluetooth is nog steeds het juiste antwoord voor twee specifieke gevallen:
Je schrijft het soort code waaruit
aioblezelf is opgebouwd – een nieuw patroon dat IRQ-niveau-controle over het protocol nodig heeft.Je draait op een hardware-target waar het
aioble-pakket niet beschikbaar is, en een dunne shim rond de controller is de enige optie.
Voor elke cameratoepassing is aioble het juiste antwoord.
11.8.2. Onderdelen van een aioble-programma¶
Elke op aioble gebaseerde toepassing heeft een kleine set bewegende onderdelen, ongeacht welke rollen het speelt.
Een langlopende
asyncio-event-loop. Alles inaiobleis een coroutine, dus de toepassing is gestructureerd als een of meer taken op één event-loop. Zie Asyncio voor de loop, taken en exceptions in detail.Een radio die aanstaat.
aiobleactiveert de BLE-radio impliciet bij eerste gebruik, maar deze kan ook expliciet worden bestuurd metaioble.config()(die doorstuurt naarbluetooth.BLE.config()nadat is verzekerd dat de radio aanstaat) en worden uitgeschakeld metaioble.stop().Een of meer rollen tegelijk in de lucht. Aan de peripheral-zijde: een geregistreerde set GATT-services (zie
aioble.register_services()) en een draaiendeaioble.advertise()-coroutine. Aan de central-zijde: een draaiendeaioble.scan()-iterator of een in behandeling zijndeaioble.Device.connect(). De radio multiplext het werk; de toepassing ziet elke rol als een onafhankelijke taak.
11.8.3. Een minimale peripheral¶
Het kleinste bruikbare aioble-programma – een peripheral die een enkele alleen-lezen characteristic adverteert – is kort:
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())
Een central die niets meer doet dan verbinden en één keer lezen is even kort:
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())
Beide programma’s zijn ongeveer vijftien regels en ze dekken de hele stroom van “radio is uit” tot “nuttig werk gedaan”.
11.8.4. De radio uitzetten¶
Op een camera op batterijvoeding is de BLE-radio de grootste discretionaire belasting van het budget. Twee knoppen doen ertoe.
De eerste is impliciet: aioble activeert de radio bij eerste gebruik, en de radio slaapt automatisch tussen geplande gebeurtenissen door (advertentiebursts, verbindingsgebeurtenissen, scanvensters). Het kiezen van langere intervallen bij aioble.advertise() / aioble.scan() en het overeenkomen van een langer verbindingsinterval bij connect() houdt de radio proportioneel langer uit. De advertentietabel in Adverteren en scannen is hier de praktische gids.
De tweede is expliciete uitschakeling:
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() deactiveert de onderliggende BLE-radio en breekt alles af wat in de lucht is – open verbindingen vallen weg, scanners en adverteerders annuleren, L2CAP-kanalen sluiten. Coroutines die op die bewerkingen stonden te wachten, werpen hun gebruikelijke exceptions (DeviceDisconnectedError en aanverwanten), wat het opruimmechanisme is waarvoor de omringende async with-blokken zijn geschreven. Het aanroepen van een aioble-coroutine daarna activeert de radio opnieuw vanuit koude staat.
Het typische patroon voor een periodieke sensorcamera op batterijvoeding is:
Ontwaak volgens een schema (timer, bewegingssensor, knop).
Voer de burst van BLE-werk uit – adverteer, accepteer een verbinding, push de waarde, verbreek.
Roep
aioble.stop()aan en slaap tot het volgende ontwaken.
11.8.5. Wat aioble niet doet¶
aioble dekt bewust GATT, GAP en L2CAP – de lagen die een toepassing gebruikt. Drie onderdelen vallen buiten het bereik:
Alles onder de linklaag. Kanaalselectie, frequency hopping, pakketbevestigingen en versleuteling op linklaagniveau gebeuren allemaal binnen de BLE-port en het siliconen van de controller;
aioblestelt geen hooks op dat niveau bloot.Classic Bluetooth.
aiobleis uitsluitend BLE. Audioverbindingen, RFCOMM, A2DP en andere classic-profiel-functies maken geen deel uit van de API.Bluetooth Mesh. De mesh-netwerklaag van de Bluetooth SIG (een aparte stack bovenop BLE-advertising) is niet geïmplementeerd op de camera. De camera kan adverteren en observeren, maar kan niet deelnemen aan de relay- / friend- / proxy-rollen van een mesh-netwerk.
11.8.6. Exceptions¶
Er komen vier exception-types uit aioble. Elk vuurt af vanuit een coroutine die op een bewerking stond te wachten toen er iets misging; async with-blokken wikkelen zich netjes af wanneer ze propageren.
aioble.DeviceDisconnectedError– de BLE-link naar de peer viel weg terwijl een GATT-bewerking (read,write,notified,indicated,subscribe,exchange_mtu, …) in de lucht was. Geworpen binnen welke coroutine ook stond te wachten. Verreweg de meest voorkomende exception; vang hem in alle code die opnieuw moet verbinden bij verlies.aioble.GattError– een GATT-bewerking bereikte de peer maar voltooide met een ATT-status ongelijk aan nul (write-with-response geweigerd, indicate niet bevestigd, read-not-permitted, …). De statuscode staat op het_status-attribuut van de exception.aioble.L2CAPDisconnectedError– het L2CAP-kanaal viel weg terwijl eensend(),recvinto()offlush()in de lucht was. Een van beide zijden kan het kanaal hebben gesloten, of de onderliggende GAP-verbinding viel weg.aioble.L2CAPConnectionError– geworpen doorl2cap_connect()wanneer de luisteraar weigerde of de controller de kanaalopzet liet mislukken. De Bluetooth-statuscode is het eerste positionele argument.
Bewerkingen die een expliciete timeout_ms accepteren (de connect- / discovery- / read- / write- / pair-aanroepen, plus timeout() als wrapper) werpen daarnaast asyncio.TimeoutError uit asyncio wanneer de deadline verstrijkt voordat de bewerking voltooid is.