11.8. Modulul aioble¶
Specificația Bluetooth Core oferă un vocabular care se mapează pe două module MicroPython.
bluetooth– legătura de nivel scăzut cu controlerul BLE. Sincronă, bazată pe evenimente printr-o funcție de retroapelare (callback) de tip IRQ și structurată în jurul tampoanelor (buffer) de octeți, al handle-urilor și al primitivelor GATT brute. Expune protocolul așa cum este, nu așa cum vor să-l consume aplicațiile Python.aioble– un wrapper de nivel superior, scris în Python deasuprabluetooth, care transformă fiecare operațiune la distanță într-o corutinăasyncioși fiecare obiect BLE (servicii, caracteristici, conexiuni, rezultate de scanare, canale L2CAP) într-o clasă Python ergonomică. Scanările devin iteratoare asincrone; conexiunile devin gestionare de context asincrone; notificările devin așteptabile.
11.8.1. Când să apelezi la modulul de nivel scăzut¶
bluetooth este în continuare răspunsul potrivit pentru două cazuri restrânse:
Scrii genul de cod din care este construit însuși
aioble– un model nou care necesită control la nivel IRQ asupra protocolului.Rulezi pe o țintă hardware unde pachetul
aioblenu este disponibil, iar o cojiță subțire în jurul controlerului este singura opțiune.
Pentru fiecare aplicație de cameră, aioble este răspunsul potrivit.
11.8.2. Componentele unui program aioble¶
Fiecare aplicație bazată pe aioble are un mic set de piese mobile, indiferent de rolurile pe care le joacă.
O buclă de evenimente
asynciode lungă durată. Totul înaiobleeste o corutină, deci aplicația este structurată ca una sau mai multe sarcini pe o singură buclă de evenimente. Consultă Asyncio pentru detalii despre buclă, sarcini și excepții.Un radio pornit.
aiobleactivează implicit radioul BLE la prima utilizare, dar acesta poate fi controlat și explicit cuaioble.config()(care transmite mai departe cătrebluetooth.BLE.config()după ce se asigură că radioul este pornit) și oprit cuaioble.stop().Unul sau mai multe roluri în desfășurare simultan. Pe partea periferică: un set înregistrat de servicii GATT (vezi
aioble.register_services()) și o corutinăaioble.advertise()în execuție. Pe partea centrală: un iteratoraioble.scan()în execuție sau unaioble.Device.connect()în așteptare. Radioul multiplexează lucrul; aplicația vede fiecare rol ca pe o sarcină independentă.
11.8.3. Un periferic minimal¶
Cel mai mic program aioble util – un periferic care face publicitate unei singure caracteristici doar pentru citire – este scurt:
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())
Un central care nu face nimic mai mult decât să se conecteze și să citească o dată este la fel de scurt:
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())
Ambele programe au în jur de cincisprezece linii și acoperă întregul flux de la „radioul este oprit” la „lucru util efectuat”.
11.8.4. Oprirea radioului¶
Pe o cameră alimentată cu baterie, radioul BLE este cel mai mare consumator discreționar din buget. Două manete contează.
Prima este implicită: aioble activează radioul la prima utilizare, iar radioul intră automat în repaus între evenimentele programate (explozii de publicitate, evenimente de conexiune, ferestre de scanare). Alegerea unor intervale mai lungi la aioble.advertise() / aioble.scan() și convenirea asupra unui interval de conexiune mai lung în momentul connect() menține radioul oprit proporțional mai mult timp. Tabelul de publicitate din Difuzare și scanare este ghidul practic aici.
A doua este oprirea explicită
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() dezactivează radioul BLE subiacent și demontează orice este în desfășurare – conexiunile deschise se întrerup, scanerele și agenții de publicitate se anulează, canalele L2CAP se închid. Corutinele care așteptau acele operațiuni generează excepțiile lor obișnuite (DeviceDisconnectedError și altele), care reprezintă mecanismul de curățare pentru care au fost scrise blocurile async with din jur. Apelarea oricărei corutine aioble ulterior activează din nou radioul de la rece.
Modelul tipic pentru o cameră de senzor periodică alimentată cu baterie este:
Trezire conform unui program (temporizator, senzor de mișcare, buton).
Rulează explozia de lucru BLE – fă publicitate, acceptă o conexiune, transmite valoarea, deconectează.
Apelează
aioble.stop()și intră în repaus până la următoarea trezire.
11.8.5. Ce nu face aioble¶
aioble acoperă în mod deliberat GATT, GAP și L2CAP – straturile pe care le folosește o aplicație. Trei componente sunt în afara domeniului de aplicare:
Orice se află sub stratul de legătură. Selecția canalelor, saltul de frecvență, confirmarea pachetelor și criptarea la nivelul stratului de legătură se întâmplă toate în interiorul portului BLE și al siliciului controlerului;
aioblenu expune cârlige la acel nivel.Bluetooth Classic.
aiobleeste exclusiv BLE. Legăturile audio, RFCOMM, A2DP și alte caracteristici de profil clasic nu fac parte din API.Bluetooth Mesh. Stratul de rețea mesh al Bluetooth SIG (o stivă separată deasupra publicității BLE) nu este implementat pe cameră. Camera poate face publicitate și poate observa, dar nu poate participa la rolurile de releu / prieten / proxy ale unei rețele mesh.
11.8.6. Excepții¶
Patru tipuri de excepții provin din aioble. Fiecare se declanșează din interiorul unei corutine care aștepta o operațiune când ceva a mers prost; blocurile async with se derulează în mod curat atunci când acestea se propagă.
aioble.DeviceDisconnectedError– legătura BLE către partener s-a întrerupt în timp ce o operațiune GATT (read,write,notified,indicated,subscribe,exchange_mtu, …) era în desfășurare. Generată în interiorul oricărei corutine care aștepta. De departe cea mai frecventă excepție; prinde-o în orice cod care ar trebui să se reconecteze în caz de pierdere.aioble.GattError– o operațiune GATT a ajuns la partener, dar s-a finalizat cu o stare ATT diferită de zero (scriere-cu-răspuns respinsă, indicare neconfirmată, citire-nepermisă, …). Codul de stare se află în atributul_statusal excepției.aioble.L2CAPDisconnectedError– canalul L2CAP s-a întrerupt în timp ce unsend(),recvinto()sauflush()era în desfășurare. Oricare dintre părți poate fi închis canalul, sau conexiunea GAP subiacentă a dispărut.aioble.L2CAPConnectionError– generată del2cap_connect()atunci când ascultătorul a refuzat sau controlerul a eșuat configurarea canalului. Codul de stare Bluetooth este primul argument pozițional.
Operațiunile care primesc un timeout_ms explicit (apelurile de conectare / descoperire / citire / scriere / împerechere, plus timeout() ca wrapper) generează în plus asyncio.TimeoutError din asyncio atunci când termenul limită expiră înainte ca operațiunea să se finalizeze.