
BLE sob um microscópio (ATTы GATTы…)
Parte 1, visão geral
Já se passou bastante tempo desde o lançamento da primeira especificação do Bluetooth 4.0. E embora o tema do BLE seja muito interessante, ele ainda afasta muitos desenvolvedores devido à sua complexidade. Em meus artigos anteriores, foquei principalmente no nível mais baixo — a Camada de Enlace e a Camada Física. Isso me permitiu evitar conceitos complexos e confusos, como o Protocolo de Atributos (ATP) e o Perfil de Atributos Genéricos (GATT). No entanto, sem entender esses conceitos, é impossível desenvolver dispositivos compatíveis. Hoje, gostaria de compartilhar esse conhecimento com vocês. Neste artigo, utilizarei... Para iniciantes, do site da Nordic. Então, vamos começar.
Por que tudo é tão complicado?
Na minha opinião, ficou imediatamente claro que controlar dispositivos via smartphones era um conceito muito promissor e de longo prazo. Portanto, decidimos estruturá-lo da forma mais completa possível desde o início. Isso visava impedir que os fabricantes de dispositivos desenvolvessem seus próprios protocolos, que posteriormente se tornariam incompatíveis. Isso explica a complexidade. Desde o princípio, tentamos incluir tudo o que fosse possível no protocolo BLE. E se isso seria útil posteriormente ou não, era irrelevante. Também previmos a possibilidade de expandir a lista de dispositivos no futuro.
Vamos analisar a imagem que mostra o diagrama do protocolo BLE. Ele consiste em várias camadas. A camada mais baixa, a camada física (PHY), é responsável pelo canal de rádio do dispositivo. A camada de enlace (LL) contém toda a sequência de bytes da mensagem transmitida. Estudamos essa camada em artigos anteriores. A Interface do Controlador Host (HCI) é o protocolo para comunicação entre as camadas ou chips BLE, caso o Controlador e o Host estejam implementados em chips diferentes. O Protocolo de Controle e Adaptação de Enlace Lógico (L2CAP) é responsável pela formação, enquadramento, controle de erros e remontagem de pacotes. O Protocolo de Gerenciamento de Segurança (SMP) é responsável pela criptografia de pacotes. O Perfil de Acesso Genérico (GAP) é responsável pela troca inicial de dados entre os dispositivos para determinar "quem é quem". A varredura e a publicidade também se enquadram nesse perfil. Neste artigo, focarei nas duas partes restantes do protocolo: GATT e ATT. O GATT é uma superestrutura do ATT, portanto, estão intimamente interligados.

Para simplificar a discussão, gostaria de usar uma analogia. Ouvi-a em algum lugar e gostaria de repeti-la. Imagine um dispositivo BLE como uma estante com várias prateleiras. Cada prateleira representa um tópico diferente. Por exemplo, temos prateleiras para ficção científica, matemática e enciclopédias. Cada prateleira contém livros sobre esse tópico. Alguns livros até têm marcadores de papel com anotações. Também temos um pequeno catálogo de papel com todos os livros. Se você se lembra das bibliotecas escolares, elas são como gavetas estreitas com fichas de papel. Nessa analogia, a estante é o perfil do nosso dispositivo. As prateleiras são os serviços, os livros são as características e o catálogo é uma tabela de atributos. Os marcadores nos livros são os descritores, que discutirei com mais detalhes posteriormente.
Qualquer pessoa que já tenha desenvolvido dispositivos sabe que muitos projetos contêm trechos de código semelhantes. Isso ocorre porque muitos dispositivos têm funcionalidades similares. Por exemplo, se os dispositivos são alimentados por bateria, o problema de carregá-los e monitorar seu nível será o mesmo. O mesmo se aplica aos sensores. Essencialmente, trata-se de uma abordagem orientada a objetos para programação. "Proporciona a capacidade de criar objetos que combinam propriedades e comportamentos em uma união autocontida que pode então ser reutilizada."Na minha opinião, o BLE tentou uma abordagem semelhante. O Bluetooth Special Interest Group (SIG) desenvolveu perfis. Dispositivos de diferentes fabricantes com perfis idênticos devem funcionar perfeitamente entre si. Os perfis, por sua vez, consistem em serviços, e os serviços consistem em características complementadas por descritores. Em geral, pode ser algo assim:

