Zephyr RTOS – Primeiro Projeto BLE: Monitor Cardíaco

Dando continuidade a nossa série sobre o Zephyr RTOS, faremos nosso primeiro Projeto BLE baseado em um Monitor de Frequência Cardíaca.
Este post faz parte da série Zephyr RTOS + Nordic. Leia também os outros posts da série:

Dando continuidade a nossa série sobre o Zephyr RTOS, agora vamos dar uma olhada em um exemplo de um projeto baseado em Bluetooth Low Energy (BLE).

Quanto aos exemplos, você achará diversos deles para BLE tanto na árvore do Zephyr em /zephyr/samples/bluetooth, quanto na árvore do NCS em /nrf/samples/bluetooth para a família nRF da Nordic.

Para esse artigo, utilizaremos o exemplo peripheral_hr, que implementa um periférico BLE conectável que simula o comportamento de um monitor de batimentos cardíacos. Este exemplo se encontra na pasta /zephyr/samples/peripheral_hr do seu ambiente.

Como usarei novamente uma nRF52833-DK neste artigo, vou abrir o nRF Connect, chamar o Toolchain Manager e de lá pedir para abrir o Command Prompt (CMD) na versão do SDK que vou utilizar.

Com o CMD aberto e com as variáveis de ambientes já setadas, vou acessar o diretório e invocar o Visual Studio Code (VS Code)  de lá.

Esse é o método que utilizo no Windows com o nRF Connect SDK (NCS). Para esse artigo,

vou considerar que você também já está com o NCS ou com o Zephyr “puro” instalado e configurado e já consegue  utilizar ou o Visual Studio Code (VS Code) junto com o west, ou então utilizar o ambiente Segger instalado pelo NCS para gerar código e baixar em sua placa. Caso não tenha isso pronto ou tenha dúvidas, sugiro dar uma olhada no primeiro artigo dessa série, o Zephyr RTOS – Instalando o Ambiente de Desenvolvimento, aqui no Embarcados. E –  fora às duas linhas de comando acima – o resto do artigo é agnóstico a OS.

Analisando a estrutura do projeto

image 18

Podemos mais uma vez ver acima uma enxuta estrutura de projeto baseado em Zephyr:

Arquivo .overlay

Primeiro temos uma pasta boards com um arquivo rv32m1_vega_ri5cy.overlay. Arquivos .overlay são modificações do Device Tree (dts) específicas do seu projeto. Ou seja, o seu projeto pode remover, adicionar ou modificar o dts da placa que você estiver utilizando.

Esses arquivos residem na pasta boards e possuem a nomenclatura nome_da_placa.overlay. No caso este é para a placa OpenISA VEGAboard.

A não ser que você esteja justamente utilizando essa placa e invoque o comando west build -b rv32m1_vega_ri5cy.overlay, esse arquivo não será utilizado no seu projeto.

No artigo sobre Device Trees no Zephyr, quando veremos como utilizar placas shields e sensores veremos melhor sobre esse tipo arquivo.

Arquivo CMakeList.txt

Neste projeto não temos maiores novidades no CMakeList, apenas a chamada da base do Zephyr, declaração do projeto e adição dos arquivos fonte:

Arquivo prj.conf

Aqui começamos a ver mais da versatilidade do Zephyr neste arquivo de configuração do sistema:

Perceba que existem diversas configurações referentes ao Bluetooth começando com CONFIG_BT_xxx.

Essas configurações dos arquivos .PRJ são definições referenciadas nos códigos fontes sem o CONFIG_ inicial.

Essas configurações podem ser usadas tanto para definir módulos do Zephyr a serem carregados, definir arquivos a serem adicionados no CMakelist ou então definir trechos de código que devem ou não serem executados.

Se você tiver a extensão do VS Code Kconfig for the Zephyr Project, você pode facilmente colocar o mouse em cima ou pedir para ir para definição para ver mais sobre cada uma. Mas vamos a um resumo rápido sobre as configurações deste projeto.

Configurações do Bluetooth

CONFIG_BT

Esta configuração habilita todo o subsistema de Bluetooth no Zephyr. É pré-requisto para todas as outras utilizadas neste projeto.

CONFIG_BT_DEBUG_LOG

Habilita jogar mensagens de debug do Bluetooth para a interface de console. Observe que não existe configuração de habilitar o console nesse projeto. Ela normalmente é default no boardfiles da maioria das placas, e é o caso da nRF52833-Dk que estou usando.

Vamos aproveitar para dar uma olhada nas dependências entre configurações do Kconfig.

Se usarmos a extensão Kconfig for the Zephyr Project para ir na origem desse CONFIG_DEBUG_LOG, iremos para o arquivo /zephyr/subsys/bluetooth/common/Kconfig e termos o seguinte:

