11.8. Moduł aioble¶
Specyfikacja Bluetooth Core dostarcza słownictwa, które odwzorowuje się na dwa moduły MicroPython.
bluetooth– niskopoziomowe powiązanie z kontrolerem BLE. Synchroniczne, sterowane zdarzeniami poprzez wywołanie zwrotne w stylu IRQ, zbudowane wokół buforów bajtowych, uchwytów i nagich prymitywów GATT. Eksponuje protokół takim, jaki jest, a nie takim, jakim chcą go konsumować aplikacje Python.aioble– wyżej poziomowa nakładka, napisana w Pythonie na baziebluetooth, która zamienia każdą zdalną operację w korutynęasyncio, a każdy obiekt BLE (usługi, charakterystyki, połączenia, wyniki skanowania, kanały L2CAP) w ergonomiczną klasę Pythona. Skanowania stają się asynchronicznymi iteratorami; połączenia stają się asynchronicznymi menedżerami kontekstu; powiadomienia stają się oczekiwalne.
11.8.1. Kiedy sięgnąć po moduł niższego poziomu¶
bluetooth jest nadal właściwym wyborem w dwóch wąskich przypadkach:
Piszesz rodzaj kodu, z którego zbudowany jest sam
aioble– nowy wzorzec wymagający kontroli nad protokołem na poziomie IRQ.Działasz na docelowym sprzęcie, na którym pakiet
aioblenie jest dostępny, a cienka warstwa pośrednicząca wokół kontrolera jest jedyną opcją.
Dla każdej aplikacji kamery właściwą odpowiedzią jest aioble.
11.8.2. Elementy programu aioble¶
Każda aplikacja oparta na aioble ma niewielki zestaw ruchomych części, niezależnie od tego, jakie role pełni.
Długo działająca pętla zdarzeń
asyncio. Wszystko waioblejest korutyną, więc aplikacja jest zbudowana jako jedno lub więcej zadań na pojedynczej pętli zdarzeń. Szczegółowe informacje o pętli, zadaniach i wyjątkach znajdziesz w Asyncio.Włączone radio.
aiobleaktywuje radio BLE niejawnie przy pierwszym użyciu, ale można je też kontrolować jawnie za pomocąaioble.config()(która przekazuje wywołanie dobluetooth.BLE.config()po upewnieniu się, że radio jest włączone) i wyłączyć za pomocąaioble.stop().Jedna lub więcej ról działających jednocześnie. Po stronie urządzenia peryferyjnego: zarejestrowany zestaw usług GATT (zobacz
aioble.register_services()) i działająca korutynaaioble.advertise(). Po stronie urządzenia centralnego: działający iteratoraioble.scan()lub oczekująceaioble.Device.connect(). Radio multipleksuje pracę; aplikacja widzi każdą rolę jako niezależne zadanie.
11.8.3. Minimalne urządzenie peryferyjne¶
Najmniejszy użyteczny program aioble – urządzenie peryferyjne rozgłaszające pojedynczą charakterystykę tylko do odczytu – jest krótki:
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())
Urządzenie centralne, które nie robi nic więcej niż połączenie i jednorazowy odczyt, jest podobnie krótkie:
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ą około piętnastu linii i obejmują cały przepływ od „radio jest wyłączone” do „użyteczna praca wykonana”.
11.8.4. Wyłączanie radia¶
W kamerze zasilanej z baterii radio BLE jest największym uznaniowym obciążeniem budżetu. Znaczenie mają dwa pokrętła.
Pierwsze jest niejawne: aioble aktywuje radio przy pierwszym użyciu, a radio automatycznie usypia pomiędzy zaplanowanymi zdarzeniami (impulsami rozgłaszania, zdarzeniami połączeń, oknami skanowania). Wybranie dłuższych interwałów w aioble.advertise() / aioble.scan() oraz uzgodnienie dłuższego interwału połączenia w momencie connect() proporcjonalnie zwiększa czas wyłączenia radia. Praktycznym przewodnikiem jest tu tabela rozgłaszania w Rozgłaszanie i skanowanie.
Drugim jest jawne wyłączenie:
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() dezaktywuje leżące pod spodem radio BLE i likwiduje wszystko, co jest w trakcie – otwarte połączenia są zrywane, skanery i nadajniki rozgłaszające są anulowane, kanały L2CAP zamykane. Korutyny, które oczekiwały na te operacje, zgłaszają swoje zwykłe wyjątki (DeviceDisconnectedError i pokrewne), co jest mechanizmem sprzątania, dla którego napisano otaczające bloki async with. Wywołanie dowolnej korutyny aioble później ponownie aktywuje radio od zimnego stanu.
Typowy wzorzec dla okresowej, zasilanej z baterii kamery sensorowej to:
Wybudzenie według harmonogramu (licznik czasu (timer), sensor ruchu, przycisk).
Wykonanie serii pracy BLE – rozgłaszanie, przyjęcie połączenia, wypchnięcie wartości, rozłączenie.
Wywołanie
aioble.stop()i uśpienie do następnego wybudzenia.
11.8.5. Czego aioble nie robi¶
aioble celowo obejmuje GATT, GAP i L2CAP – warstwy, których używa aplikacja. Trzy elementy są poza zakresem:
Cokolwiek poniżej warstwy łącza. Wybór kanału, przeskakiwanie częstotliwości, potwierdzenia pakietów i szyfrowanie warstwy łącza dzieją się wewnątrz portu BLE i krzemu kontrolera;
aioblenie eksponuje haków na tym poziomie.Klasyczny Bluetooth.
aiobleobsługuje wyłącznie BLE. Łącza audio, RFCOMM, A2DP i inne funkcje profili klasycznych nie są częścią tego API.Bluetooth Mesh. Warstwa sieci kratowej Bluetooth SIG (osobny stos na wierzchu rozgłaszania BLE) nie jest zaimplementowana na kamerze. Kamera może rozgłaszać i obserwować, ale nie może uczestniczyć w rolach przekaźnika / przyjaciela / proxy sieci kratowej.
11.8.6. Wyjątki¶
Z aioble wychodzą cztery typy wyjątków. Każdy jest zgłaszany z wnętrza korutyny, która oczekiwała na operację, gdy coś poszło nie tak; bloki async with rozwijają się czysto, gdy się propagują.
aioble.DeviceDisconnectedError– łącze BLE do partnera zostało zerwane podczas trwania operacji GATT (read,write,notified,indicated,subscribe,exchange_mtu, …). Zgłaszany wewnątrz tej korutyny, która oczekiwała. Zdecydowanie najczęstszy wyjątek; przechwytuj go w każdym kodzie, który powinien wznawiać połączenie po jego utracie.aioble.GattError– operacja GATT dotarła do partnera, ale zakończyła się niezerowym statusem ATT (zapis z odpowiedzią odrzucony, wskazanie niepotwierdzone, odczyt niedozwolony, …). Kod statusu znajduje się w atrybucie_statuswyjątku.aioble.L2CAPDisconnectedError– kanał L2CAP został zerwany podczas trwaniasend(),recvinto()lubflush(). Kanał mogła zamknąć którakolwiek ze stron lub zniknęło leżące pod spodem połączenie GAP.aioble.L2CAPConnectionError– zgłaszany przezl2cap_connect(), gdy strona nasłuchująca odmówiła lub kontroler nie zdołał skonfigurować kanału. Kod statusu Bluetooth jest pierwszym argumentem pozycyjnym.
Operacje, które przyjmują jawne timeout_ms (wywołania connect / discovery / read / write / pair, a także timeout() jako nakładka), dodatkowo zgłaszają asyncio.TimeoutError z asyncio, gdy termin upłynie przed zakończeniem operacji.