Por exemplo, vejamos o diagrama de perfil de um monitor de frequência cardíaca (pulseira fitness). Ele consiste em dois serviços e várias funcionalidades. A hierarquia do perfil fica imediatamente clara neste diagrama. A função de checkpoint redefine a contagem total de calorias gastas para zero.
1. O serviço de frequência cardíaca inclui três características (0x180D):
a) Característica obrigatória da frequência cardíaca (0x2A37)
b) Parâmetro opcional de posição do sensor de carroceria (0x2A38)
c) Característica condicional do ponto de controle da frequência cardíaca (0x2A39)
2. Serviço de manutenção da bateria (0x180F):
a) Característica obrigatória do nível da bateria (0x2A19)
UUID
Para que seja possível acessar elementos de perfil (serviços, características e descritores) de forma única, todos eles precisam ser numerados. Para isso, foi introduzido o conceito de Identificador Universalmente Único (UUID). O UUID é indicado entre parênteses em cada linha. E há uma peculiaridade aqui. Eles decidiram usar códigos de 16 e 128 bits para os UUIDs. Por quê? O protocolo BLE tem como foco a conservação de energia. Portanto, um código de 16 bits é bastante razoável. É improvável que mais de 65 serviços e características únicos sejam criados em um futuro próximo. A essa altura, tudo o que podia ser contado já foi contado (lembre-se de onde vem o ditado "você também contou" :-)). Elementos numerados , , и Você pode consultar os links.
No entanto, creio que todos se lembram da história do endereço IP de 4 bytes na internet. Inicialmente, pensaram que seria suficiente, mas ainda estamos lutando para migrar para um endereço de 6 bytes. Para evitar repetir esse erro e dar rédea solta às mãos maliciosas dos entusiastas do "faça você mesmo", o SIG decidiu imediatamente introduzir UUIDs de 128 bits. Isso me lembra da banda não licenciada de 433 MHz, que era usada para todos os tipos de gambiarras em canais de rádio. No nosso caso, recebemos um identificador de serviço e característica de 128 bits. Isso significa que podemos usar praticamente qualquer valor de 128 bits para nossos serviços e dispositivos. A probabilidade de todos chegarem ao mesmo UUID é praticamente nula.
Na verdade, UUIDs curtos de 16 bits possuem sua própria extensão para um valor de 128 bits. Na especificação, essa extensão é chamada de UUID Base do Bluetooth e tem o valor 00000000-0000-1000-8000-00805F9B34FB. Se, por exemplo, o UUID de 16 bits de um atributo tiver o valor 0x1234, o UUID equivalente de 128 bits teria o valor 00001234-0000-1000-8000-00805F9B34FB. A fórmula correspondente é inclusive fornecida:
Valor de 128 bits = valor de 16 bits * 2^96 + UUID_base_do_Bluetooth
Não sei de onde veio esse número mágico. Se algum leitor souber, por favor, escreva nos comentários (um usuário com o apelido Sinopteek já o fez. Veja os comentários). Quanto à criação de UUIDs de 128 bits, em princípio, você pode usar um método especial. , que fará isso por você.
ATTs GATTs…
Agora vem a parte divertida. Deixe-me lembrá-lo de que o ATT é baseado em uma relação cliente-servidor. Estamos agora analisando o dispositivo servidor. Ele contém informações como valores de sensores, status de interruptores de luz, dados de localização e assim por diante. Agora que todos os "participantes do nosso desfile" estão numerados, precisamos armazená-los de alguma forma na memória do dispositivo. Para fazer isso, os colocamos em uma tabela chamada tabela de atributos. Lembre-se bem disso. Este é o coração do BLE. É o que examinaremos mais adiante. De agora em diante, chamaremos cada linha de atributo. Esta tabela está localizada em uma camada profunda da pilha e, em geral, não temos acesso direto a ela. Nós a inicializamos e acessamos, mas o que acontece dentro dela é um mistério.
Vamos analisar a imagem da especificação, mas antes disso, gostaria de esclarecer uma confusão comum em relação aos termos, especificamente aos descritores. A função de um descritor é complementar a descrição de uma característica. Os descritores são usados quando suas capacidades precisam ser expandidas. Eles também são atributos e, assim como serviços e características, estão localizados na tabela de atributos. Discutiremos isso em detalhes na segunda parte do artigo. No entanto, às vezes os descritores são usados para se referir a um número de linha na tabela de atributos. Isso deve ser levado em consideração. Para evitar confusão, usaremos o termo "ponteiro de atributo" para esses fins.