Perceba que esta configuração faz um select de outras duas configurações. Perceba também que apesar referenciadas como CONFIG_xxx nos arquivos .proj, os arquivos Kconfig que originam essas configurações não trazem esse prefixo.

Então sabemos que habilitar o BT_DEBUG_LOG habilita essas duas outras configurações: BT_DEBUG para todas as funções de debug do Bluetooth e LOG para o sistema de logs do Zephyr.

Se agora formos na origem da configuração LOG teremos o seguinte:

Ou seja, habilitar a CONFIG_BT_DEBUG_LOG no proj.conf, habilitar o subsistema de LOG que por sua vez habilita a biblioteca do PRINTK para exibir as mensagens no console. Prático e modular, não?

Agora, voltando às configurações…

CONFIG_BT_SMP 

Habilita o Security Manager Protocol, necessário para poder fazer o pareamento Bluetooth. Ela vai chamar algumas bibliotecas de AES, RPA e ECC para lidar com a troca de chaves entre os dispositivos.

CONFIG_BT_PERIPHERAL

Habilita a função Periférico Bluetooth, contendo as funções necessárias para fazer broadcast do pacote de advertising com os dados do dispositivo e também as funções necessárias para realizar uma conexão com uma Central Bluetooth.

CONFIG_BT_DIS e CONFIG_BT_DIS_PNP

 Habilitam o Device Informatinon Service (DIS), que disponibiliza diversas informações sobre o dispositivo, como fabricante, modelo do componente, versão, entre outros.

Pausa para explicação: O Bluetooth possui uma série de padronizações de tipos de perfis, serviços e características para ser utilizado entre os dispositivos, definidas pelo Bluetooth SIG.

Perfis (Profiles) são conjuntos de serviços, como por exemplo o Heart Rate Profile. Este perfil é composto pelos Heart Rate Service e pelo Device Information Service. Aqui você pode ver uma lista de todos os perfis disponíveis: Specifications | Bluetooth® Technology Website.

Serviços (Services), por sua vez, são um bloco lógico composto por diversas características padrão. Um serviço geralmente tem um ID único chamado UUID, que pode ser tanto de 16-bit para serviços oficiais definidos pelo Bluetooth SIG, quanto de 128-bits para serviços definidos pelo usuário. A Bluetooth SIG disponibiliza uma lista de serviços padrão em Assigned Numbers | Bluetooth® Technology Website.

Se olharmos a documentação do Heart Rate Service, você verá que o 16-bit UUID definido e 0x180D e que ele pode conter até 3 características, sendo apenas a primeira mandatória: Heart Rate Measurement, Body Sensor Location e Heart Rate Control Point.

Características (Characteristics), definem um ponto de troca de dados (ou até mesmo um array de dados) entre dispositivos. De maneira similar aos serviços, as características também tem UUIDs de 16 ou 128 bits.

Por exemplo, o Heart Rate Service tem como obrigatória a característica Heart Rate Measurement que tem seu UUID definido em 0x2A37.

Voltando mais uma vez às configurações de Bluetooth do projeto…

CONFIG_BT_BAS

Este é o Battery Service (BAS), que contém… (suspense) … informações sobre o nível de bateria do dispositivo.

CONFIG_BT_HRS

Esse é o  Heart Rate Service (HRS) propriamente dito.

CONFIG_BT_DEVICE_NAME

Aqui se configura o nome que o periférico terá quando for escaneado por outros dispositivos.

Vou alterar para “Embarcados Heartrate Sensor” já que estamos por aqui mesmo.

CONFIG_BT_DEVICE_APPEARANCE

Aqui se define qual a aparência que nosso dispositivo terá para o mundo, ou seja, qual perfil de produto padrão estamos utilizando. Isso servirá para nosso periférico ser automaticamente reconhecido como um monitorador de batimentos cardíacos por diversos aplicativos. Novamente a lista se encontra em Assigned Numbers | Bluetooth® Technology Website.

Arquivo src/main.c

Aqui temos o principal (e único) código-fonte de nosso projeto. Vamos dar uma olhada nele.

Primeiro temos a declaração de bibliotecas:

É importante frisar aqui que, mesmo que você já tenha declarado o uso do módulo ou subsistema no proj.conf, caso você precise interagir diretamente, como por exemplo para inicialização e/ou configuração, você vai precisar declarar as bibliotecas necessárias no seu projeto.

Agora olhando a função main():

Bom, aqui temos primeiro uma parte de inicialização:

bt_enable(br_ready_cb_t cb)

Esta é a função que realiza a inicialização de todo o subsistema Bluetooth.

Ela poderia receber um parâmetro que é o callback automático para a função bt_ready, que realiza a configuração do Bluetooth. Aqui no caso foi passado um parâmetro NULL, checado se houve erro em bt_enable para só então chamar a função bt_ready.

bt_enable(void)

Aqui, uma vez inicializado o subsistema de Bluetooth, é feita a configuração do mesmo.

Aqui temos além de uns belos printk de inicialização, a chamada da função bt_le_adv_start, que inicia o serviço de advertising, fazendo nosso periférico Bluetooth ser descoberto pelo mundo.

O primeiro parâmetro dela, BT_LE_ADV_CONN_NAME, é uma definição dos parâmetros do advertisiong. Se você for atrás dessa definição, verá que ela define o dispositivo como conectável, habilita e transmite o nome do dispositivo e define os intervalos de advertising.

Em seguida temos a estrutura ad que define os dados de advertising e o tamanho dela (ARRAY_SIZE(ad)).

Ela é definida no bloco abaixo:

Aqui temos alguns macros que vão nos ajudar a montar a estrutura do pacote de advertising. A macro BT_DATA_BYTE define cada campo de dados do pacote. 

Neste caso o primeiro campo é composto pela definição de flags de advertising (BT_DATA_FLAGS) seguida pelas flags BT_LE_AD_GENERAL (que define é o modo de advertising padrão, ao contrário do BT_LE_AD_LIMITED que define um modo de prioridade quando escaneado por alguma central, tem intervalo de advertising reduzido e não deve ser usado durante mais de um minuto) e o BT_LE_AD_NO_BREDR (que define que não suportamos o protocolo de conexão BR/EDR, também conhecido como Bluetooth Classic, uma vez que somos um dispositivo BLE). 

Você pode ver mais informações sobre as flags em Advertising Works, Part 2 | Bluetooth® Technology Website.

Já o segundo campo, contém a definição a definição de que seguirão alguns  UUIDs de 16 bits (BT_DATA_UUID16_ALL) e a lista dos UUIDs dos nossos serviços disponíveis: BT_UUID_HRS_VAL para o serviço de Heart Rate, BT_UUID_BAS_VAL para o serviço de nível de bateria e o serviço BR_UUID_DIS_VAL para o serviço de informação de dispositivo.

Vale ressaltar aqui que não é necessário listar todos os UUIDs se serviços disponíveis no periférico, apenas os que desejamos que sejam descobertos antes de ser realizada a conexão.

Voltando a nossa rotina de inicialização…

bt_conn_cb_register(&conn_callbacks)

Aqui são definidas os callbacks de funções a serem chamadas no caso de conexão ou desconexão. Elas são definidas através da estrutura conn_callbacks:

Veja essas funções e observe que entre os parâmetros que elas recebem ao serem chamadas pelo subsistema de Bluetooth estão o err indicando se houve algum erro na conexão, e o reason, que diz por qual motivo ocorreu a desconexão.

Fora isso, temos mais um par de singelos printks informativos nessas funções, mas poderíamos, por exemplo, tomar ações como ligar ou desligar sensores em função de ter ocorrido uma conexão, ou desconexão.

bt_conn_auth_cb_register(&auth_cb_display)

De maneira análoga à anterior, aqui temos a estrutura de callbacks em função de autenticação, ou seja, para as requisições de pareamento. No caso aqui temos apenas a definição do callback de cancel, que ocorre quando há o despareamento. Outros callbacks possíveis aqui seriam no caso de confirmação de pareamento e de requisição de entrada de chave de pareamento, entre outros.

Por fim vamos nosso loop principal, onde a cada 1 segundo vamos chamar duas funções: hrs_notify() e bas_notify().

hrs_notify()

Esta função simula o valor dos batimentos cardíacos e faz a interface com a thread do Heart Rate Service (HRS).

Temos aqui basicamente a simulação em si que vai incrementar o valor do batimento a cada vez que for chamada e fazer um overflow de 160bpm para 90bpm.

E o novo valor de bpm é passado para o HRS através da função bt hrs _notify(valor).

Aqui fica fácil imaginar que poderíamos fazer a leitura de um sensor de batimentos cardíacos, ou então pegar um valor de leitura presente em alguma fifo e atualizar o valor no HRS.

bas_notify()

De maneira semelhante, aqui é feita a simulação do nível da bateria.

Se sua placa possuir recurso para ler seu valor de tensão, aqui seria um bom ponto para atualizar o BAS com o valor corrente.

Rodando o Build e Flash

Agora vamos fazer o build do projeto:

E carregá-lo na placa:

Se a sua placa tiver uma interface serial e você conectar a ela com um programa como o Tera Term, você verá as informações de boot do Zepjyr:

