11.14. Conclusão¶
Percorreu o Bluetooth Low Energy desde o rádio até à API Python utilizada para o controlar:
A motivação – O BLE é a resposta quando a câmara quer falar com algo próximo sem qualquer infraestrutura entre eles. Um telemóvel na mesma sala, um wearable no pulso, um beacon numa parede. Curto alcance, sem rede a que se juntar, quase sem consumo de energia.
O rádio – 2,4 GHz, 40 canais: três para publicidade, 37 para dados de ligação, com saltos numa sequência pseudo-aleatória com evitamento adaptativo de canais com ruído. Pacotes breves, rádios maioritariamente em repouso.
A camada de ligação – enquadramento de pacotes, endereçamento, agendamento de ligações, retransmissão e encriptação ao nível da camada de ligação. Nada disto é configurado a partir de Python; tudo isso transparece nos parâmetros de ligação e no MTU.
Perfil de Acesso Genérico (GAP) – descoberta e gestão de ligações. Quatro papéis: periférico e emissor (publicam), central e observador (examinam). As cargas de publicidade transportam o nome local, UUIDs de serviço, aparência e dados específicos do fabricante – 31 bytes mais uma resposta de scan opcional de 31 bytes. O intervalo de ligação, a latência do periférico e o tempo limite de supervisão controlam o comportamento de uma ligação aberta.
Perfil de Atributos Genéricos (GATT) – uma árvore de serviços, cada um contendo características, cada uma opcionalmente contendo descritores, identificados por UUIDs (16 bits para os padrões Bluetooth-SIG, 128 bits para os personalizados). Cinco operações: read e write (pull, iniciadas pelo cliente), notify e indicate (push, iniciadas pelo servidor, subscritas através do Descritor de Configuração de Característica do Cliente). O tamanho da carga útil é limitado pelo MTU negociado.
A API Python – o
aiobletransforma cada padrão BLE numa corrotina asyncio. Um periférico éaioble.advertise()a iterar sobre ligações, com objectosService/Characteristicconstruídos uma vez e confirmados poraioble.register_services(). Uma central usaaioble.scan()para encontrar um par,connect()para abrir a ligação,service()echaracteristic()para percorrer a árvore GATT remota, e depoisread()/write()/subscribe()/notified()para os dados reais. As desconexões surgem comoaioble.DeviceDisconnectedErrordentro da corrotina que estava à espera.Canais L2CAP – a saída de emergência para fluxos de bytes em volume que não se enquadram no modelo chave/valor do GATT.
aioble.DeviceConnection.l2cap_accept()/l2cap_connect()abrem um canal por aplicação sobre a ligação GAP, com envio/recepção controlados por fluxo de créditos e um MTU maior do que o GATT consegue transportar.Emparelhamento e encriptação – as ligações BLE são públicas por predefinição.
aioble.DeviceConnection.pair()inicia uma troca de chaves que produz uma ligação encriptada;bond=True(a predefinição) persiste as chaves para que as ligações subsequentes saltem o handshake. Semmitm=Truee uma capacidade de IO utilizável, a encriptação protege contra intrusos passivos mas não contra um redirecionamento activo durante o emparelhamento original.
Isso é suficiente para escrever aplicações de câmara que publicam estado como periférico, lêem dados de sensor como central, enviam valores em tempo real para um telemóvel via BLE, protegem a ligação com um passo de emparelhamento e vinculação, e – para o caso raro de transferência em volume – saem do GATT para um canal L2CAP.
11.14.1. Resolução de problemas¶
As falhas do BLE são principalmente discrepâncias entre o que os dois lados esperam, e um inspector no lado do telemóvel é a forma mais rápida de ver de quem são as expectativas erradas. A ferramenta padrão é o nRF Connect for Mobile (Nordic Semiconductor, gratuito para Android e iOS): examina, liga, percorre a base de dados GATT, lê e escreve características, e subscreve notificações – para que o comportamento do lado da câmara possa ser testado isoladamente, sem escrever uma aplicação complementar de todo.
Os modos de falha comuns:
«O meu dispositivo aparece no scanner mas não liga.» Na maior parte das vezes o pacote de publicidade tem
connectable=False(modo emissor), ou uma ligação anterior ainda está aberta e a câmara já passouaioble.advertise(). Adicione declarações de impressão em torno da chamada de publicidade para confirmar.«exchange_mtu(512) foi executado mas as minhas notificações ainda estão limitadas a 20 bytes.» O MTU negociado é
min(local, peer)– o telemóvel ou a biblioteca central pode não ter pedido um MTU maior do seu lado, caso em que a ligação permanece em 23. Inspeccionemtuapós o retorno deexchange_mtu(). Note também queexchange_mtu()só funciona uma vez por ligação; chame-o antes da primeira operação de grande dimensão.«O emparelhamento falha com um erro genérico.» Dois culpados habituais: a incompatibilidade de capacidade de IO (pedir
mitm=Truenuma câmara que declaraio=3/ sem entrada sem saída – não há forma de confirmar o código numérico, portanto o motor de emparelhamento abandona), e uma hora de relógio de parede completamente errada na câmara quando o par a requer. Defina o relógio comntptime.settime()antes da primeira tentativa de emparelhamento.«As notificações nunca chegam ao cliente.» Duas coisas a verificar, por ordem: (a) foi a característica declarada com
notify=True? – o bit de propriedade tem de estar definido no lado do servidor; (b) o cliente chamousubscribe()? – sem escrever o Descritor de Configuração de Característica do Cliente (CCCD), o servidor é informado de que nenhum cliente quer notificações e descarta-as silenciosamente.«O nome publicado está truncado ou em falta.» A carga de publicidade tem 31 bytes, e os campos de flags + UUID de serviço + aparência consomem cada um bytes no topo. Um
name=longo mais vários UUIDs de serviço transborda. Encurte o nome ou use scanning activo para que a resposta de scan (outros 31 bytes) transporte o excesso. O nRF Connect mostra ambas as metades separadamente, o que torna a divisão óbvia.«L2CAP connect levanta imediatamente.» Normalmente uma discrepância de PSM – ambos os lados têm de concordar no mesmo número de PSM fora de banda. Um
L2CAPConnectionErrortransporta o código de estado Bluetooth como primeiro argumento; o estado2(«PSM não suportado») é o sinal revelador.«As ligações vinculadas ainda desencadeiam um handshake de emparelhamento completo a cada reconexão.»
aioble.security.load_secrets()não foi chamado na inicialização. Sem isso, as chaves guardadas estão em flash mas nunca são carregadas para memória, pelo que a identidade do par é desconhecida e o emparelhamento é executado de raiz de cada vez.
Quando tudo o resto falha, o módulo de nível inferior bluetooth expõe um callback IRQ que dispara para cada evento subjacente; subscrever a ele brevemente e imprimir os eventos é o equivalente a um rastreio Wireshark para o lado da câmara.
11.14.2. Utilizar esta referência mais tarde¶
Trate os capítulos sobre Bluetooth como material de referência; consultar a disposição exacta de uma carga de publicidade de periférico ou o fluxo de scan-e-subscrição central é a utilização pretendida. As páginas de referência aioble — BLE Assíncrono e bluetooth — Bluetooth de baixo nível listam cada método, flag e constante num único local quando a questão é apenas «qual é o nome exacto desta chamada».