11.8. O módulo aioble¶
A especificação Bluetooth Core fornece um vocabulário que se mapeia em dois módulos MicroPython.
bluetooth– a ligação de baixo nível ao controlador BLE. Síncrono, orientado a eventos através de uma callback no estilo IRQ, e estruturado em torno de buffers de bytes, handles e as primitivas GATT básicas. Expõe o protocolo tal como é, não como as aplicações Python o querem consumir.aioble– um invólucro de nível superior, escrito em Python sobrebluetooth, que transforma cada operação remota numa corrotinaasyncioe cada objeto BLE (serviços, características, ligações, resultados de scan, canais L2CAP) numa classe Python ergonómica. Os scans tornam-se iteradores assíncronos; as ligações tornam-se gestores de contexto assíncronos; as notificações tornam-se aguardáveis.
11.8.1. Quando usar o módulo de nível inferior¶
bluetooth continua a ser a resposta certa em dois casos restritos:
Está a escrever o tipo de código com que
aiobleé construído – um novo padrão que precisa de controlo ao nível de IRQ sobre o protocolo.Está a correr numa plataforma de hardware onde o pacote
aioblenão está disponível, e um invólucro fino em torno do controlador é a única opção.
Para todas as aplicações de câmara, aioble é a resposta certa.
11.8.2. Componentes de um programa aioble¶
Cada aplicação baseada em aioble tem um pequeno conjunto de partes móveis, independentemente dos papéis que desempenha.
Um ciclo de eventos
asynciode longa duração. Tudo emaiobleé uma corrotina, pelo que a aplicação é estruturada como uma ou mais tarefas num único ciclo de eventos. Consulte Asyncio para detalhes sobre o ciclo, tarefas e exceções.Um rádio ligado.
aiobleativa o rádio BLE implicitamente no primeiro uso, mas também pode ser controlado explicitamente comaioble.config()(que encaminha parabluetooth.BLE.config()após garantir que o rádio está ativo) e desligado comaioble.stop().Um ou mais papéis em execução simultânea. No lado periférico: um conjunto registado de serviços GATT (consulte
aioble.register_services()) e uma corrotinaaioble.advertise()em execução. No lado central: um iteradoraioble.scan()em execução ou umaioble.Device.connect()pendente. O rádio multiplexa o trabalho; a aplicação vê cada papel como uma tarefa independente.
11.8.3. Um periférico mínimo¶
O programa aioble mais pequeno e útil – um periférico que publica uma única característica só de leitura – é curto:
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())
Um central que não faz mais do que ligar e ler uma vez é igualmente curto:
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())
Ambos os programas têm cerca de quinze linhas e cobrem todo o fluxo de «rádio desligado» até «trabalho útil concluído».
11.8.4. Desligar o rádio¶
Numa câmara a bateria, o rádio BLE é o maior consumo discricionário do orçamento. Dois parâmetros importam.
O primeiro é implícito: aioble ativa o rádio no primeiro uso, e o rádio dorme entre os eventos agendados (rajadas de publicidade, eventos de ligação, janelas de scan) automaticamente. Escolher intervalos mais longos em aioble.advertise() / aioble.scan() e acordar um intervalo de ligação mais longo no momento de connect() mantém o rádio desligado durante uma proporção maior do tempo. A tabela de publicidade em Publicidade e pesquisa é o guia prático aqui.
O segundo é o desligamento explícito
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() desativa o rádio BLE subjacente e derruba tudo em curso – ligações abertas caem, scanners e anunciantes cancelam, canais L2CAP fecham. Corrotinas que estavam à espera nessas operações levantam as suas exceções habituais (DeviceDisconnectedError e semelhantes), que é o mecanismo de limpeza para o qual os blocos async with circundantes foram escritos. Chamar qualquer corrotina aioble depois disso ativa o rádio novamente a partir do zero.
O padrão típico para uma câmara sensor alimentada a bateria e periódica é:
Acordar num horário (temporizador, sensor de movimento, botão).
Executar a rajada de trabalho BLE – publicitar, aceitar uma ligação, enviar o valor, desligar.
Chamar
aioble.stop()e dormir até ao próximo acordar.
11.8.5. O que o aioble não faz¶
aioble cobre deliberadamente GATT, GAP e L2CAP – as camadas que uma aplicação usa. Três componentes estão fora do âmbito:
Qualquer coisa abaixo da camada de ligação. A seleção de canais, o salto de frequência, os reconhecimentos de pacotes e a encriptação da camada de ligação acontecem todos dentro da porta BLE e do silício do controlador;
aioblenão expõe ganchos a esse nível.Bluetooth Clássico.
aiobleé exclusivamente BLE. Ligações de áudio, RFCOMM, A2DP e outras funcionalidades de perfis clássicos não fazem parte da API.Bluetooth Mesh. A camada de redes em malha do Bluetooth SIG (uma pilha separada sobre publicidade BLE) não está implementada na câmara. A câmara pode publicitar e observar, mas não pode participar nos papéis de retransmissão / amigo / proxy de uma rede em malha.
11.8.6. Exceções¶
Quatro tipos de exceção provêm de aioble. Cada um é lançado dentro de uma corrotina que estava a aguardar uma operação quando algo correu mal; os blocos async with desenrolam-se de forma limpa quando se propagam.
aioble.DeviceDisconnectedError– a ligação BLE ao par caiu enquanto uma operação GATT (read,write,notified,indicated,subscribe,exchange_mtu, …) estava em curso. Levantado dentro de qualquer corrotina que estava à espera. A exceção mais comum de longe; capturá-la em qualquer código que deva reconectar após perda.aioble.GattError– uma operação GATT chegou ao par mas completou com um estado ATT não nulo (escrita com resposta rejeitada, indicação não reconhecida, leitura não permitida, …). O código de estado está no atributo_statusda exceção.aioble.L2CAPDisconnectedError– o canal L2CAP caiu enquanto umsend(),recvinto(), ouflush()estava em curso. Qualquer um dos lados pode ter fechado o canal, ou a ligação GAP subjacente desapareceu.aioble.L2CAPConnectionError– levantado porl2cap_connect()quando o ouvinte recusou ou o controlador falhou na configuração do canal. O código de estado Bluetooth é o primeiro argumento posicional.
As operações que têm um timeout_ms explícito (as chamadas de ligação / descoberta / leitura / escrita / emparelhamento, mais timeout() como invólucro) levantam adicionalmente asyncio.TimeoutError de asyncio quando o prazo expirar antes de a operação completar.