Assim, um atributo é um valor discreto que possui as seguintes propriedades associadas a ele:
1. Um identificador de atributo é um índice de tabela correspondente a um atributo.
2. O tipo de atributo é um UUID que descreve seu tipo.
3. O valor do atributo são os dados indexados pelo ponteiro do atributo.
4. As permissões de atributo são a parte de um atributo que não pode ser lida ou escrita usando o protocolo de atributo.
Como devemos entender tudo isso? O índice de um atributo é, em termos gerais, o seu número na nossa tabela.
Isso permite que o cliente referencie um atributo em solicitações de leitura ou gravação. Podemos numerar nossas linhas (atributos) de 0x0001 a 0xFFFF. Em nossa metáfora da estante de livros, isso corresponde ao número do cartão em um catálogo impresso. Da mesma forma que em um catálogo de biblioteca, os cartões são organizados em ordem crescente. O número de cada linha subsequente deve ser maior que o da anterior. Assim como em uma biblioteca, os cartões às vezes se perdem, também podem ocorrer lacunas em nossos números de linha. Isso é aceitável. O importante é que estejam em ordem crescente.
O tipo de atributo define o que o atributo representa. Semelhante à linguagem C,
Onde houver variáveis booleanas, numéricas e de texto, aqui é a mesma coisa. Pelo tipo de atributo, sabemos
Com o que estamos lidando aqui e como devemos proceder com esse atributo? Abaixo, examinaremos alguns tipos de atributos específicos. Por exemplo, "declaração de serviço" (0x2800), "declaração de característica" (0x2803) e "declaração de descritor" (0x2902).
O valor de um atributo é o seu valor real, sem exageros. Se o tipo do atributo for uma string, seu valor pode ser, por exemplo, o slogan "Olá, Mundo!!!". Se o tipo do atributo for uma "declaração de serviço", seu valor é o próprio serviço. Às vezes, trata-se de informações sobre onde encontrar outros atributos e suas propriedades.
As permissões de atributo permitem que o servidor determine se o acesso de leitura ou gravação é permitido.
Observe que essas permissões se aplicam apenas ao valor do atributo, não ao ponteiro, tipo ou ao próprio campo de permissão. Portanto, se um atributo for gravável, podemos alterar, por exemplo, a string "Olá Mundo!!!" para "Bom dia". No entanto, não podemos proibir a escrita de uma nova linha ou alterar o tipo do atributo e designar a string como uma "declaração de serviço". Quando um cliente acessa um servidor, ele solicita seus atributos. Isso permite que o cliente determine o que o servidor pode fornecer, embora a leitura e a gravação de valores não sejam obrigatórias.
O que parece
O conceito do GATT é agrupar atributos em uma tabela de atributos em uma ordem muito específica e lógica. Vamos analisar mais detalhadamente o perfil de frequência cardíaca abaixo. A coluna mais à esquerda desta tabela é opcional. Ela simplesmente descreve o que esta linha (atributo) representa. Todas as outras colunas já são conhecidas.

