11.6. Serviços e características

Assim que o GAP estabelece uma ligação aberta entre dois dispositivos, a camada acima — o Generic Attribute Profile, GATT — tem de dar significado aos bytes que fluem através dessa ligação. A opção do BLE neste ponto é invulgar. Onde o TCP expõe um fluxo de bytes brutos e deixa à aplicação a tarefa de inventar o seu próprio enquadramento, o GATT expõe uma pequena base de dados chave/valor que um lado aloja e o outro lê, escreve ou subscreve.

Essa base de dados é o que os criadores de aplicações passam a maior parte do seu tempo de desenvolvimento BLE a pensar. O que a câmara publica para um telemóvel, o que ela observa num sensor remoto, como um teclado Bluetooth informa o seu anfitrião de qual tecla foi premida — tudo são valores de características em alguma base de dados GATT.

11.6.1. Dois eixos de funções, não um

Uma fonte frequente de confusão: periférico / central e servidor / cliente são dois eixos independentes, não sinónimos.

  • Periférico e central são funções GAP, definidas durante a ligação. O periférico anuncia-se e é conectado; o central faz a pesquisa e inicia a ligação. Isto fica estabelecido no momento em que a ligação é criada e não muda.

  • Servidor e cliente são funções GATT, definidas por operação de característica. O servidor aloja a característica; o cliente lê, escreve ou subscreve a ela.

Os dois eixos são independentes segundo a especificação. Um periférico é normalmente o servidor (uma pulseira de frequência cardíaca publica as suas leituras) e um central é normalmente o cliente (um telemóvel lê-as), mas o BLE permite qualquer combinação — um periférico pode descobrir uma característica no central ao qual acabou de se ligar, ou uma única ligação pode alojar serviços em ambos os lados em simultâneo.

A maioria das aplicações de câmara segue o emparelhamento convencional (periférico + servidor, ou central + cliente), pelo que o restante desta secção os trata como um único eixo quando é o caso convencional que está a ser descrito. Quando a distinção importa, ambos os termos são explicitados.

11.6.2. Dentro da base de dados

Uma base de dados GATT é uma árvore. As folhas transportam os bytes reais. Os ramos agrupam folhas relacionadas em unidades com significado humano.

A tree with a top node labelled "GATT database". Below it, three Service nodes labelled "Generic Access (0x1800)", "Battery (0x180F)", and "Environmental Sensing (0x181A)". Each Service has child Characteristic nodes; the Battery service has "Battery Level (0x2A19)" with a child Descriptor "CCCD". The Environmental Sensing service has "Temperature (0x2A6E)" and "Humidity (0x2A6F)".

Uma base de dados GATT. Os serviços agrupam características; as características transportam os bytes da aplicação; os descritores transportam metadados sobre a característica.

Existem três tipos de nó:

  • Um serviço é um agrupamento lógico de valores relacionados. O Bluetooth SIG publica definições de serviços padrão para casos de uso comuns — Battery Service para nível de bateria, Environmental Sensing para temperatura / humidade / pressão, Heart Rate para monitores de frequência cardíaca — para que uma aplicação genérica num telemóvel possa reconhecer um serviço que nunca viu antes. Uma aplicação também pode definir os seus próprios serviços para coisas que o SIG não padronizou.

  • Uma característica é um valor nomeado dentro de um serviço. O Battery Service tem uma única característica — Battery Level, uma percentagem de um byte. O Environmental Sensing tem características separadas para temperatura, humidade, pressão, e assim por diante. Uma característica é a unidade das operações GATT — lê-se uma característica, escreve-se numa característica, subscreve-se uma característica.

  • Um descritor é metadados associados a uma característica. Alguns descritores são padronizados — o Client Characteristic Configuration Descriptor (CCCD) é o mais conhecido, pois escrever nele é a forma como um cliente informa o servidor «envia-me notificações sobre esta característica». Outros são definidos pelo utilizador e transportam informações como o formato de apresentação ou propriedades alargadas.

Um servidor GATT (tipicamente o periférico) declara a sua base de dados uma vez na inicialização e a base de dados não muda durante a execução. Um cliente GATT (tipicamente o central) descobre o que está na base de dados após a ligação — percorrendo a árvore, lendo os UUIDs dos serviços que encontra e depois as características dentro de cada um.

11.6.3. UUIDs

Cada serviço, característica e descritor tem um UUID (Universally Unique IDentifier) que identifica que tipo de coisa é. Os UUIDs têm três tamanhos:

  • 16 bits. Reservados para padrões definidos pelo Bluetooth SIG. O Battery Service é 0x180F. O Battery Level (uma característica) é 0x2A19. A lista completa está publicada no site de números atribuídos do Bluetooth SIG em https://www.bluetooth.com/specifications/assigned-numbers/.

  • 32 bits. Um nível intermédio raramente utilizado.

  • 128 bits. O que todos os outros utilizam — um fornecedor ou aplicação gera um aleatoriamente e usa-o para o seu serviço ou característica personalizado. As câmaras que definem o seu próprio protocolo vivem aqui.

A classe bluetooth.UUID aceita qualquer um dos três tamanhos:

import bluetooth

BATTERY_SERVICE = bluetooth.UUID(0x180F)
CUSTOM_SERVICE = bluetooth.UUID("12345678-1234-5678-9abc-def012345678")

Um UUID de 16 bits codifica num pequeno payload de publicidade, o que é uma razão pela qual os serviços padrão são preferíveis quando um existe — uma pulseira de frequência cardíaca que anuncia 0x180D (Heart Rate) custa dois bytes; um UUID personalizado custa dezasseis. Para aplicações que não necessitam de interoperabilidade padrão, um UUID de 128 bits gerado é a resposta certa.

11.6.4. O que os serviços padronizados pelo SIG oferecem

O argumento para usar um serviço padrão é simples: as aplicações existentes já sabem como comunicar com ele. Um dispositivo que anuncia o serviço Heart Rate (0x180D) e expõe a característica Heart Rate Measurement (0x2A37) funciona com todas as aplicações de fitness do planeta sem que ninguém escreva novo código. Um dispositivo que reimplementa os mesmos dados com UUIDs personalizados precisa da sua própria aplicação complementar e do seu próprio documento de protocolo.

Os padrões têm um custo. Os formatos de bytes dentro de cada característica são especificados — o SIG decidiu que o Heart Rate Measurement é um campo de flags de um byte seguido de um valor de frequência cardíaca de 8 ou 16 bits, opcionalmente seguido de intervalos R-R — e um dispositivo conforme tem de seguir esses formatos. Os serviços personalizados estão livres dessa restrição.

A resposta pragmática para câmaras: use o serviço padrão quando existir um para o tipo de dados que tem (Battery Service, Environmental Sensing), e defina um personalizado com um UUID de 128 bits para qualquer coisa específica da sua aplicação.

11.6.5. Objetos do lado do servidor e do lado do cliente

Para os mesmos blocos de construção conceptuais (serviço, característica, descritor), cada biblioteca GATT expõe dois conjuntos paralelos de objetos: