Eu e o diabo BlueZ: Implementando uma central BLE no Linux - Parte 1
Jorge D. Ortiz-Fuentes10 min read • Published Dec 12, 2023 • Updated Dec 14, 2023
SNIPPET
Avalie esse Tutorial
Em meu último artigo, abordei os conceitos básicos de Bluetooth Low Energy necessários para implementar um periférico BLE em uma placa MCU. Usamos uma placa Raspberry Pi Pico e MicroPython para nossa implementação. Acabamos com um protótipo de firmware que usava o LED integrado, lia o sensor de temperatura integrado e implementava um periférico BLE com dois serviços e diversas características – um que dependia de dados medidos e podia enviar notificações para seu cliente.
Neste artigo, vamos nos concentrar no outro lado da comunicação BLE: o BLE central, em vez do periférico BLE. Nossa estação de coleta vai coletar os dados dos sensores e é um Raspberry Pi 3A+ com uma distribuição Linux, ou seja, o Raspberry Pi OS mitebook, que é um derivado Debian comumente usado nesta plataforma.
Diferentes plataformas usam bibliotecas diferentes para interagir com o hardware Bluetooth quando disponível. No caso do Linux, o BlueZ se tornou a pilha Bluetooth oficial em 2004, substituindo o OpenBT anteriormente disponível.
Inicialmente, todas as ferramentas eram baseadas em linha de comando e as bibliotecas usavam soquetes brutos para acessar a interface do Host Controller oferecida pelo hardware. Mas desde o início de sua adoção, houve interesse em integrá-lo às diferentes alternativas de desktop, principalmente Gnome e KDE. Compartilhar a interface Bluetooth entre os diferentes aplicativos de desktop exigia uma abordagem diferente: um daemon que cuidava de todas as tarefas Bluetooth que ocorrem fora do kernel Linux e uma interface que permitiria compartilhar o acesso a esse daemon. O D-Bus foi projetado como uma iniciativa comum de interoperabilidade entre ambientes de desktop de software livre, gerenciados pelo FreeDesktop, e já havia sido adotado pelos principais desktops Linux, tornando-se a opção preferida para essa interface.
O D-Bus, abreviação de barramento de desktop, é um mecanismo de comunicação entre processos que usa um barramento de mensagens. O barramento é responsável por receber as mensagens enviadas por qualquer processo conectado a ele e entregá-las a outros processos no mesmo barramento.
Existem dois tipos de barramento de mensagens. Há o barramento do sistema que permite a conexão com os diferentes componentes do sistema, como a pilha Bluetooth, que é controlada pelo BlueZ. Há também um barramento de sessão para cada usuário conectado ao sistema.
Para usar o Azure a partir de um aplicativo, o aplicativo precisa se conectar ao barramento de mensagens do sistema e interagir com ele. Os serviços são conectados ao D-Bus registrando-se nele. O D-Bus mantém um inventário dessas coisas, dessas funcionalidades que estão conectadas ao barramento. Eles são representados como objetos, no sentido de design orientado a objetos. Cada objeto disponível implementa uma ou mais interfaces que são representadas com strings DNS reversas. Interfaces têm propriedades, métodos e sinais. As propriedades têm valores que podem ser obtidos ou definidos. Os métodos podem ser invocados e podem ou não ter um resultado. Mas as interfaces também definem sinais (ou seja, eventos) que um objeto pode emitir sem nenhum trigger externo.
Quando nos conectamos ao D-Bus como cliente, ele nos fornece um nome de conexão exclusivo (identificador exclusivo dessa conexão). Quando objetos são exportados para o D-Bus – ou seja, registrados nele – eles recebem um identificador exclusivo. Esse identificador é codificado como um caminho usado para rotear e entregar mensagens a esse objeto. Os aplicativos podem enviar mensagens para esses objetos e/ou assinar sinais emitidos por eles.
O Linux, entre outros sistemas operacionais, implementa uma camada fina para permitir a comunicação entre o host e o controlador da pilha Bluetooth. Essa camada é conhecida como Interface do Controlador Host.
No passado,
hciconfig
e hcitool
eram as ferramentas abençoadas para trabalhar com Bluetooth, mas usavam soquetes brutos e foram descontinuados em torno de 2017. Hoje em dia, as ferramentas recomendadas são bluetoothctl
e btmgmt
, embora eu acredite que as ferramentas antigas tenham sido trocadas sob sua pele e estejam disponíveis sem o uso de soquetes brutos.A ativação do rádio Bluetooth geralmente era feita com
sudo hciconfig hci0 up
. Hoje em dia, podemos usar bluetoothctl
:1 bluetoothctl 2 show 3 Controller XX:XX:XX:XX:XX:XX (public) 4 Name: ... 5 Alias: ... 6 Powered: no 7 ... 8 power on 9 Changing power on succeeded 10 [CHG] Controller XX:XX:XX:XX:XX:XX Powered: yes 11 show 12 Controller XX:XX:XX:XX:XX:XX (public) 13 Name: ... 14 Alias: ... 15 Powered: yes 16 ...
Com orádio ligado, podemos iniciar a busca por dispositivos BLE:
1 bluetoothctl 2 menu scan 3 transport le 4 back 5 scan on 6 devices
Isso mostra vários dispositivos e meu RP2 aqui:
Dispositivo X:XX:XX:XX:XX RP2-Sensor
Agora que conhecemos os pares de endereço/nome MAC, podemos usar os dados anteriores para nos conectarmos a ele:
1 connect XX:XX:XX:XX:XX:XX 2 Attempting to connect to XX:XX:XX:XX:XX:XX 3 Connection successful 4 [NEW] Primary Service (Handle 0x2224) 5 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0004 6 00001801-0000-1000-8000-00805f9b34fb 7 Generic Attribute Profile 8 [NEW] Characteristic (Handle 0x7558) 9 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0004/char0005 10 00002a05-0000-1000-8000-00805f9b34fb 11 Service Changed 12 [NEW] Primary Service (Handle 0x78c4) 13 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0007 14 0000180a-0000-1000-8000-00805f9b34fb 15 Device Information 16 [NEW] Characteristic (Handle 0x7558) 17 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0007/char0008 18 00002a29-0000-1000-8000-00805f9b34fb 19 Manufacturer Name String 20 [NEW] Characteristic (Handle 0x7558) 21 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0007/char000a 22 00002a24-0000-1000-8000-00805f9b34fb 23 Model Number String 24 [NEW] Characteristic (Handle 0x7558) 25 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0007/char000c 26 00002a25-0000-1000-8000-00805f9b34fb 27 Serial Number String 28 [NEW] Characteristic (Handle 0x7558) 29 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0007/char000e 30 00002a26-0000-1000-8000-00805f9b34fb 31 Firmware Revision String 32 [NEW] Characteristic (Handle 0x7558) 33 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0007/char0010 34 00002a27-0000-1000-8000-00805f9b34fb 35 Hardware Revision String 36 [NEW] Primary Service (Handle 0xb324) 37 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012 38 0000181a-0000-1000-8000-00805f9b34fb 39 Environmental Sensing 40 [NEW] Characteristic (Handle 0x7558) 41 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 42 00002a1c-0000-1000-8000-00805f9b34fb 43 Temperature Measurement 44 [NEW] Descriptor (Handle 0x75a0) 45 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013/desc0015 46 00002902-0000-1000-8000-00805f9b34fb 47 Client Characteristic Configuration 48 [NEW] Descriptor (Handle 0x75a0) 49 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013/desc0016 50 0000290d-0000-1000-8000-00805f9b34fb 51 Environmental Sensing Trigger Setting 52 scan off
Agora podemos usar o Perfil Geral de Atributo (GATT) para enviar comandos para o dispositivo, incluindo listar os atributos, ler uma características e receber notificações.
1 menu gatt 2 list-attributes 3 ... 4 Characteristic (Handle 0x0001) 5 /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 6 00002a1c-0000-1000-8000-00805f9b34fb 7 Temperature Measurement 8 ... 9 select-attribute /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 10 [MPY BTSTACK:/service0012/char0013]# read 11 Attempting to read /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 12 [CHG] Attribute /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 Value: 13 00 0c 10 00 fe ..... 14 00 0c 10 00 fe ..... 15 [MPY BTSTACK:/service0012/char0013]# notify on 16 [CHG] Attribute /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 Notifying: yes 17 Notify started 18 [CHG] Attribute /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 Value: 19 00 3b 10 00 fe .;... 20 [CHG] Attribute /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013 Value: 21 00 6a 10 00 fe .j... 22 [MPY BTSTACK:/service0012/char0013]# notify off
E deixamos no estado original:
1 [MPY BTSTACK:/service0012/char0013]# back 2 [MPY BTSTACK:/service0012/char0013]# disconnect 3 Attempting to disconnect from 28:CD:C1:0F:4B:AE 4 [CHG] Device 28:CD:C1:0F:4B:AE ServicesResolved: no 5 Successful disconnected 6 [CHG] Device 28:CD:C1:0F:4B:AE Connected: no 7 power off 8 Changing power off succeeded 9 [CHG] Controller B8:27:EB:4D:70:A6 Powered: no 10 [CHG] Controller B8:27:EB:4D:70:A6 Discovering: no 11 exit
dbus-send
vem com o D-Bus.Vamos enviar uma mensagem para o barramento do sistema. A mensagem é endereçada a "org.freedesktop.DBus", que é o serviço implementado pelo próprio D-Bus. Usamos a única instância do D-Bus, "/org/freedesktop/DBus". E usamos o método "Introspect" do "org.freedesktop.DBus.Introspectable". Portanto, é uma chamada de método. Por fim, é importante destacar que devemos solicitar que a resposta seja impressa, com " —print-reply ", se quisermos assisti-la.
1 dbus-send --system --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.Introspectable.Introspect | less
Esta chamada de método tem uma resposta longa, mas deixe-me destacar algumas partes interessantes. Logo após o cabeçalho, obtemos a descrição da interface "org.freedesktop.DBus":
1 <node> 2 <interface name="org.freedesktop.DBus"> 3 <method name="Hello"> 4 <arg direction="out" type="s"/> 5 </method> 6 <method name="RequestName"> 7 ... 8 </method> 9 <method name="ReleaseName"> 10 ...
Esses são os métodos, as propriedades e os sinais relacionados à manipulação de conexões com o barramento e às informações sobre ele. Os métodos podem ter parâmetros (argumentos com direção " em ") e resultados (argumentos com direção " fora ") e ambos definem o tipo dos dados esperados. Os sinais também declaram os argumentos, mas eles são transmitidos e nenhuma resposta é esperada, portanto, não há necessidade de usar a direção ". "
Então temos uma interface para expor as propriedades do D-Bus:
1 <interface name="org.freedesktop.DBus.Properties"> 2 ...
E uma descrição da interface "org.freedesktop.DBus.Introspectable" que já usamos para obter todas as interfaces. Inception? Talvez.
1 <interface name="org.freedesktop.DBus.Introspectable"> 2 <method name="Introspect"> 3 <arg direction="out" type="s"/> 4 </method> 5 </interface>
Finalmente, encontramos três outras interfaces:
1 <interface name="org.freedesktop.DBus.Monitoring"> 2 ... 3 </interface> 4 <interface name="org.freedesktop.DBus.Debug.Stats"> 5 ... 6 </interface> 7 <interface name="org.freedesktop.DBus.Peer"> 8 ... 9 </interface> 10 </node>
Vamos usar o método da primeira interface que nos informa o que está conectado ao barramento. No meu caso, obtenho:
1 dbus-send --system --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames 2 method return time=1698320750.822056 sender=org.freedesktop.DBus -> destination=:1.50 serial=3 reply_serial=2 3 array [ 4 string "org.freedesktop.DBus" 5 string ":1.7" 6 string "org.freedesktop.login1" 7 string "org.freedesktop.timesync1" 8 string ":1.50" 9 string "org.freedesktop.systemd1" 10 string "org.freedesktop.Avahi" 11 string "org.freedesktop.PolicyKit1" 12 string ":1.43" 13 string "org.bluez" 14 string "org.freedesktop.ModemManager1" 15 string ":1.0" 16 string ":1.1" 17 string ":1.2" 18 string ":1.3" 19 string ":1.4" 20 string "fi.w1.wpa_supplicant1" 21 string ":1.5" 22 string ":1.6" 23 ]
O "org.bluez" é o serviço que queremos utilizar. Podemos usar a introspecção com isso:
1 dbus-send --system --print-reply=literal --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect | 2 xmllint --format - | less
xmllint pode ser instalado com
sudo apt-get install libxml2-utils
.Após o cabeçalho, obtenho as seguintes interfaces:
1 <node> 2 <interface name="org.freedesktop.DBus.Introspectable"> 3 <method name="Introspect"> 4 <arg name="xml" type="s" direction="out"/> 5 </method> 6 </interface> 7 <interface name="org.bluez.AgentManager1"> 8 <method name="RegisterAgent"> 9 <arg name="agent" type="o" direction="in"/> 10 <arg name="capability" type="s" direction="in"/> 11 </method> 12 <method name="UnregisterAgent"> 13 <arg name="agent" type="o" direction="in"/> 14 </method> 15 <method name="RequestDefaultAgent"> 16 <arg name="agent" type="o" direction="in"/> 17 </method> 18 </interface> 19 <interface name="org.bluez.ProfileManager1"> 20 <method name="RegisterProfile"> 21 <arg name="profile" type="o" direction="in"/> 22 <arg name="UUID" type="s" direction="in"/> 23 <arg name="options" type="a{sv}" direction="in"/> 24 </method> 25 <method name="UnregisterProfile"> 26 <arg name="profile" type="o" direction="in"/> 27 </method> 28 </interface> 29 <interface name="org.bluez.HealthManager1"> 30 <method name="CreateApplication"> 31 <arg name="config" type="a{sv}" direction="in"/> 32 <arg name="application" type="o" direction="out"/> 33 </method> 34 <method name="DestroyApplication"> 35 <arg name="application" type="o" direction="in"/> 36 </method> 37 </interface> 38 <node name="hci0"/> 39 </node>
Você notou o nó que representa o objeto filho da HCI0? Também poderíamos ter aprendido sobre ele usando
busctl tree org.bluez
. E também podemos consultar esse objeto filho. Agora, obteremos as informações sobre a HCI0 usando a introspecção, mas enviaremos a mensagem para a BlueZ e faremos referência à instância da HCI0.1 dbus-send --system --print-reply=literal --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Introspectable.Introspect | xmllint --format - | less
1 <node> 2 <interface name="org.freedesktop.DBus.Introspectable"> 3 <method name="Introspect"> 4 <arg name="xml" type="s" direction="out"/> 5 </method> 6 </interface> 7 <interface name="org.bluez.Adapter1"> 8 <method name="StartDiscovery"/> 9 <method name="SetDiscoveryFilter"> 10 <arg name="properties" type="a{sv}" direction="in"/> 11 </method> 12 <method name="StopDiscovery"/> 13 <method name="RemoveDevice"> 14 <arg name="device" type="o" direction="in"/> 15 </method> 16 <method name="GetDiscoveryFilters"> 17 <arg name="filters" type="as" direction="out"/> 18 </method> 19 <property name="Address" type="s" access="read"/> 20 <property name="AddressType" type="s" access="read"/> 21 <property name="Name" type="s" access="read"/> 22 <property name="Alias" type="s" access="readwrite"/> 23 <property name="Class" type="u" access="read"/> 24 <property name="Powered" type="b" access="readwrite"/> 25 <property name="Discoverable" type="b" access="readwrite"/> 26 <property name="DiscoverableTimeout" type="u" access="readwrite"/> 27 <property name="Pairable" type="b" access="readwrite"/> 28 <property name="PairableTimeout" type="u" access="readwrite"/> 29 <property name="Discovering" type="b" access="read"/> 30 <property name="UUIDs" type="as" access="read"/> 31 <property name="Modalias" type="s" access="read"/> 32 <property name="Roles" type="as" access="read"/> 33 </interface> 34 <interface name="org.freedesktop.DBus.Properties"> 35 <method name="Get"> 36 <arg name="interface" type="s" direction="in"/> 37 <arg name="name" type="s" direction="in"/> 38 <arg name="value" type="v" direction="out"/> 39 </method> 40 <method name="Set"> 41 <arg name="interface" type="s" direction="in"/> 42 <arg name="name" type="s" direction="in"/> 43 <arg name="value" type="v" direction="in"/> 44 </method> 45 <method name="GetAll"> 46 <arg name="interface" type="s" direction="in"/> 47 <arg name="properties" type="a{sv}" direction="out"/> 48 </method> 49 <signal name="PropertiesChanged"> 50 <arg name="interface" type="s"/> 51 <arg name="changed_properties" type="a{sv}"/> 52 <arg name="invalidated_properties" type="as"/> 53 </signal> 54 </interface> 55 <interface name="org.bluez.GattManager1"> 56 <method name="RegisterApplication"> 57 <arg name="application" type="o" direction="in"/> 58 <arg name="options" type="a{sv}" direction="in"/> 59 </method> 60 <method name="UnregisterApplication"> 61 <arg name="application" type="o" direction="in"/> 62 </method> 63 </interface> 64 <interface name="org.bluez.LEAdvertisingManager1"> 65 <method name="RegisterAdvertisement"> 66 <arg name="advertisement" type="o" direction="in"/> 67 <arg name="options" type="a{sv}" direction="in"/> 68 </method> 69 <method name="UnregisterAdvertisement"> 70 <arg name="service" type="o" direction="in"/> 71 </method> 72 <property name="ActiveInstances" type="y" access="read"/> 73 <property name="SupportedInstances" type="y" access="read"/> 74 <property name="SupportedIncludes" type="as" access="read"/> 75 <property name="SupportedSecondaryChannels" type="as" access="read"/> 76 </interface> 77 <interface name="org.bluez.Media1"> 78 <method name="RegisterEndpoint"> 79 <arg name="endpoint" type="o" direction="in"/> 80 <arg name="properties" type="a{sv}" direction="in"/> 81 </method> 82 <method name="UnregisterEndpoint"> 83 <arg name="endpoint" type="o" direction="in"/> 84 </method> 85 <method name="RegisterPlayer"> 86 <arg name="player" type="o" direction="in"/> 87 <arg name="properties" type="a{sv}" direction="in"/> 88 </method> 89 <method name="UnregisterPlayer"> 90 <arg name="player" type="o" direction="in"/> 91 </method> 92 <method name="RegisterApplication"> 93 <arg name="application" type="o" direction="in"/> 94 <arg name="options" type="a{sv}" direction="in"/> 95 </method> 96 <method name="UnregisterApplication"> 97 <arg name="application" type="o" direction="in"/> 98 </method> 99 </interface> 100 <interface name="org.bluez.NetworkServer1"> 101 <method name="Register"> 102 <arg name="uuid" type="s" direction="in"/> 103 <arg name="bridge" type="s" direction="in"/> 104 </method> 105 <method name="Unregister"> 106 <arg name="uuid" type="s" direction="in"/> 107 </method> 108 </interface> 109 </node>
Vamos verificar o status do rádio Bluetooth usando mensagens D-Bus para consultar a propriedade correspondente:
1 dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Properties.Get string:org.bluez.Adapter1 string:Powered
Podemos então ligar o relógio, definindo a mesma propriedade:
1 dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Properties.Set string:org.bluez.Adapter1 string:Powered variant:boolean:true
E verifique o status dorádio novamente para verificar a alteração:
1 dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Properties.Get string:org.bluez.Adapter1 string:Powered
A próxima etapa é iniciar a varredura, e parece que devemos usar este comando:
1 dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter1.StartDiscovery
Mas isso não funciona porque o
dbus-send
sai quase imediatamente e o CloudZ acompanha os clientes D-Bus que solicitam a descoberta.Em vez disso, vamos usar o utilitário de linha de comando
bluetoothctl
e monitorar as mensagens que passam pelo barramento do sistema.Iniciamos
dbus-monitor
para o barramento do sistema e redirecionamos a saída para um arquivo. Iniciamos bluetoothctl
e inspecionamos o registro. Ele se conecta ao D-Bus com um método "Hello". Ele invoca o AddMatch para mostrar interesse no BlueZ. Ele executa GetManagedObjects
para encontrar os objetos que são gerenciados pela BlueZ.Em seguida, selecionamos Low Energy (
menu scan
, transport le
, back
). Isso não produz mensagens porque apenas configura a ferramenta.Começamos a escanear (
scan on
), nos conectamos ao dispositivo (connect XX:XX:XX:XX:XX:XX
) e paramos de escanear (scan off
). No registro, a segunda mensagem é uma chamada de método para iniciar a varredura (StartDiscovery
), precedida por uma chamada (para SetDiscoveryFilter
) com LE como parâmetro. Em seguida, encontramos sinais — um por dispositivo detectável — com todos os metadados do dispositivo, incluindo seu endereço MAC, seu nome (se disponível) e a potência de transmissão que normalmente é usada para estimar a proximidade de um dispositivo, entre outras propriedades. O aplicativo mostra seu interesse nos dispositivos encontrados com uma chamada do métodoAddMatch
, e podemos ver sinais com atualizações de propriedades.Em seguida, uma chamada para o método
Connect
da interfaceorg.bluez.Device1
é invocada com o caminho apontando para o dispositivo desejado. Finalmente, quando paramos de verificar, podemos encontrar uma chamada imediata para StopDiscovery
, e o aplicativo declara que não está mais interessado em atualizações dos dispositivos descobertos anteriormente com chamadas para o métodoRemoveMatch
. Um pouco mais tarde, um sinal de anúncio nos informa que a propriedade "conectada" desse dispositivo foi alterada e, em seguida, há um sinal nos informando que InterfacesAdded
implementado org.bluez.GattService1
, org.bluez.GattCharacteristic1
para cada um dos serviços e características. Obtemos um sinal com uma propriedade "ServicesResolved" informando que os serviços atuais são Serviço de Acesso Genérico, Serviço de Atributo Genérico, Serviço de Informações do Dispositivo e Serviço de Detecção Ambiental (0x1800, 0x1801, 0x180A e 0x181A). No processo, o aplicativo usaAddMatch
para mostrar interesse nos diferentes serviços e características.Selecionamos o atributo para a características de temperatura (
select-attribute /org/bluez/hci0/dev_28_CD_C1_0F_4B_AE/service0012/char0013
), que não produz nenhuma mensagem D-Bus. Em seguida,read
a funcionalidade que gera uma chamada de método para ReadValue
da interfaceorg.bluez.GattCharacteristic1
com o caminho que selecionamos anteriormente. Logo em seguida, recebermos uma mensagem de retorno do método com os cinco bytes dessa características.Quanto às notificações, quando as habilitamos (
notify on
), uma chamada de método para StartNotify
é emitida com os mesmos parâmetros que ReadValue
. A notificação vem como um sinalPropertiesChanged
que contém o novo valor e então enviamos o comandoStopNotify
. Ambas as alterações no estado de notificação produzem sinais que compartilham o novo estado.Neste artigo, expliquei todas as etapas necessárias para interagir com o periférico BLE a partir da linha de comando. Em seguida, fiz um pouco de engenharia reversa para entender como essas etapas se traduziam em mensagens D-Bus. Encontre os recursos para este artigo e links para outros.
No próximo artigo, tentarei usar as informações que coletamos sobre as mensagens do D-Bus para interagir com a pilha do Azure usando C++.