No topo de cada grupo, sempre temos um atributo de declaração de serviço. Seu tipo é sempre 0x2800 e seu índice depende de quantos atributos já existem na tabela. Suas permissões são sempre somente leitura, sem qualquer autenticação ou autorização. Discutiremos esses conceitos mais adiante. O valor é outro UUID que identifica o serviço. Na tabela, o valor é 0x180D, que é definido pelo Bluetooth SIG como um serviço de frequência cardíaca.
Após a declaração de serviço, vem a declaração de característica. Ela é semelhante em forma à declaração de serviço. Seu UUID é sempre 0x2803 e suas permissões também são sempre somente leitura, sem qualquer autenticação ou autorização. Vamos analisar o campo Valor do Atributo, que contém alguns dados. Ele sempre contém um ponteiro, um UUID e um conjunto de propriedades. Esses três elementos descrevem a declaração de valor da característica subsequente. O ponteiro indica, naturalmente, a localização da declaração de valor da característica na tabela de atributos. O UUID descreve o tipo de informação ou valor que podemos esperar — por exemplo, um valor de temperatura, o estado de um interruptor de luz ou algum outro valor arbitrário. Finalmente, as propriedades descrevem como interagir com o valor da característica.
Há outra armadilha à nossa espera aqui. Tem a ver com permissões de atributos e propriedades características. Vejamos uma imagem das propriedades do campo de bits da especificação.

Como você pode ver, também existem campos aqui que fornecem acesso de leitura e gravação. Você pode estar se perguntando por que temos permissões de leitura/gravação para o atributo e a propriedade.
Permissões de leitura/gravação para um valor de característica? Não deveriam ser sempre as mesmas? A questão é que as propriedades de um valor de característica são essencialmente recomendações do cliente usadas nas camadas GATT e de aplicação. São simplesmente dicas sobre o que o cliente pode esperar do atributo de declaração da característica. Vamos analisar isso mais de perto. Que tipos de permissões um atributo possui?
1. Permissões de acesso:
- lendo
— gravação
— leitura e escrita
2. Permissão de autenticação:
— autenticação necessária
- Não é necessária autenticação
3. Permissão de autorização:
- Autorização necessária
- Não é necessária autorização
A principal diferença entre permissões de atributos e propriedades de características é que as primeiras são específicas do servidor, enquanto as últimas são específicas do cliente. Um servidor pode permitir a leitura do valor de uma característica, mas ainda exigir autenticação ou autorização. Portanto, quando um cliente solicita as propriedades de uma característica, veremos que a leitura é permitida. No entanto, tentar lê-las resultará em um erro. Assim, podemos afirmar com segurança que as permissões têm precedência sobre as propriedades. Nós, do lado do cliente, não temos como saber quais permissões um atributo possui.
Descritor
Vamos retornar à nossa tabela. Após declarar o valor da característica, as seguintes declarações de atributos são possíveis:
1. Anúncio de nova funcionalidade (um serviço pode ter múltiplas funcionalidades)
2. Nova declaração de serviço (pode haver várias na tabela)
3. Declaração do descritor
No caso da característica de medição da frequência cardíaca em nossa tabela, a declaração do valor da característica é acompanhada por uma declaração de descritor. Um descritor é um atributo que contém informações adicionais sobre a característica. Existem vários tipos de descritores, que discutiremos em detalhes na segunda parte deste artigo. Por ora, abordaremos apenas o Descritor de Configuração de Característica do Cliente (CCCD). Ele possui o UUID 0x2902. Usando este descritor, o cliente pode habilitar uma indicação ou uma notificação no servidor. A diferença entre elas é sutil, mas existe. A notificação não requer confirmação de recebimento do cliente. A indicação requer, embora ocorra no nível GATT, e não no nível da aplicação. Por que isso acontece? Infelizmente, não sei. Direi apenas que os especialistas da Nordic recomendam o uso de notificação. Além disso, a verificação de integridade do pacote (usando CRC) ocorre em ambos os casos.
Conclusão
Ao final do artigo, gostaria de dizer o seguinte. A última tabela é um pouco confusa. No entanto, parei nela porque está apresentada em , na qual me baseio. Na segunda parte do meu artigo, pretendo aprofundar a especificação do Bluetooth 4.0. Diagramas e ilustrações mais precisos nos aguardam lá. Na terceira parte, gostaria de analisar um registro obtido de um dos dispositivos usando o Wireshark e ver toda a teoria que estudamos em ação.
Funcionário do Grupo de Empresas
Vladimir Pechersky
Fonte: habr.com
