11.8. Модуль aioble¶
Специфікація Bluetooth Core дає словник, що відображається на два модулі MicroPython.
bluetooth— низькорівневе прив’язування до BLE-контролера. Синхронне, керується подіями через зворотний виклик у стилі IRQ, структуроване навколо байтових буферів, дескрипторів і базових примітивів GATT. Воно відкриває протокол таким, яким він є, а не таким, яким Python-застосунки хотіли б його споживати.aioble— обгортка вищого рівня, написана на Python поверхbluetooth, яка перетворює кожну віддалену операцію на корутинуasyncio, а кожен BLE-об’єкт (сервіси, характеристики, з’єднання, результати сканування, канали L2CAP) — на зручний Python-клас. Сканування стають асинхронними ітераторами; з’єднання — асинхронними контекстними менеджерами; сповіщення — очікуваними.
11.8.1. Коли звертатися до низькорівневого модуля¶
bluetooth залишається правильною відповіддю для двох вузьких випадків:
Ви пишете код того типу, з якого побудований
aioble— новий шаблон, що потребує керування протоколом на рівні IRQ.Ви працюєте на апаратній платформі, де пакет
aiobleнедоступний, і тонка обгортка навколо контролера — єдиний варіант.
Для будь-якого застосунку камери aioble — правильна відповідь.
11.8.2. Частини програми aioble¶
Кожен застосунок на основі aioble має невеликий набір складових частин, незалежно від ролей, які він виконує.
Тривалий цикл подій
asyncio. Все вaiobleє корутиною, тому застосунок структурований як одна або більше задач на одному циклі подій. Дивіться Asyncio для деталей про цикл, задачі та винятки.Увімкнене радіо.
aiobleактивує BLE-радіо неявно при першому використанні, але ним також можна керувати явно за допомогоюaioble.config()(який перенаправляє доbluetooth.BLE.config()після забезпечення включення радіо) і вимикати за допомогоюaioble.stop().Одна або більше ролей одночасно. На стороні периферійного пристрою: зареєстрований набір GATT-сервісів (див.
aioble.register_services()) і запущена корутинаaioble.advertise(). На стороні центрального пристрою: запущений ітераторaioble.scan()або незавершенийaioble.Device.connect(). Радіо мультиплексує роботу; застосунок бачить кожну роль як незалежну задачу.
11.8.3. Мінімальний периферійний пристрій¶
Найменша корисна програма aioble — периферійний пристрій, що рекламує єдину характеристику лише для читання — коротка:
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())
Центральний пристрій, що робить не більше ніж підключається і читає один раз, також короткий:
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())
Обидві програми займають близько п’ятнадцяти рядків і охоплюють весь потік від «радіо вимкнено» до «корисна робота виконана».
11.8.4. Вимкнення радіо¶
У камері з живленням від акумулятора BLE-радіо є найбільшим невимушеним споживачем енергії. Важливі два регулятори.
Перший — неявний: aioble активує радіо при першому використанні, а радіо автоматично спить між запланованими подіями (сплески реклами, події з’єднання, вікна сканування). Вибір довших інтервалів у aioble.advertise() / aioble.scan() і погодження довшого інтервалу з’єднання під час connect() пропорційно збільшує час, коли радіо вимкнено. Таблиця реклами в Рекламування та сканування є практичним посібником тут.
Другий — явне вимкнення:
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() деактивує базове BLE-радіо і руйнує все активне — відкриті з’єднання розриваються, сканери та рекламодавці скасовуються, канали L2CAP закриваються. Корутини, які очікували на ці операції, генерують свої звичайні винятки (DeviceDisconnectedError та аналогічні), що є механізмом очищення, для якого написані оточуючі блоки async with. Виклик будь-якої корутини aioble після цього активує радіо знову з нуля.
Типова схема для датчикової камери з переодичним живленням від акумулятора:
Пробудження за розкладом (таймер, датчик руху, кнопка).
Виконання пакету BLE-роботи — реклама, прийняття з’єднання, надсилання значення, від’єднання.
Виклик
aioble.stop()і сон до наступного пробудження.
11.8.5. Що aioble не робить¶
aioble навмисно охоплює GATT, GAP і L2CAP — рівні, які використовує застосунок. Три речі виходять за межі його відповідальності:
Все нижче канального рівня. Вибір каналу, переключення частот, підтвердження пакетів і шифрування канального рівня — всі ці процеси відбуваються всередині BLE-порту і кремнію контролера;
aiobleне надає гачки на цьому рівні.Класичний Bluetooth.
aioble— лише BLE. Аудіозв’язки, RFCOMM, A2DP та інші функції класичних профілів не є частиною API.Bluetooth Mesh. Рівень мережевої маршрутизації Bluetooth SIG (окремий стек поверх BLE-реклами) не реалізовано на камері. Камера може рекламувати і спостерігати, але не може брати участь у ролях ретрансляції/друга/проксі мережі Mesh.
11.8.6. Винятки¶
З aioble виходять чотири типи винятків. Кожен з них генерується всередині корутини, яка очікувала операцію, коли щось пішло не так; блоки async with розкручуються чисто при їх поширенні.
aioble.DeviceDisconnectedError— BLE-зв’язок з партнером обірвався під час виконання GATT-операції (read,write,notified,indicated,subscribe,exchange_mtu, …). Генерується всередині тієї корутини, яка очікувала. Найпоширеніший виняток; перехоплюйте його в будь-якому коді, що має перепідключатися при втраті з’єднання.aioble.GattError— GATT-операція дісталася партнера, але завершилася з ненульовим статусом ATT (запис із відповіддю відхилено, індикацію не підтверджено, читання заборонено, …). Код статусу знаходиться в атрибуті_statusвинятку.aioble.L2CAPDisconnectedError— канал L2CAP розірвався під час очікуванняsend(),recvinto()абоflush(). Будь-яка зі сторін могла закрити канал, або базове GAP-з’єднання припинилося.aioble.L2CAPConnectionError— генеруєтьсяl2cap_connect(), коли слухач відмовив або контролер не зміг налаштувати канал. Код статусу Bluetooth є першим позиційним аргументом.
Операції, що приймають явний timeout_ms (виклики connect/discovery/read/write/pair, плюс timeout() як обгортка), додатково генерують asyncio.TimeoutError з asyncio, коли дедлайн минає до завершення операції.