11.8. Modulen aioble¶
Bluetooth Core-specifikationen ger ett vokabulär som mappar mot två MicroPython-moduler.
bluetooth– den lågnivå-bindning till BLE-styrenheten. Synkron, händelsedriven genom ett IRQ-liknande återanrop, och strukturerad kring bytebuffertar, handtag och de nakna GATT-primitiverna. Den exponerar protokollet som det är, inte som Python-applikationer vill konsumera det.aioble– en högre nivås omslutning, skriven i Python ovanpåbluetooth, som förvandlar varje fjärroperation till enasyncio-coroutine och varje BLE-objekt (tjänster, egenskaper, anslutningar, skanningsresultat, L2CAP-kanaler) till en ergonomisk Python-klass. Skanningar blir asynkrona iteratorer; anslutningar blir asynkrona kontexthanterare; notifieringar blir await-bara.
11.8.1. När man ska gripa efter lågnivåmodulen¶
bluetooth är fortfarande rätt svar för två snäva fall:
Du skriver den sorts kod som
aioblesjälv är byggd av – ett nytt mönster som behöver IRQ-nivåkontroll över protokollet.Du kör på ett hårdvarumål där paketet
aiobleinte är tillgängligt, och ett tunt skikt runt styrenheten är det enda alternativet.
För varje kameraapplikation är aioble rätt svar.
11.8.2. Delar av ett aioble-program¶
Varje aioble-baserad applikation har en liten uppsättning rörliga delar, oavsett vilka roller den spelar.
En långkörande
asyncio-händelseloop. Allt iaiobleär en coroutine, så applikationen är strukturerad som en eller flera uppgifter på en enda händelseloop. Se Asyncio för loopen, uppgifterna och undantagen i detalj.En radio som är på.
aiobleaktiverar BLE-radion implicit vid första användning, men den kan också styras explicit medaioble.config()(som vidarebefordrar tillbluetooth.BLE.config()efter att ha säkerställt att radion är uppe) och stängas ner medaioble.stop().En eller flera roller i luften samtidigt. På peripheral-sidan: en registrerad uppsättning GATT-tjänster (se
aioble.register_services()) och en körandeaioble.advertise()-coroutine. På central-sidan: en körandeaioble.scan()-iterator eller en väntandeaioble.Device.connect(). Radion multiplexar arbetet; applikationen ser varje roll som en oberoende uppgift.
11.8.3. En minimal peripheral¶
Det minsta användbara aioble-programmet – en peripheral som annonserar en enda skrivskyddad egenskap – är 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())
En central som inte gör mer än att ansluta och läsa en gång är likaledes 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())
Båda programmen är ungefär femton rader och de täcker hela flödet från ”radion är av” till ”nyttigt arbete gjort”.
11.8.4. Stänga av radion¶
På en batteridriven kamera är BLE-radion det största skönsmässiga uttaget på budgeten. Två rattar spelar roll.
Den första är implicit: aioble aktiverar radion vid första användning, och radion sover mellan schemalagda händelser (annonseringsskurar, anslutningshändelser, skanningsfönster) automatiskt. Att välja längre intervall på aioble.advertise() / aioble.scan() och komma överens om ett längre anslutningsintervall vid connect()-tillfället håller radion av proportionellt mer av tiden. Annonseringstabellen i Annonsering och skanning är den praktiska guiden här.
Den andra är explicit nedstängning:
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() avaktiverar den underliggande BLE-radion och river ner allt i luften – öppna anslutningar tappas, skannrar och annonsörer avbryts, L2CAP-kanaler stängs. Coroutiner som väntade på dessa operationer kastar sina vanliga undantag (DeviceDisconnectedError med flera), vilket är den uppstädningsmekanism som de omgivande async with-blocken skrevs för. Att anropa någon aioble-coroutine efteråt aktiverar radion igen från kallt.
Det typiska mönstret för en periodisk batteridriven sensorkamera är:
Vakna enligt ett schema (timer, rörelsesensor, knapp).
Kör BLE-arbetsskuren – annonsera, acceptera en anslutning, pusha värdet, koppla från.
Anropa
aioble.stop()och sov tills nästa uppvaknande.
11.8.5. Vad aioble inte gör¶
aioble täcker avsiktligt GATT, GAP och L2CAP – de lager en applikation använder. Tre delar ligger utanför omfånget:
Allt under länklagret. Kanalval, frekvenshoppning, paketbekräftelser och länklagerkryptering sker allt inuti BLE-porten och styrenhetens kisel;
aiobleexponerar inga krokar på den nivån.Klassisk Bluetooth.
aiobleär endast för BLE. Ljudlänkar, RFCOMM, A2DP och andra klassisk-profil-funktioner är inte en del av API:et.Bluetooth Mesh. Bluetooth SIG:s mesh-nätverkslager (en separat stack ovanpå BLE-annonsering) är inte implementerat på kameran. Kameran kan annonsera och observera, men den kan inte delta i ett mesh-nätverks roller som relä / vän / proxy.
11.8.6. Undantag¶
Fyra undantagstyper kommer ut ur aioble. Var och en avfyras inifrån en coroutine som inväntade en operation när något gick fel; async with-block rullas tillbaka snyggt när de propagerar.
aioble.DeviceDisconnectedError– BLE-länken till motparten tappades medan en GATT-operation (read,write,notified,indicated,subscribe,exchange_mtu, …) var i luften. Kastas inuti vilken coroutine som än väntade. Det överlägset vanligaste undantaget; fånga det i all kod som ska återansluta vid förlust.aioble.GattError– en GATT-operation nådde motparten men slutfördes med en ATT-status skild från noll (write-with-response avvisad, indicate inte bekräftad, read-not-permitted, …). Statuskoden finns på undantagets_status-attribut.aioble.L2CAPDisconnectedError– L2CAP-kanalen tappades medan ensend(),recvinto()ellerflush()var i luften. Någon sida kan ha stängt kanalen, eller så försvann den underliggande GAP-anslutningen.aioble.L2CAPConnectionError– kastas avl2cap_connect()när lyssnaren vägrade eller styrenheten misslyckades med kanaluppsättningen. Bluetooth-statuskoden är det första positionella argumentet.
Operationer som tar ett explicit timeout_ms (anropen för connect / discovery / read / write / pair, plus timeout() som ett omslag) kastar dessutom asyncio.TimeoutError från asyncio när tidsgränsen löper ut innan operationen slutförs.