Observe que como estou utilizando o NCS e um dispositivo da família nRF, está sendo usado o SoftController da Nordic. Caso estivesse usando alguma outra plataforma de hardware, seria usado o Link Layer padrão do Zephyr ao invés do SoftDevice.

Existem também todas as informações de versões de plataforma, revisão do stack e versão do HCI e LMP.

Conectando ao nosso monitor cardíaco.

nRF Toolbox

Agora o próximo passo é nos conectarmos com o nosso periférico. Para isso, primeiramente vamos utilizar o celular e o aplicativo nRF Toolbox da Nordic. (Versão Android / Versão Iphone).

Importante ressaltar que essas aplicações podem ser usadas com quaisquer dispositivos BLE, independente de conterem um SoC Nordic ou não.

No nRF Toolbox, selecione a aplicação de HRM, solicite conexão a seu periférico e visualize o gráfico de bpm e a indicação de bateria.

image 19

Se olharmos o terminal, vamos ver que apareceram as seguintes mensagens:

Observe a indicação de habilitação das notificações do BAS e HRS. Isso quer dizer que o dispositivo central (o celular) solicitou para o dispositivo periférico atualizar periodicamente os valores de BAS e HRS.

Se desconectarmos, teremos o seguinte no terminal:

nRF Connect

Agora vamos utilizar a versão mobile do nRF Connect da Nordic para ver mais detalhes do nosso dispositivo (Versão Android / Versão Iphone). 

Aqui você verá o seu periférico na lista de dispositivos próximos.

image 20

Está vendo aquele símbolo de coração? O próprio smartphone já sabe que o periférico é um HRM. Isso se deve a esta linha lá no prj.conf:

Primeiramente clique em cima do dispositivo, sem utilizar a opção de conectar.

Veremos então a informação de tudo que está sendo transmitido no pacote de advertising:

image 21

Observe que ele indica que é apenas BLE e não suporta Br/EDR, que é GeneralDiscoverable e que possui três serviços, 0x180D (HRS), 0x180F (BAS) e 0x180A (DIS).

Clicando em Connect você verá a lista de todos os serviços (PRIMARY SERVICES) de nosso periférico.

image 22

Os dois primeiros (0x1801 e 0x1800) são serviços padrões de status e nome do dispositivo, seguidos pelo BAS, DIS e HRS.

Você pode clicar nos diversos serviços e ver as características dentro deles. Observe que existem características de leitura (seta para baixo)  e de escrita (seta para cima).

Além disso, existem características como a Heart Rate Measurement – 0x2A37 que possuem um ícone com três setas para baixo. Isso quer dizer que você pode assinar as notificações da característica, recebendo cada novo valor que o código principal está enviando através da função abaixo:

image 23

Conclusão

Vimos então o detalhamento de um exemplo de periférico bluetooth conectável, contendo três serviços: Monitoramento Cardíaco, Nível de Bateria e Informações de Dispositivo.

Se você quiser desabilitar algum desses serviços, basta ir no prj.conf e colocar um “n” ou comentar a ativação dos mesmos:

Lembre-se que no caso de desabilitar o HRS ou o BAS, você precisará também remover a chamada de suas bibliotecas respectivas no topo do main.c e remover suas funções e chamadas no main().

Além deste exemplo do HRS, existem diversos outros de serviços diferentes disponíveis no Zephyr que podem ser usados como base de projetos, entre eles:

Outro exemplo interessante é o zephyr/samples/bluetooth/peripheral, que além do HRS, DIS e BAS que vimos nesse artigo, utiliza também um serviço vendor-specific que poder ser utilizado como framework para o seu próprio serviço.

E, é claro, se você vai utilizar algum serviço padrão Bluetooth SIG (UUID de 16 bits) que ainda não está disponível no Zephyr, sinta-se a vontade para contribuir de volta para a comunidade Open Source do Zephyr através de um Pull Request.

Saiba Mais

Introdução ao Bluetooth Smart (BLE)

Aplicações e Perfis do Bluetooth Low Energy (BLE)

Outros artigos da série

<< Zephyr RTOS – Instalando o Ambiente de DesenvolvimentoZephyr RTOS – Adicionando Bootloader e Update de Firmware >>

João Dullius é Engenheiro de Aplicações na BP&M desde 2013, atuando atualmente com foco em em aplicações IoT, Machine Learning e RF. Engenheiro eletricista de formação, trabalhou em diversos projetos embarcados na área de telecomunicações e automação industrial e agrícola.

Notificações
Notificar
guest
0 Comentários
Inline Feedbacks
View all comments

WEBINAR

Imagens de Ultrassom: Princípios e Aplicações

DATA: 26/10 ÀS 19:30 H